file_get_contents(): операция SSL не удалась с кодом 1. И многое другое
Я пытался получить доступ к этой конкретной службе 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
работает. В связи со спецификой этой проблемы; Я не нахожу много информации для помощи. Кто-нибудь из вас сталкивался с чем-то подобным? Благодарю.
21 ответ
Это была чрезвычайно полезная ссылка для поиска:
http://php.net/manual/en/migration56.openssl.php
Официальный документ, описывающий изменения, внесенные для открытия ssl в PHP 5.6 Отсюда я узнал еще об одном параметре, который я должен был установить в false: "verify_peer_name"=>false
Итак, мой рабочий код выглядит так:
<?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; ?>
Вы не должны просто отключить проверку. Скорее вы должны скачать пакет сертификатов, возможно, пакет 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. Если это не так, это все равно не будет работать, пока вы не получите корневой сертификат сайта и не поместите его в файл сертификата.
Я исправил это, убедившись, что OpenSSL был установлен на моей машине, а затем добавил это в мой php.ini:
openssl.cafile=/usr/local/etc/openssl/cert.pem
Вы можете обойти эту проблему, написав пользовательскую функцию, которая использует 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.
Работая на меня, я использую PHP 5.6. Расширение openssl должно быть включено, и при вызове карты Google API api verify_peer сделать false Ниже код работает для меня.
<?php
$arrContextOptions=array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
),
);
$response = file_get_contents("https://maps.googleapis.com/maps/api/geocode /json?latlng=" . $latitude . "," . $longitude . "&sensor=false&key=" . Yii::$app->params['GOOGLE_API_KEY'], false, stream_context_create($arrContextOptions));
echo $response; ?>
После того, как я стал жертвой этой проблемы в CentOS после обновления php до php5.6, я нашел решение, которое помогло мне.
Получите правильный каталог для ваших сертификатов, который будет размещен по умолчанию с этим
php -r 'print_r(openssl_get_cert_locations());' | grep '\[default_cert_file\]' | awk '{print $3}'
Затем используйте это, чтобы получить сертификат и поместить его в местоположение по умолчанию, найденное из кода выше
wget http://curl.haxx.se/ca/cacert.pem -O <default location>
Сначала вам нужно включить
curl
расширение в PHP. Тогда вы можете использовать эту функцию:
function file_get_contents_ssl($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_REFERER, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3000); // 3 sec.
curl_setopt($ch, CURLOPT_TIMEOUT, 10000); // 10 sec.
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
Работает аналогично функции file_get_contents(..) .
Пример:
echo file_get_contents_ssl("https://www.example.com/");
Выход:
<!doctype html>
<html>
<head>
<title>Example Domain</title>
...
В основном вам нужно установить переменную среды SSL_CERT_FILE в путь к файлу PEM ssl-сертификата, загруженного по следующей ссылке: http://curl.haxx.se/ca/cacert.pem.
Мне понадобилось много времени, чтобы понять это.
Если у вас версия PHP 5, попробуйте установить cURL, набрав в терминале следующую команду:
sudo apt-get install php5-curl
Следующие шаги помогут решить эту проблему,
- Загрузите сертификат CA по этой ссылке: https://curl.haxx.se/ca/cacert.pem
- Найдите и откройте php.ini
- Ищу
curl.cainfo
и вставьте абсолютный путь, по которому вы скачали сертификат.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
- Перезапустите WAMP/XAMPP (сервер Apache).
- Оно работает!
надеюсь, это поможет!!
<?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; ?>
Просто протестируйте версию PHP 7.2, она работает хорошо
Была такая же ошибка с PHP 7 на XAMPP и OSX.
Вышеупомянутый ответ в /questions/24018680/filegetcontents-operatsiya-ssl-ne-udalas-s-kodom-1-i-mnogoe-drugoe/24018703#24018703 хорош, но он не полностью решил проблему для меня. Мне пришлось предоставить полную цепочку сертификатов, чтобы 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 и, надеюсь, без недостатков безопасности.
Причиной этой ошибки является то, что в 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);
}
Просто хотел добавить к этому, так как столкнулся с той же проблемой, и ничего, что я нигде не мог найти, работало бы (например, загрузка файла cacert.pem, настройка cafile в php.ini и т. Д.)
Если вы используете NGINX и ваш SSL-сертификат поставляется с "промежуточным сертификатом", вам нужно объединить промежуточный файл сертификата с вашим основным файлом "mydomain.com.crt", и он должен работать. Apache имеет настройку, специфичную для промежуточных сертификатов, но NGINX нет, поэтому он должен находиться в том же файле, что и ваш обычный сертификат.
У меня была та же проблема ssl на моей машине разработчика (php 7, xampp на windows) с самозаверяющим сертификатом, который пытался открыть файл " https://localhost/...". Очевидно, что сборка корневого сертификата (cacert.pem) не сработала. Я просто скопировал вручную код из apache server.crt-File в скачанный cacert.pem и сделал запись openssl.cafile=path/to/cacert.pem в php.ini
Еще одна вещь, чтобы попробовать это переустановить 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 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
Исправление для macos 12.4/Mamp 6.6/Homebrew 3.5.2/ [электронная почта защищена]
Терминал
Проверить версию
openssl version -a
Мой указывал на:
...
OPENSSLDIR: "/opt/homebrew/etc/openssl@3"
...
Итак, я просмотрел каталог доморощенного/opt/homebrew/etc/openssl@3
и нашел cert.pem и убедился, что текущая версия файла php.ini моего Mamp указывает на правильный cert.pem версии openssl для доморощенного
добавить в php.ini
openssl.cafile=/opt/homebrew/etc/openssl@3/cert.pem
Относительно ошибок, подобных
[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
Проблема была исправлена, по крайней мере, для меня в любом случае.
У меня была такая же проблема для другой защищенной страницы при использовании 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
Все сейчас работает.
Что касается меня, я запускал XAMPP на машине с Windows 10 (localhost) и недавно обновился до PHP 8. Я пытался открыть HTTPS-ссылку localhost через file_get_contents().
В моем файле php.ini была строка, которая гласила:
openssl.cafile="C:\Users\[USER]\xampp\apache\bin\curl-ca-bundle.crt"
Этот комплект сертификатов использовался для проверки «внешних» URL-адресов и был пакетом от Mozilla, как уже обсуждали некоторые люди. Я не знаю, пришел ли XAMPP таким путем или я устанавливал его раньше.
В какой-то момент я настроил HTTPS на моем локальном хосте, что привело к созданию другого пакета сертификатов. Этот пакет нужно было использовать для проверки URL-адресов localhost. Чтобы напомнить себе, где находится этот пакет, я открыл httpd-ssl.conf и нашел строку, которая гласила:
SSLCertificateFile "conf/ssl.crt/server.crt"
(Полный путь был C:\Users[USER]\xampp\apache\ conf \ ssl.crt \ server.crt)
Чтобы заставить работать как локальный, так и внешний URL-адреса одновременно, я скопировал содержимое моего файла localhost «server.crt» в пакет Mozilla «curl-ca-bundle.crt».
.
.
.
m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
-----END CERTIFICATE-----
Localhost--I manually added this
================================
-----BEGIN CERTIFICATE-----
MIIDGDCCAgCgAwIBAgIQIH+mTLNOSKlD8KMZwr5P3TANBgkqhkiG9w0BAQsFADAU
...
На этом этапе я мог использовать file_get_contents () как с URL-адресами localhost, так и с внешними URL-адресами без дополнительной настройки.
file_get_contents("https://localhost/...");
file_get_contents("https://google.com");
$csm = stream_context_create(['ssl' => ['capture_session_meta' => TRUE]]);$sourceCountry=file_get_contents('https://api.wipmania.com/'.$ip.'?website.com', FALSE, $csm);