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. Если это не так, это все равно не будет работать, пока вы не получите корневой сертификат сайта и не поместите его в файл сертификата.

elitechief21
источник
Откуда взялся файл .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
andlin
источник
@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;
?>
Dipti
источник
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. Оно работает!

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

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

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

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

SlickTheNick
источник
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);
}
cweiske
источник
Это помогло мне найти мою проблему. Несмотря на то, что я использую 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

hangerer
источник
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

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

Aleks
источник
Ура! 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

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

Aubs
источник