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

Сторінка вмирає на другому рядку із такими помилками:

  • Попередження: file_get_contents (): Операція SSL не виконана з кодом 1. Повідомлення про помилки OpenSSL: помилка: 14090086: Підпрограми SSL: SSL3_GET_SERVER_CERTIFICATE: не вдалося перевірити сертифікат у ... php у рядку 2
    • Попередження: file_get_contents (): не вдалося ввімкнути криптовалюту у ... php у другому рядку
    • Попередження: 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:

  • extension = php_openssl.dll
  • enable_url_fopen = Увімкнено

Він сказав мені, що оскільки ми перебуваємо на Gentoo, відкриваючий файл збирається, коли ми будуємо; і він не встановлений у файлі 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
Це може бути засмучуючи, якщо ви тестуєтесь з місцевого середовища на тлі. У цьому випадку я зазвичай нуклеюю перевірку.
HappyCoder

12
Оскільки я завжди приїжджаю сюди, коли намагаюся це зробити, ось код, який легко копіювати та вставляти:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent

154

Не слід просто вимикати перевірку. Швидше ви повинні завантажувати пакет сертифікатів, можливо, пакет із завитками зробить?

Тоді вам просто потрібно помістити його на свій веб-сервер, надавши користувачеві, який запускає 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.
vee

1
Файл pem повинен бути однаковим. У той час, коли це було розміщено, вони мали дві версії пакету сертифікатів на своєму сайті, pem і crt, і я просто використовував файл crt для свого прикладу (природно, це те, що вони видалять). Для подальшої довідки, файли crt зазвичай перейменовані у файли pem, які перейменовані так, що Windows розпізнає файл як файл сертифіката. Це не завжди так, але це в цьому випадку. Я
оновлю

4
є також корисна функція, stream_context_set_defaultяку можна використовувати, тому вам не доведеться щоразу передавати її у file_get_contents
Кен Кох

Хто-небудь коли-небудь примушував це працювати? Я спробував із пакетом cacert.pem, пов'язаним у цій відповіді, а також із сертифікатом кореня Comodo CA (який є коренем CA для сертифікату, який я хочу перевірити), але він завжди не вдасться.
zmippie

1
З точки зору безпеки: поруч із використанням контекстного варіанту потоку файлів cafile, може бути дуже важливим також визначити дозволені шифри в контекстному варіанті потоку шифрів і заборонити ті версії SSL, які відомі як вразливі. Також для встановлення параметра контексту потоку enable_compression на значення true рекомендується пом'якшити вектор атаки ЗЛОЧОМ.
Йозеф Глац

36

Я вирішив це, переконавшись, що OpenSSL був встановлений на моїй машині, а потім додав це до мого php.ini:

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

@Akhi Ви маєте змогу знайти кілька навчальних посібників, якщо ви опублікуєте це в Google
andlin

2
Я використовував той самий метод, використовуючи PHP 7 на IIS, завантажив cert.pemфайл і встановив php.iniподібне, і він спрацював:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua

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 map api verify_peer зробити помилковим Нижче код працює для мене.

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

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

для тестування все гаразд
Геоморілло

5

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

Якщо ви використовуєте NGINX, а ваш сертифікат SSL постачається з "проміжним сертифікатом", вам потрібно поєднати проміжний файл cert з вашим основним файлом "mydomain.com.crt", і він повинен працювати. Apache має налаштування, специфічне для проміжних сертифікатів, але NGINX це не так, воно повинно бути в тому ж файлі, що і ваш звичайний cert.


4

Причиною цієї помилки є те, що PHP не має списку довірених авторизованих сертифікатів.

PHP 5.6 і пізніші спроби автоматично завантажувати ЦС, яким довіряє система. Проблеми з цим можна вирішити. Для отримання додаткової інформації див. 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 почав використовувати надійний пакет OpenSSLs
Джо Ліпсон

4

Була така ж помилка з PHP 7 на XAMPP та OSX.

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

Отримайте кореневий / проміжний сертифікат

Перш за все мені довелося розібратися, що таке корінь та проміжний сертифікат.

Найзручніший спосіб - це, можливо, онлайн-інструмент cert, як ssl-shopper

Там я знайшов три сертифікати, один сертифікат сервера та два сертифікати ланцюга (один - кореневий, інший - проміжний).

Все, що мені потрібно зробити, це просто пошукати в Інтернеті обох. У моєму випадку це корінь:

thawte DV SSL SHA256 CA

І це призводить до його URL thawte.com . Тож я просто вклав цей серт у текстовий файл і зробив те саме для проміжного. Зроблено.

Отримайте сертифікат хоста

Наступне, що мені довелося - це завантажити мій сервер cert. В 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 шукає файли cert. І є цей параметр, який підкаже file_get_contents (), де шукати файли cert. Можливо, обидва способи спрацюють. Я віддав перевагу параметру способу. (Порівняно з рішенням, згаданим вище).

Отже, це мій 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, щоб дізнатися, чи потрібен він. Якщо це так, ви можете шукати ім’я вашого емітента cert + термін "ланцюжок" або "проміжний", щоб знайти правильний файл.
Н.Р.

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, намагаючись отримати доступ до самого сервера, який має дешевий сертифікат безпеки, який, можливо, його потрібно було оновити, але замість цього я встановив сертифікат 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

1

Щодо помилок, подібних до

[11-травня 2017 19:19:13 Америка / Чикаго] PHP Попередження: file_get_contents (): Операція SSL не виконана з кодом 1. OpenSSL Повідомлення про помилки: помилка: 14090086: Підпрограми SSL: ssl3_get_server_certificate: сертифікат не підтверджено

Ви перевірили дозволи cert та каталогів, на які посилається 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, після чого перезапустіть 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.