Невозможно file_get_contents или cURL через HTTPS
Я использую file_get_contents
захватить содержимое сайта в течение многих лет.
Недавно они обновили свой URL HTTPS
а также file_get_contents
перестал работать.
Я читал предыдущие вопросы и пробовал отмеченные решения, но ничего не помогло.
Например, я попробовал это, и он вернул следующее:
openssl: yes http wrapper: yes https wrapper: yes wrappers: array ( 0 => 'https', 1 => 'ftps', 2 => 'compress.zlib', 3 => 'compress.bzip2', 4 => 'php', 5 => 'file', 6 => 'data', 7 => 'http', 8 => 'ftp', 9 => 'zip', )
Итак, я попробовал это решение с file_get_contents
, но безрезультатно.
Затем я попробовал это решение с cURL
вообще игнорировать шифрование, но безрезультатно
Независимо от того, какое решение я пробую, ничего не возвращается.
Я не добавила extension=php_openssl.dll
а также allow_url_include = On
в PHP.ini
в соответствии с этим, поскольку данный конкретный сайт находится на общем хосте, и хостинговая компания не разрешает редактировать файл PHP.ini, хотя они могут быть уже включены по умолчанию.
Я пробовал другие HTTPS
сайты, а некоторые работают, а некоторые нет, и я не уверен, почему.
Я пытался с другого сервера (и другого IP) на том же веб-хосте, и он также не работал с целью HTTPS
сайт.
Как я могу отладить и исправить это?
ОБНОВИТЬ:
phpinfo показывает:
curl
cURL support enabled
cURL Information libcurl/7.36.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5 libssh2/1.8.0
openssl
OpenSSL support enabled
OpenSSL Version OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008
6 ответов
ЗАКЛЮЧИТЕЛЬНЫЙ ОТВЕТ
Если ваш провайдер не будет обновлять openSSL до TLS 1.2, вам следует серьезно подумать о другом провайдере. Вы должны проверить свой сервер с помощью ссылки "SSL SERVER TEST" ниже. Ваш сервер, вероятно, имеет уязвимости безопасности SSL.
Сервер, к которому вы пытаетесь подключиться, поддерживает только TLS 1.2 и TLS 1.1
Не поддерживает:TLS 1.0, SSL 3, SSL2.
Когда выполняется запрос SSL, как часть протокола SSL, curl представляет список шифров на хост-сервер. Затем сервер выбирает, какой протокол шифрования использовать, на основе списка, представленного curl.
Хост, к которому вы пытаетесь подключиться, поддерживает эти наборы шифров
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x6b)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x67)
TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33)
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d)
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c)
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d)
TLS_RSA_WITH_AES_256_CBC_SHA (0x35)
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c)
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)
Поскольку ваш openSSL был выпущен в июле 2008 года, а TLSv1.2 был выпущен в следующем месяце, августе 2008 года, лучше всего использовать TLSv1.1.
ВОЗМОЖНО ВРЕМЕННОЕ ИСПРАВЛЕНИЕ, пока вы не обновите
У меня нет высокого уровня уверенности, это будет работать для вас
Вы должны проверить SSL своего собственного сервера с чем-то вроде этого SSL SERVER TEST
Если ваш сервер поддерживает TLS1.1, вы можете попробовать следующее. Я не могу проверить это, потому что у меня нет той же версии curl, что и у вас на старом сервере с вашей версией openSSL.
Используйте параметр curl, CURLOPT_SSL_CIPHER_LIST, чтобы запретить хост-серверу использовать что-либо кроме TLS 1.1
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
curl_setopt($ch, CURL_SSLVERSION_TLSv1_1);
Если нет, то попробуйте:
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'DEFAULT');
curl_setopt($ch, CURL_SSLVERSION_TLSv1_1);
НИЖНЯЯ ЛИНИЯ
По нескольким причинам, чем эта проблема, вам необходимо обновить ваш openSSL.
-------------------------------------------------------------------------
-
ПРЕДЫДУЩАЯ УСТРАНЕНИЕ НЕИСПРАВНОСТЕЙ НИЖЕ
Первое, что я делаю, это отключаю JavaScript в браузере. Если я могу получить страницу с помощью браузера без JavaScript, я ЗНАЮ, что могу получить ее с помощью PHP.
Я строю запрос, чтобы он выглядел точно так же, как в браузере. Я перехожу на вкладку "Сеть" Инспектора и редактирую заголовок запроса и копирую его, вставляя в мой код.
$request = array();
$request[] = 'Host: example.com';
$request[] = 'Connection: keep-alive';
$request[] = 'Pragma: no-cache';
$request[] = 'Cache-Control: no-cache';
$request[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
$request[] = 'User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36';
$request[] = 'DNT: 1';
$request[] = 'Origin: https://example.com';
$request[] = 'Referer: https://example.com/entry/login';
$request[] = 'Accept-Encoding: gzip, deflate';
$request[] = 'Accept-Language: en-US,en;q=0.8';
Инициализировать локон
$url = 'https://example.com/entry/login';
$ch = curl_init($url);
Добавьте параметры запроса
curl_setopt($ch, CURLOPT_HTTPHEADER, $request);
Скажите curl, чтобы включить заголовки
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_HEADER, true);
Вернуть ответ
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
Следуйте за редиректами Перенаправления могут быть ловушкой. Возможно, вам не придется следить и анализировать ответ. Часто перенаправления существуют, чтобы установить куки.
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_COOKIESESSION , true );
Пусть завиток справится со сжатием
curl_setopt($ch, CURLOPT_ENCODING,"");
Установить параметры тайм-аута
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_FAILONERROR,true);
Сделайте запрос и получите ответ
Далее вы получите все, что вам нужно знать о запросах. $ Info также будет иметь все заголовки перенаправления. Если перенаправления были сделаны, у $ responseHeader будут все заголовки ответа.
ОБНОВЛЕНИЕ: новый полностью протестированный код
Это может не иметь значения, потому что это также работает на моей машине:
echo file_get_contents($url);
Если curl не удается, этот код должен дать вам причину, почему он не прошел.
Измените URL. Этот принадлежит клиенту.
<?php
header('content-type: text/plain');
$url = 'https://amxemr.com';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING,"");
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_FAILONERROR,true);
curl_setopt($ch, CURLOPT_ENCODING,"");
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$data = curl_exec($ch);
if (curl_errno($ch)){
echo 'Retreive Base Page Error: ' . curl_error($ch);
}
else {
$info = rawurldecode(var_export(curl_getinfo($ch),true));
// Get the cookies:
$skip = intval(curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$responseHeader= substr($data,0,$skip);
$data= substr($data,$skip);
echo "HEADER: $responseHeader\n";
echo "\n\nINFO: $info\n\nDATA: $data";
}
?>
Если вышеописанное не сработало, запустите phpinfo()
<?php
phpinfo();
?>
Там должен быть раздел curl и openSSL.
--------------------------------------------------------------------
ОБНОВЛЕНИЕ ВТОРОЕ
Хорошие новости
Я знаю проблему и смог повторить ошибки, которые вы получили.
Retreive Base Page Error:
Unknown SSL protocol error in connection to www.xxxx.com:443
ПРИМЕЧАНИЕ xxx был сайтом по ссылке, которую вы мне дали, вы можете удалить это сообщение сейчас.
Забавно, у меня один сервер я не обновляю. И, к счастью, у него была та же версия openSSL с июля 2008 года.
Вам нужно обновить ваш openSSL. Также file_get_contents() не удалось на этом сервере тоже. Он работал над версией openSSL в феврале 2013 года, а также с июня 2014 года.
Я не могу сказать, нужно ли обновлять что-либо еще, например, функции, которые используют openSSL, могут (или не могут) обновляться.
Я иду с пословицей, если она не сломана, не чините ее. Я действительно считаю, что некоторые обновления на самом деле являются более низкими. Я все еще на XP. Но он сломался, и вам нужно это исправить.
По крайней мере, это не выстрел в темноте. Я уверен, что вы должны обновить. Это была методическая процедура устранения неполадок, которая смогла дублировать ваши ошибки. Вы можете вернуться к использованию file_get_contents()
тоже.
Использование curl
с curl
Вы можете легко ввести любую страницу поверх https
,
обратите внимание на следующие строки:
curl_setopt($ch, CURLOPT_SSLVERSION, 4);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
вот рабочий код, проверенный на twitter
а также facebook
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
//ini_set('display_errors',1);
//$crawled = [];
set_time_limit(0);// to infinity for example
ob_start();
$output;
function grabAll($url){
$ch = curl_init();
// 2. set the options, including the url
curl_setopt($ch, CURLOPT_URL,$url);
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// curl_setopt($ch, CURLOPT_HEADER, 0);
//curl_setopt ($ch, CURLOPT_CAINFO, "ca-cert/cacert.pem");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, 4);
//curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, '1L');
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//curl_setopt($ch, CURLOPT_TIMEOUT, 400);
//curl_setopt ($ch, CURLOPT_POST, 1);
// 3. execute and fetch the resulting HTML output
//curl_exec($ch);
$output = curl_exec($ch);
ob_flush();//Flush the data here
if ($output === FALSE) {
echo "cURL Error: " . curl_error($ch);
}
$info = curl_getinfo($ch);
//echo 'Took ' . $info['total_time'] . ' seconds for url ' . $info['url'];
// 4. free up the curl handle
curl_close($ch);
//print_r($crawled);
//return $output ;
echo $output;
}
grabAll('https://twitter.com/?lang=en');
ОБНОВЛЕНИЕ 1: используйте этот код для сохранения файла
function grab_image($url,$saveto){
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSLVERSION, 4);
curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
$raw=curl_exec($ch);
curl_close ($ch);
if(file_exists($saveto)){
unlink($saveto);
}
$fp = fopen($saveto,'x');
fwrite($fp, $raw);
fclose($fp);
}
grab_image('i.imgur.com/85wsoLI.jpg','download/');
надеюсь, это решило вашу проблему!!
вот демо на моем сервере: http://54.167.121.86/curl/curl.php
Если по nothing
, вы имеете в виду пустое тело ответа, это не похоже на проблему httpS. в противном случае curl_exec будет жаловаться, curl_exec() вернет bool(false), а curl_error() укажет на проблему SSL.
How can I debug and fix this?
исследуйте запрос, отправленный вашим браузером, когда вы получите действительный ответ (для этого используйте инструменты разработчика вашего браузера. Например, на вкладке "Сеть" в Ctrl+shift+i в Google Chrome), затем сравните его с запросом, отправленным curl, когда вы получаете неверный ответ (для этого используйте CURLOPT_VERBOSE) и 1 на 1 добавляете все заголовки, которые посылает браузер,
например, вы заметите, что libcurl отправляет нет user-agent
заголовок, в то время как ваш браузер отправляет что-то вроде user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
так что добавьте этот заголовок. вы также заметите, что libcurl по умолчанию отправляет Accept: */*
пока ваш браузер отправляет Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
- так что исправь, заставь curl отправлять одинаковые заголовки.
продолжайте делать это, пока 2 запроса не станут неразличимыми, и по пути вы обнаружите разницу, которая делает curl заблокированным.
моя ставка на заголовок агента пользователя.
Поскольку это похоже на проблему с версией SSL, вы можете настроить CURL на ее игнорирование, используя CURLOPT_SSL_VERIFYPEER.
Вот скрипт, работающий с размещенным вами URL
$url = 'https://XXX/YYY/view-all';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
print_r($response);
Иногда это помогает не проверять сертификат и хост, а просто доверять криптографии в SSL.
$context = stream_context_create(
array('http' => array(
'follow_location' => true
),
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false
)
)
);
$content = @file_get_contents($file, FALSE, $context);
Есть ли у сайта HTTPS самозаверяющий сертификат? Можете ли вы предоставить доменные имена для некоторых сайтов, которые работают, а некоторые - нет?
Вы пытались использовать "allow_self_signed" => true
в настройках контекста потока?
Итак, это выглядит так:
$arrContextOptions=array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
"allow_self_signed"=>true,
),
);
$response = file_get_contents($url, false, stream_context_create($arrContextOptions));