file_get_contents (): Thao tác SSL không thành công với mã 1, Không thể kích hoạt tiền điện tử


188

Tôi đã cố gắng truy cập dịch vụ REST cụ thể này từ trang PHP tôi đã tạo trên máy chủ của chúng tôi. Tôi thu hẹp vấn đề xuống hai dòng này. Vì vậy, trang PHP của tôi trông như thế này:

<?php
$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json");

echo $response; ?>

Trang chết trên dòng 2 với các lỗi sau:

  • Cảnh báo: file_get_contents (): Thao tác SSL không thành công với mã 1. OpenSSL Thông báo lỗi: error: 14090086: Các thói quen SSL: SSL3_GET_SERVER_CERTIFICATE: xác minh chứng chỉ không thành công trong ... php trên dòng 2
    • Cảnh báo: file_get_contents (): Không thể bật tiền điện tử trong ... php trên dòng 2
    • Cảnh báo: file_get_contents ( https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json): không thể mở luồng: thao tác không thành công trong ... php trên dòng 2

Chúng tôi đang sử dụng máy chủ Gentoo. Gần đây chúng tôi đã nâng cấp lên phiên bản PHP 5.6. Đó là sau khi nâng cấp khi vấn đề này xuất hiện.

Tôi tìm thấy khi tôi thay thế dịch vụ REST bằng một địa chỉ như https://www.google.com; trang của tôi hoạt động tốt

Trong một lần thử trước tôi đã đặt “verify_peer”=>falsevà chuyển nó thành đối số cho file_get_contents, như được mô tả ở đây: file_get_contents bỏ qua verify_peer => false? Nhưng như nhà văn lưu ý; nó không có gì khác biệt

Tôi đã hỏi một trong những quản trị viên máy chủ của chúng tôi nếu những dòng này trong tệp php.ini của chúng tôi tồn tại:

  • tiện ích mở rộng = php_openssl.dll
  • allow_url_fopen = Bật

Anh ấy nói với tôi rằng vì chúng tôi ở Gentoo, openssl được biên dịch khi chúng tôi xây dựng; và nó không được đặt trong tệp php.ini.

Tôi cũng xác nhận rằng allow_url_fopenđang làm việc. Do tính chất chuyên biệt của vấn đề này; Tôi không tìm thấy nhiều thông tin để được giúp đỡ. Có ai trong số các bạn bắt gặp một cái gì đó như thế này? Cảm ơn.


Nếu bạn sử dụng Kaspersky, hãy kiểm tra điều này: stackoverflow.com/a/54791481/3549317
cespon

Câu trả lời:


344

Đây là một liên kết rất hữu ích để tìm:

http://php.net/manual/en/migration56.openssl.php

Một tài liệu chính thức mô tả các thay đổi được thực hiện để mở ssl trong PHP 5.6 Từ đây tôi đã biết thêm một tham số mà tôi nên đặt thành false: "verify_peer_name" => false

Lưu ý: Điều này có ý nghĩa bảo mật rất đáng kể. Vô hiệu hóa xác minh có khả năng cho phép kẻ tấn công MITM sử dụng chứng chỉ không hợp lệ để nghe lén các yêu cầu. Mặc dù có thể hữu ích để làm điều này trong phát triển địa phương, các cách tiếp cận khác nên được sử dụng trong sản xuất.

Vì vậy, mã làm việc của tôi trông như thế này:

<?php
$arrContextOptions=array(
    "ssl"=>array(
        "verify_peer"=>false,
        "verify_peer_name"=>false,
    ),
);  

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

echo $response; ?>

122
điều này phá vỡ chứng nhận SSL và là một lỗ hổng bảo mật
hypery2k

8
Như @ hypery2k đã chỉ ra, bạn không nên vô hiệu hóa xác minh. Xem câu trả lời của tôi cho một giải pháp thay thế không đánh bại mục đích sử dụng ssl.
elitechief21 24/2/2015

4
Điều này thực sự không nên làm. Thay vì đẩy mạnh vấn đề, bạn thực sự nên sửa nó. Xem giải pháp tốt hơn từ @ elitechief21.
Jasper

5
Điều này có thể gây bực bội nếu bạn đang thử nghiệm từ môi trường địa phương chống lại api. Trong trường hợp này, tôi thường nuke xác minh.
HappyCoder

12
Vì tôi luôn đến đây khi tôi thử làm điều này, đây là mã để dễ dàng sao chép và dán:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent

154

Bạn không nên tắt xác minh. Thay vào đó bạn nên tải xuống một gói chứng chỉ, có lẽ gói curl sẽ làm gì?

Sau đó, bạn chỉ cần đặt nó trên máy chủ web của mình, cung cấp cho người dùng chạy quyền php để đọc tệp. Sau đó, mã này sẽ làm việc cho bạn:

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/path/to/bundle/cacert.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

Hy vọng, chứng chỉ gốc của trang web bạn đang cố truy cập nằm trong gói curl. Nếu không, điều này vẫn không hoạt động cho đến khi bạn lấy chứng chỉ gốc của trang web và đưa nó vào tệp chứng chỉ của bạn.


Tập tin .crt đến từ đâu? Không có liên kết trong trang web curl bạn cung cấp nó chỉ có .pem.
vee

1
Các tập tin pem nên giống nhau. Tại thời điểm này được đăng, họ có hai phiên bản của gói chứng chỉ trên trang web của họ, pem và crt và tôi chỉ sử dụng tệp crt cho ví dụ của tôi (tự nhiên đó là phiên bản họ sẽ xóa). Để tham khảo trong tương lai, các tệp crt thường được đổi tên thành các tệp pem, được đổi tên để các cửa sổ nhận ra tệp là một tệp chứng chỉ. Điều này sẽ không luôn luôn như vậy, nhưng đó là trong trường hợp này. Tôi sẽ cập nhật ví dụ của mình để nó sử dụng tệp pem hiện tại trên trang web curl
elitechief21

4
Cũng có một chức năng hữu ích stream_context_set_defaultcó thể được sử dụng để bạn không phải chuyển nó vào file_get_contents mọi lúc
Ken Koch

Đã có ai từng làm việc này chưa? Tôi đã thử với gói cacert.pem được liên kết trong câu trả lời này và cả với chứng chỉ Comodo CA Root (là gốc CA cho chứng chỉ mà tôi muốn xác minh), nhưng nó sẽ luôn thất bại.
zmippie

1
Từ góc độ bảo mật: bên cạnh việc sử dụng tùy chọn bối cảnh luồng quán cà phê, điều rất quan trọng là cũng xác định các mật mã được phép trong tùy chọn ngữ cảnh luồng mật mã và cấm các phiên bản SSL được gọi là dễ bị tổn thương. Ngoài ra, nên cài đặt tùy chọn bối cảnh luồng vô hiệu hóa thành true để giảm thiểu vectơ tấn công CRIME.
Josef Glatz

36

Tôi đã sửa lỗi này bằng cách đảm bảo rằng OpenSSL đã được cài đặt trên máy của tôi và sau đó thêm nó vào php.ini của tôi:

openssl.cafile=/usr/local/etc/openssl/cert.pem

@Akhi Bạn sẽ có thể tìm thấy một số hướng dẫn nếu bạn Google nó
andlin

2
Tôi đã sử dụng cùng một phương thức bằng PHP 7 trên IIS, tải xuống cert.pemtệp và đặt php.ininhư thế này và nó đã hoạt động:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua

1
Tôi đã tải xuống tệp PEM từ curl.haxx.se/docs/caextract.html - đã khắc phục sự cố cho tôi trên Windows bằng một URL gstatic.com cụ thể.
Jake

Tải xuống tệp PEM từ curl.haxx.se/docs/caextract.html không hoạt động với tôi trên Centos 7. Tôi đã tạo chứng chỉ gói bằng cách ghép chứng chỉ chính và pkcs7 và đặt nó trên máy chủ và sau đó chỉ định đường dẫn openssl.cafile. +1 cho câu trả lời và hướng đúng.
Arvind K.

Khi tạo một gói, đừng quên chuyển đổi pkcs thành tệp pem trước khi gắn nó với chứng nhận chính
Arvind K.

22

Bạn có thể khắc phục vấn đề này bằng cách viết một hàm tùy chỉnh sử dụng curl, như trong:

function file_get_contents_curl( $url ) {

  $ch = curl_init();

  curl_setopt( $ch, CURLOPT_AUTOREFERER, TRUE );
  curl_setopt( $ch, CURLOPT_HEADER, 0 );
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
  curl_setopt( $ch, CURLOPT_URL, $url );
  curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE );

  $data = curl_exec( $ch );
  curl_close( $ch );

  return $data;

}

Sau đó, chỉ sử dụng file_get_contents_curlthay vì file_get_contentsbất cứ khi nào bạn gọi một url bắt đầu bằng https.


Điều này làm việc cho tôi. Làm tốt lắm, cảm ơn!
Nick Green

11

Làm việc cho tôi, tôi đang sử dụng PHP 5.6. tiện ích mở rộng openssl nên được bật và trong khi gọi google map api verify_peer, hãy đặt sai Mã bên dưới đang hoạt động với tôi.

<?php
$arrContextOptions=array(
    "ssl"=>array(
         "verify_peer"=>false,
         "verify_peer_name"=>false,
    ),
);  
$url = "https://maps.googleapis.com/maps/api/geocode/json?latlng="
      . $latitude
      . ","
      . $longitude
      . "&sensor=false&key="
      . Yii::$app->params['GOOGLE_API_KEY'];

$data = file_get_contents($url, false, stream_context_create($arrContextOptions));

echo $data;
?>

10

Nếu phiên bản PHP của bạn là 5, hãy thử cài đặt cURL bằng cách nhập lệnh sau trong thiết bị đầu cuối:

sudo apt-get install php5-curl

9
Điều này hoàn toàn không có gì để làm với cURL.
Andreas

2
Cài đặt php curl nên là câu trả lời chính xác! Đối với hệ thống Mac của tôi, tôi đang sử dụng cổng và lệnh là:sudo port install php70-curl
hailong


6

Các bước dưới đây sẽ khắc phục vấn đề này,

  1. Tải xuống Chứng chỉ CA từ liên kết này: https://curl.haxx.se/ca/cacert.pem
  2. Tìm và mở php.ini
  3. Tìm kiếm curl.cainfovà dán đường dẫn tuyệt đối nơi bạn đã tải xuống Chứng chỉ.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Khởi động lại WAMP / XAMPP (máy chủ apache).
  5. Nó hoạt động!

mong rằng sẽ giúp !!


cái này có an toàn không Tôi yêu các giải pháp dễ dàng nhưng tôi thiếu một số thông tin ở đây ..
swisswiss

để thử nghiệm là ok
Geomorillo

5

Chỉ muốn thêm vào điều này vì tôi gặp phải vấn đề tương tự và không có gì tôi có thể tìm thấy ở bất cứ đâu sẽ hoạt động (ví dụ: tải xuống tệp cacert.pem, đặt cafile trong php.ini, v.v.)

Nếu bạn đang sử dụng NGINX và chứng chỉ SSL của bạn đi kèm với "chứng chỉ trung gian", bạn cần kết hợp tệp chứng chỉ trung gian với tệp "mydomain.com.crt" chính của mình và nó sẽ hoạt động. Apache có một cài đặt cụ thể cho các certs trung gian, nhưng NGINX không vì vậy nó phải nằm trong cùng một tệp với chứng chỉ thông thường của bạn.


4

Lý do cho lỗi này là PHP không có danh sách các cơ quan cấp chứng chỉ tin cậy.

PHP 5.6 trở lên cố gắng tự động tải các CA được hệ thống tin cậy. Các vấn đề với điều đó có thể được sửa chữa. Xem http://php.net/manual/en/migration56.openssl.php để biết thêm thông tin.

PHP 5.5 trở về trước thực sự khó thiết lập chính xác vì bạn phải chỉ định gói CA theo cách thủ công trong từng ngữ cảnh yêu cầu, một điều bạn không muốn rắc xung quanh mã của mình. Vì vậy, tôi đã quyết định cho mã của mình rằng đối với các phiên bản PHP <5.6, xác minh SSL đơn giản bị vô hiệu hóa:

$req = new HTTP_Request2($url);
if (version_compare(PHP_VERSION, '5.6.0', '<')) {
    //correct ssl validation on php 5.5 is a pain, so disable
    $req->setConfig('ssl_verify_host', false);
    $req->setConfig('ssl_verify_peer', false);
}

Điều này giúp tôi tìm ra vấn đề của mình. Mặc dù tôi đang dùng PHP 5.6, tôi đã sử dụng một thư viện ứng dụng khách api đã lỗi thời, chỉ định thủ công một tệp CA cũ bằng cách sử dụng tùy chọn bối cảnh quán cà phê theo liên kết của bạn ở trên. Loại bỏ nó khỏi thư viện khách api đã sửa nó cho tôi. Có lẽ PHP đã bắt đầu sử dụng gói tin cậy của OpenSSL
Joe Lipson

4

Có cùng lỗi với PHP 7 trên XAMPP và OSX.

Câu trả lời được đề cập ở trên trong https://stackoverflow.com/ là tốt, nhưng nó không giải quyết được hoàn toàn vấn đề cho tôi. Tôi đã phải cung cấp chuỗi chứng chỉ hoàn chỉnh để làm cho file_get_contents () hoạt động trở lại. Đó là cách tôi đã làm:

Nhận chứng chỉ gốc / trung cấp

Trước hết tôi phải tìm ra cái gốc chứng chỉ trung gian.

Cách thuận tiện nhất có lẽ là một công cụ chứng chỉ trực tuyến như ssl-shopper

Ở đó tôi tìm thấy ba chứng chỉ, một chứng chỉ máy chủ và hai chứng chỉ chuỗi (một là chứng chỉ gốc, một chứng chỉ khác là trung gian).

Tất cả những gì tôi cần làm chỉ là tìm kiếm trên internet cho cả hai. Trong trường hợp của tôi, đây là root:

thawte DV SSL SHA256 CA

Và nó dẫn đến url thawte.com của anh ấy . Vì vậy, tôi chỉ cần đặt chứng chỉ này vào một tệp văn bản và làm tương tự cho trung gian. Làm xong.

Nhận chứng chỉ máy chủ

Điều tiếp theo tôi phải làm là tải xuống chứng chỉ máy chủ của mình. Trên Linux hoặc OS X, nó có thể được thực hiện với openssl:

openssl s_client -showcerts -connect whatsyoururl.de:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/whatsyoururl.de.cert

Bây giờ mang tất cả lại với nhau

Bây giờ chỉ cần hợp nhất tất cả chúng vào một tập tin. (Có lẽ thật tốt khi chỉ cần đặt chúng vào một thư mục, tôi chỉ hợp nhất chúng vào một tệp). Bạn có thể làm như thế này:

cat /tmp/thawteRoot.crt > /tmp/chain.crt
cat /tmp/thawteIntermediate.crt >> /tmp/chain.crt
cat /tmp/tmp/whatsyoururl.de.cert >> /tmp/chain.crt

nói với PHP nơi tìm chuỗi

Có một hàm tiện dụng openssl_get_cert_locations () sẽ cho bạn biết, nơi PHP đang tìm kiếm các tệp cert. Và có tham số này, nó sẽ cho file_get_contents () nơi tìm tệp cert. Có lẽ cả hai cách sẽ làm việc. Tôi thích cách tham số. (So ​​với giải pháp đã đề cập ở trên).

Vì vậy, đây là Mã PHP của tôi

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/Applications/XAMPP/xamppfiles/share/openssl/certs/chain.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents($myHttpsURL, 0, stream_context_create($arrContextOptions));

Đó là tất cả. file_get_contents () đang hoạt động trở lại. Không có CURL và hy vọng không có lỗi bảo mật.


Tập tin là chain.pemgì? Đây có phải là chain.crt?
Dr.X

Nó không phải là chain.crt, đó là chứng chỉ thực tế. Đó là danh sách nếu chứng chỉ trung gian, còn gọi là chuỗi chứng chỉ. Bạn không nhất thiết cần nó. Sử dụng trình kiểm tra chứng chỉ SSL để tìm hiểu xem bạn có cần nó không. Nếu vậy, bạn có thể tìm kiếm tên của công ty phát hành chứng chỉ của bạn + cụm từ "chuỗi" hoặc "trung gian" để tìm tệp chính xác.
nr

4

Sau khi trở thành nạn nhân của vấn đề này trên centOS sau khi cập nhật php lên php5.6, tôi đã tìm thấy một giải pháp hiệu quả cho mình.

Lấy thư mục chính xác cho certs của bạn được đặt theo mặc định với điều này

php -r 'print_r(openssl_get_cert_locations()["default_cert_file"]);'

Sau đó sử dụng cái này để lấy chứng chỉ và đặt nó vào vị trí mặc định được tìm thấy từ đoạn mã trên

wget http://curl.haxx.se/ca/cacert.pem -O <default location>

3

Có vấn đề ssl tương tự trên máy phát triển của tôi (php 7, xampp trên windows) với chứng chỉ tự ký đang cố gắng tạo tệp " https: // localhost / ..." -. Rõ ràng là hội đồng chứng chỉ gốc (cacert.pem) không hoạt động. Tôi vừa sao chép mã thủ công từ apache server.crt-File trong tệp cacert.pem đã tải xuống và thực hiện mục openssl.cafile = path / to / cacert.pem trong php.ini


2

Một điều khác để thử là cài đặt lại ca-certificatesnhư chi tiết ở đây .

# yum reinstall ca-certificates
...
# update-ca-trust force-enable 
# update-ca-trust extract

Và một điều nữa cần thử là cho phép rõ ràng cho phép chứng chỉ của một trang web được mô tả ở đây (đặc biệt nếu một trang web là máy chủ của riêng bạn và bạn đã có .pem trong tầm tay).

# cp /your/site.pem /etc/pki/ca-trust/source/anchors/
# update-ca-trust extract

Tôi đã gặp phải lỗi SO chính xác này sau khi nâng cấp lên PHP 5.6 trên CentOS 6 khi cố truy cập vào máy chủ có chứng chỉ bảo mật giá rẻ có thể cần cập nhật, nhưng thay vào đó tôi đã cài đặt chứng chỉ letencrypt và với hai bước trên nó đã làm mánh khóe Tôi không biết tại sao bước thứ hai là cần thiết.


Các lệnh hữu ích

Xem phiên bản openssl:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

Xem các cài đặt hiện tại của PHP cli ssl:

# php -i | grep ssl
openssl
Openssl default config => /etc/pki/tls/openssl.cnf
openssl.cafile => no value => no value
openssl.capath => no value => no value

1

Về các lỗi tương tự như

[11-May-2017 19:19:13 America / Chicago] Cảnh báo PHP: file_get_contents (): Thao tác SSL không thành công với mã 1. OpenSSL Thông báo lỗi: error: 14090086: Các thường trình SSL: ssl3_get_server_cert ve: chứng nhận xác nhận không thành công

Bạn đã kiểm tra các quyền của chứng chỉ và thư mục được tham chiếu bởi openssl chưa?

Bạn có thể làm được việc này

var_dump(openssl_get_cert_locations());

Để có được một cái gì đó tương tự như thế này

array(8) {
  ["default_cert_file"]=>
  string(21) "/usr/lib/ssl/cert.pem"
  ["default_cert_file_env"]=>
  string(13) "SSL_CERT_FILE"
  ["default_cert_dir"]=>
  string(18) "/usr/lib/ssl/certs"
  ["default_cert_dir_env"]=>
  string(12) "SSL_CERT_DIR"
  ["default_private_dir"]=>
  string(20) "/usr/lib/ssl/private"
  ["default_default_cert_area"]=>
  string(12) "/usr/lib/ssl"
  ["ini_cafile"]=>
  string(0) ""
  ["ini_capath"]=>
  string(0) ""
}

Vấn đề này làm tôi thất vọng trong một thời gian, cho đến khi tôi nhận ra rằng thư mục "certs" của tôi có 700 quyền, khi đó đáng lẽ phải có 755 quyền. Hãy nhớ rằng, đây không phải là thư mục cho các khóa mà là chứng chỉ. Tôi khuyên bạn nên đọc liên kết này về quyền ssl.

Khi tôi đã làm

chmod 755 certs

Vấn đề đã được khắc phục, ít nhất là đối với tôi.


Yay! CHMOD cũng là vấn đề của tôi ;-) Cảm ơn
RA.

0

Tôi gặp vấn đề tương tự đối với một trang bảo mật khác khi sử dụng wgethoặcfile_get_contents . Rất nhiều nghiên cứu (bao gồm một số câu trả lời cho câu hỏi này) đã dẫn đến một giải pháp đơn giản - cài đặt Curl và PHP-Curl - Nếu tôi hiểu đúng, Curl có Root CA cho Comodo đã giải quyết vấn đề

Cài đặt addon Curl và PHP-Curl, sau đó khởi động lại Apache

sudo apt-get install curl
sudo apt-get install php-curl
sudo /etc/init.d/apache2 reload

Tất cả bây giờ làm việc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.