file_get_contents (): операция SSL не выполнена с кодом 1, не удалось включить шифрование


188

Я пытался получить доступ к этой конкретной службе REST со страницы PHP, которую я создал на нашем сервере. Я сузил проблему до этих двух строк. Итак, моя страница PHP выглядит так:

<?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; ?>

Страница умирает в строке 2 со следующими ошибками:

  • Предупреждение: file_get_contents (): сбой операции SSL с кодом 1. Сообщения об ошибках OpenSSL: ошибка: 14090086: процедуры SSL: SSL3_GET_SERVER_CERTIFICATE: сбой проверки сертификата в ... php в строке 2
    • Предупреждение: file_get_contents (): не удалось включить шифрование в ... php в строке 2
    • Предупреждение: 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): не удалось открыть поток: не удалось выполнить операцию ... php в строке 2

Мы используем сервер Gentoo. Недавно мы обновились до версии PHP 5.6. Именно после обновления появилась эта проблема.

Я обнаружил, когда я заменяю службу REST на адрес, подобный https://www.google.com; моя страница работает просто отлично.

В более ранней попытке я установил “verify_peer”=>falseи передал это в качестве аргумента file_get_contents, как описано здесь: file_get_contents игнорируя verify_peer => false? Но, как заметил писатель; это не имело никакого значения.

Я спросил одного из наших администраторов сервера, существуют ли эти строки в нашем файле php.ini:

  • расширение = php_openssl.dll
  • allow_url_fopen = On

Он сказал мне, что, поскольку мы находимся на Gentoo, openssl компилируется при сборке; и это не установлено в файле php.ini.

Я также подтвердил, что allow_url_fopenработает. В связи со спецификой этой проблемы; Я не нахожу много информации для помощи. Кто-нибудь из вас сталкивался с чем-то подобным? Спасибо.


Если вы используете Kaspersky, проверьте это: stackoverflow.com/a/54791481/3549317
— cespon

Ответы:


344

Это была чрезвычайно полезная ссылка для поиска:

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

Официальный документ, описывающий изменения, внесенные для открытия ssl в PHP 5.6 Отсюда я узнал еще об одном параметре, который я должен был установить в false: "verify_peer_name" => false

Примечание. Это имеет очень важное значение для безопасности. Отключение проверки потенциально позволяет злоумышленнику MITM использовать недействительный сертификат для прослушивания запросов. Хотя это может быть полезно для местного развития, в производстве должны использоваться другие подходы.

Итак, мой рабочий код выглядит так:

<?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
это нарушает сертификацию SSL и является дырой в безопасности
— hypery2k

8
Как отметил @ hypery2k, вы не должны просто отключать проверку. Смотрите мой ответ для альтернативного решения, которое не противоречит цели использования ssl.
— elitechief21

4
Это действительно не должно быть сделано. Вместо того, чтобы обойти проблему, вы должны ее исправить. Смотрите лучшее решение от @ elitechief21.
— Джаспер

5
Это может быть неприятно, если вы тестируете локальную среду на предмет API. В этом случае я обычно проверяю ядерное оружие.
— HappyCoder

12
Так как я всегда прихожу сюда, когда я пытаюсь это сделать, вот код, который легко скопировать и вставить:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
— Laurent

154

Вы не должны просто отключить проверку. Скорее вы должны скачать пакет сертификатов, возможно, пакет curl подойдет?

Затем вам просто нужно поместить его на ваш веб-сервер, предоставив пользователю, который запускает php, разрешение на чтение файла. Тогда этот код должен работать для вас:

$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));

Надеюсь, корневой сертификат сайта, к которому вы пытаетесь получить доступ, находится в комплекте curl. Если это не так, это все равно не будет работать, пока вы не получите корневой сертификат сайта и не поместите его в файл сертификата.


Откуда взялся файл .crt? На сайте curl нет ссылки, которую вы предоставляете, только .pem.
— ви

1
Файл pem должен быть таким же. В то время, когда это было опубликовано, у них было две версии пакета сертификатов на их сайте, pem и crt, и я просто использовал файл crt для своего примера (естественно, это та, которую они удалили). Для дальнейшего использования файлы crt обычно переименовываются в файлы pem, которые переименовываются, чтобы Windows распознала файл как файл сертификата. Это не всегда так, но это так. Я обновлю свой пример, чтобы он использовал файл pem, который в данный момент находится на сайте curl
— elitechief21

4
есть также полезная функция, stream_context_set_defaultкоторую можно использовать, чтобы вам не приходилось каждый раз передавать ее в file_get_contents
— Кен Кох

Кто-нибудь когда-нибудь заставлял это работать? Я попытался с пакетом cacert.pem, связанным с этим ответом, а также с корневым сертификатом CA Comodo (который является корневым CA для сертификата, который я хочу проверить), но он всегда будет терпеть неудачу.
— zmippie

1
С точки зрения безопасности: наряду с использованием опции контекста потока кафе может быть очень важно также определить разрешенные шифры в опции контекста потока шифров и запретить те версии SSL, которые известны как уязвимые. Также рекомендуется установить для параметра контекста потока значение disable_compression, равное true, для смягчения вектора атаки CRIME.
— Йозеф Глатц

36

Я исправил это, убедившись, что OpenSSL был установлен на моей машине, а затем добавил это в мой php.ini:

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

@Akhi Вы сможете найти несколько учебных пособий, если будете
— гуглить

2
Я использовал тот же метод, используя PHP 7 на IIS, скачал cert.pemфайл и установил вот php.iniтак, и это сработало:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
— Дэвид Рефуа

1
Я загрузил файл PEM с curl.haxx.se/docs/caextract.html - исправил проблему для меня в Windows с определенным URL-адресом gstatic.com.
— Джейк

Загрузка файла PEM из curl.haxx.se/docs/caextract.html для меня не сработала в Centos 7. Я создал сертификат пакета, связав основной сертификат и pkcs7 и поместив его на сервер, а затем указал путь openssl.cafile. +1 за правильный ответ и направление.
— Арвинд К.

При создании пакета не забудьте преобразовать файл pkcs в файл pem, прежде чем вносить его в основную сертификацию
— Arvind K.

22

Вы можете обойти эту проблему, написав пользовательскую функцию, которая использует curl, как в:

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;

}

Тогда просто используйте file_get_contents_curlвместо file_get_contentsвсякий раз, когда вы вызываете URL, который начинается с https.


Это сработало для меня. Отличная работа и спасибо!
— Ник Грин

11

Работая на меня, я использую PHP 5.6. Расширение openssl должно быть включено, и при вызове карты Google API api verify_peer сделать false Ниже код работает для меня.

<?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

Если ваша версия PHP 5, попробуйте установить cURL, введя следующую команду в терминале:

sudo apt-get install php5-curl

9
Это не имеет ничего общего с cURL.
— Андреас

2
Установка php curl должна быть правильным ответом! Для моей системы Mac я использую порт, и команда:sudo port install php70-curl
— hailong

8

В основном вам нужно установить переменную среды SSL_CERT_FILE в путь к файлу PEM ssl-сертификата, загруженного по следующей ссылке: http://curl.haxx.se/ca/cacert.pem .

Мне понадобилось много времени, чтобы понять это.


Ссылка не сработала, но я обнаружил, что это похоже на: akrabat.com/ssl-certificate-verification-on-php-5-6
— Даниэль П

6

следующие шаги помогут решить эту проблему,

  1. Загрузите сертификат CA по этой ссылке: https://curl.haxx.se/ca/cacert.pem
  2. Найдите и откройте php.ini
  3. Найдите curl.cainfoи вставьте абсолютный путь для загрузки сертификата.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Перезапустите WAMP / XAMPP (сервер Apache).
  5. Оно работает!

надеюсь, это поможет !!


это безопасно? Я люблю простые решения, но мне не хватает некоторой информации здесь ..
— swisswiss

для тестирования в порядке
— Geomorillo

5

Просто хотел добавить к этому, так как столкнулся с той же проблемой, и ничего, что я не мог найти где-либо, не сработало бы (например, загрузка файла cacert.pem, настройка cafile в php.ini и т. Д.)

Если вы используете NGINX и ваш SSL-сертификат поставляется с «промежуточным сертификатом», вам нужно объединить промежуточный файл сертификата с вашим основным файлом «mydomain.com.crt», и он должен работать. Apache имеет настройку, специфичную для промежуточных сертификатов, но NGINX нет, поэтому он должен находиться в том же файле, что и ваш обычный сертификат.


4

Причиной этой ошибки является то, что в PHP нет списка доверенных центров сертификации.

PHP 5.6 и более поздние версии пытаются автоматически загрузить CA, которым доверяет система. Проблемы с этим можно исправить. См. Http://php.net/manual/en/migration56.openssl.php для получения дополнительной информации.

PHP 5.5 и более ранние версии действительно сложно правильно настроить, так как вам нужно вручную указывать пакет CA в каждом контексте запроса, что не нужно разбрасывать вокруг кода. Поэтому я решил для своего кода, что для версий PHP <5.6, проверка SSL просто отключается:

$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);
}

Это помогло мне найти мою проблему. Несмотря на то, что я использую PHP 5.6, я использовал устаревшую клиентскую библиотеку api, которая вручную указала старый файл CA с помощью опции контекста cafile, как указано в ссылке выше. Удаление этого из клиентской библиотеки API исправило это для меня. Предположительно PHP начал использовать доверенный пакет OpenSSL
— Джо Липсон,

4

Была такая же ошибка с PHP 7 на XAMPP и OSX.

Вышеупомянутый ответ в https://stackoverflow.com/ хорош, но он не полностью решил проблему для меня. Мне пришлось предоставить полную цепочку сертификатов, чтобы file_get_contents () снова заработал. Вот как я это сделал:

Получить корневой / промежуточный сертификат

Прежде всего я должен был выяснить, что такое корневой и промежуточный сертификат.

Наиболее удобный способ - это, возможно, онлайн-инструмент для сертификации, такой как ssl-shopper.

Там я нашел три сертификата, один серверный сертификат и два цепных сертификата (один является корневым, другой явно промежуточным).

Все, что мне нужно сделать, это просто искать в Интернете их обоих. В моем случае это корень:

thawte DV SSL SHA256 CA

И это приводит к его URL thawte.com . Так что я просто поместил этот сертификат в текстовый файл и сделал то же самое для промежуточного. Готово.

Получить сертификат хоста

Следующее, что мне нужно было, это загрузить сертификат моего сервера. В Linux или OS X это можно сделать с помощью openssl:

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

Теперь соберите их всех вместе

Теперь просто объедините их в один файл. (Может быть, хорошо просто поместить их в одну папку, я просто слил их в один файл). Вы можете сделать это так:

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

скажи PHP где найти цепочку

Есть эта удобная функция openssl_get_cert_locations (), которая скажет вам, где PHP ищет файлы сертификатов. И есть этот параметр, который сообщит file_get_contents (), где искать файлы сертификатов. Может быть, оба способа будут работать. Я предпочел параметр способом. (По сравнению с решением, упомянутым выше).

Так что теперь это мой PHP-код

$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));

Вот и все. file_get_contents () снова работает. Без CURL и, надеюсь, без недостатков безопасности.


Что за файл chain.pem? Это chain.crt?
— Dr.X

Это не chain.crt, это для фактического сертификата. Это список промежуточных сертификатов, он же цепочка сертификатов. Вам не обязательно это нужно. Используйте SSL-сертификат для проверки, если вам это нужно. Если это так, вы можете найти имя вашего эмитента сертификата + термин «цепочка» или «промежуточный», чтобы найти правильный файл.
— Н.Р.

4

После того, как я стал жертвой этой проблемы в CentOS после обновления php до php5.6, я нашел решение, которое помогло мне.

Получите правильный каталог для ваших сертификатов, который будет размещен по умолчанию с этим

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

Затем используйте это, чтобы получить сертификат и поместить его в местоположение по умолчанию, найденное из кода выше

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

3

У меня была такая же проблема ssl на моей машине разработчика (php 7, xampp на windows) с самозаверяющим сертификатом, который пытался открыть файл « https: // localhost / ...». Очевидно, что сборка корневого сертификата (cacert.pem) не сработала. Я просто скопировал вручную код из apache server.crt-File в скачанный cacert.pem и сделал запись openssl.cafile = path / to / cacert.pem в php.ini


2

Другая вещь, которую стоит попробовать - это переустановить, ca-certificatesкак описано здесь .

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

И еще одна вещь, которую нужно попробовать, это явно разрешить сертификат одного сайта, о котором идет речь, как описано здесь (особенно, если один сайт является вашим собственным сервером, и у вас уже есть доступ к .pem).

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

Я столкнулся с этой точной ошибкой SO после обновления до PHP 5.6 на CentOS 6, пытаясь получить доступ к самому серверу, у которого есть сертификат cheapsslsecurity, который, возможно, его нужно было обновить, но вместо этого я установил сертификат letsencrypt, и с этими двумя шагами выше он сделал Хитрость. Я не знаю, почему был необходим второй шаг.


Полезные команды

Посмотреть версию openssl:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

Просмотр текущих настроек PHP:

# 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

Относительно ошибок, подобных

[11-May-2017 19:19:13 Америка / Чикаго] Предупреждение PHP: file_get_contents (): сбой операции SSL с кодом 1. OpenSSL Сообщения об ошибках: ошибка: 14090086: процедуры SSL: ssl3_get_server_certificate: сбой проверки сертификата

Вы проверили разрешения сертификата и каталогов, на которые ссылается openssl?

Ты можешь сделать это

var_dump(openssl_get_cert_locations());

Чтобы получить что-то похожее на это

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) ""
}

Эта проблема на некоторое время расстроила меня, пока я не понял, что моя папка «certs» имеет 700 разрешений, тогда как она должна была иметь 755 разрешений. Помните, что это не папка для ключей, а сертификаты. Я рекомендую прочитать эту ссылку на разрешения SSL.

Однажды я сделал

chmod 755 certs

Проблема была решена, по крайней мере, для меня в любом случае.


Ура! CHMOD был моей проблемой ;-) Спасибо
— РА.

0

У меня была такая же проблема для другой защищенной страницы при использовании wgetилиfile_get_contents . Много исследований (включая некоторые ответы на этот вопрос) привели к простому решению - установке Curl и PHP-Curl - если я правильно понял, у Curl есть Root CA для Comodo, который решил проблему

Установите Curl и PHP-Curl addon, затем перезапустите Apache

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

Все сейчас работает.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.