Как работать с SSL и TLS в PHP?
SSL и TLS (Secure Sockets Layer и Transport Layer Security) являются протоколами безопасности для защиты передачи данных в Интернете.
Для работы с SSL/TLS в PHP вам сначала необходимо установить SSL/TLS сертификат на ваш сервер. Для этого обратитесь к провайдеру хостинга или к вашему системному администратору.
Затем вам нужно использовать функции PHP, такие как stream_socket_client или curl, которые поддерживают SSL/TLS соединения. Например:
// Использование stream_socket_client $context = stream_context_create(['ssl' => [ 'verify_peer' => true, // проверять сертификат сервера 'verify_peer_name' => true, // проверять имя сервера в сертификате 'cafile' => '/path/to/cert.pem', // путь к корневому сертификату ]]); $socket = stream_socket_client('ssl://example.com:443', $errno, $errstr, ini_get('default_socket_timeout'), STREAM_CLIENT_CONNECT, $context); if (!$socket) < die("Ошибка соединения: $errstr ($errno)"); >fwrite($socket, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"); $response = stream_get_contents($socket); fclose($socket); // Использование curl $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://example.com'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // проверять сертификат сервера curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cert.pem'); // путь к корневому сертификату $response = curl_exec($ch); if (curl_errno($ch)) < die("Ошибка соединения: " . curl_error($ch)); >curl_close($ch);
Обратите внимание на параметры verify_peer и verify_peer_name, которые указывают, проверять ли сертификат сервера и имя сервера в сертификате соответственно. Для проверки корневого сертификата используется параметр cafile.
Также не забудьте закрыть соединение после окончания работы с ним.
ftp_ssl_connect
ftp_ssl_connect() устанавливает явное SSL соединение с FTP-сервером, заданным аргументом hostname . Это означает, что ftp_ssl_connect() будет успешным даже если сервер не настроен для SSL-FTP, или его сертификат недействителен. Только когда будет вызвана функция ftp_login() , клиент пошлёт необходимую команду AUTH FTP, так что в указанных случаях ftp_login() завершится ошибкой.
Замечание: Почему эта функция может быть не определена
До PHP 7.0.0 ftp_ssl_connect() была доступна только если PHP был собран с поддержкой OpenSSL; это означает, что в Windows эта функция не была определена в официальных сборках PHP. Чтобы использовать эту функцию под Windows, вам придётся собрать PHP самостоятельно для включения поддержки OpenSSL.
Замечание:
ftp_ssl_connect() не предназначена для использования по протоколу sFTP. Для использования sFTP из PHP смотрите функцию ssh2_sftp() .
Список параметров
Адрес FTP-сервера. Этот параметр не должен содержать слеши в конце и префикс ftp:// в начале.
Задаёт порт, на котором устанавливается соединение. Если равен нулю или опущен, по умолчанию используется стандартный для протокола FTP — порт 21.
Задаёт время ожидания для всех операций с этим соединением. По умолчанию время ожидания устанавливается в 90 секунд. Получить и установить значение времени ожидания можно также с помощью функций ftp_set_option() и ftp_get_option() .
Возвращаемые значения
Возвращает FTP\Connection в случае успешного выполнения или false в случае возникновения ошибки.
Список изменений
Примеры
Пример #1 Пример использования функции ftp_ssl_connect()
// установка ssl-соединения
$ftp = ftp_ssl_connect ( $ftp_server );
// проверка имени пользователя и пароля
$login_result = ftp_login ( $ftp , $ftp_user_name , $ftp_user_pass );
if (! $login_result ) // В этом случае PHP уже выбросил сообщение уровня E_WARNING
die( «can’t login» );
>
// закрытие ssl-соединения
ftp_close ( $ftp );
?>
Смотрите также
SSL из PHP: socket и cURL
Сегодня, этим сонным летним утром, я расскажу вам про SSL соединение из PHP скрипта. Расскажу исходя не только лишь из теории, а ещё и решая вполне себе практическую задачу — логин на гугловский блогосервис blogger.com.
Начнём с сокетов. В хелпе заявлена возможность использования HTTPS протокола, поэтому пробуем. Набор POST переменных взят из developer’s guide. Хабрапарсер обрамляет мыло ссылкой, поэтому «@» заменена на (at).
$postvars = array(
"Email" => "mail(at)gmail.com",
"Passwd" => "pass",
"service" => "blogger"
);
$postdata = "";
foreach ( $postvars as $key => $value )
$postdata .= "&".rawurlencode($key)." ssl://google.com", 443);
$send = "";
$send .= "POST /accounts/ClientLogin HTTP/1.1\r\n";
$send .= "Host: google.com\r\n";
$send .= "Content-length: ".strlen($postdata)."\r\n";
$send .= "Content-type: text/plain\r\n";
$send .= "Connection: close\r\n";
$send .= "\r\n";
$send .= $postdata."\r\n\r\n";
fputs($fp, $send);
$html = fread($fp, 1000000);
fclose($fp);
echo "".$html."
В функции fsockopen в качестве префикса перед именем сервера используем не https, а ssl. Так прямым текстом написано в хелпе. Дальше всё просто. Формируем HTTP-header, и пихаем его в открытый сокет. Читаем ответ, и получаем
Короче, если опустить часовые мытарства и пляски вокруг функций сокета, у меня ничего не вышло. Ну то есть не вышло передать POST данные, хотя GET запросы возвращаются нормально. Может это связано только с гугловским сервером, а где-то в другом месте получится.
UPD. Огромное спасибо хабраюзеру anabolik, который подсказал что если изменить одну строку заголовка и сделать
$send .= «Content-type: application/x-www-form-urlencoded\r\n»;
то всё сразу заработает. Ещё раз спасибо. Отблагодарил всякими способами =).
Переходим ко второму способу.
Открываем страницу мана про cURL и радуемся. Столько возможностей для запросов, для всякого конфигурирования. Должно получиться. Итак, лезем в curl_setopt. Нам понадобятся
CURLOPT_URL — это URL запроса.
CURLOPT_POST — говорим, что будем посылать POST запрос.
CURLOPT_POSTFIELDS — собственно POST переменыые.
CURLOPT_RETURNTRANSFER — вернуть результат запроса, а не выводить в браузер.
Теперь собственно о SSL параметрах:
CURLOPT_SSL_VERIFYPEER — если поставить его в 0, то удалённый сервер не будет проверять наш сертификат. В противном случае необходимо этот самый сертификат послать.
CURLOPT_CAINFO — указывать файл сертификата, если CURLOPT_SSL_VERIFYPEER установлен в 1.
CURLOPT_SSLVERSION — целое число, указывает версию SSL (2 или 3), обычно определяется автоматически.
CURLOPT_SSL_VERIFYHOST — будет ли производиться проверка имени удалённого сервера, указанного в сертификате. Если установить значение «2», то будет произведена ещё и проверка соответствия имени хоста. (если честно, я так и не понял что делает этот флаг)
Вот и всё. Нам для гугла понадобится только указать, что мы с собой никаких сертификатов не принесли, пустите нас так пожалуйста. Пишем код.
$postvars = array(
«Email» => «mail(at)gmail.com»,
«Passwd» => «pass»,
«service» => «blogger»
);
$postdata = «»;
foreach ( $postvars as $key => $value )
$postdata .= «&».rawurlencode($key).» https://www.google.com/accounts/ClientLogin»);
curl_setopt ($ch, CURLOPT_POST, 1);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec ($ch);
curl_close($ch);
В переменной $result у нас теперь находятся три строки, из которых нужна только одна — последняя, которая начинается с «Auth=». Но про это, наверное, в следующий раз.
Php соединение с ssl
Замечание: Если транспортный протокол не указан, будет использован tcp:// .
- 127.0.0.1
- fe80::1
- www.example.com
- tcp://127.0.0.1
- tcp://fe80::1
- tcp://www.example.com
- udp://www.example.com
- ssl://www.example.com
- sslv2://www.example.com
- sslv3://www.example.com
- tls://www.example.com
Интернет-сокеты требуют указания порта в дополнение к адресу. В случае fsockopen() , порт передаётся вторым параметром и не затрагивает строку адреса. При работе с stream_socket_client() и другими близкими функциями, как и в случае со стандартными URL, порт указывается в конце адреса, отделённый двоеточием.
Замечание: IPv6 численные адреса с указанием порта
Во втором примере выше, в то время как IPv4 и имя хоста не изменились, за исключением добавления номера порта после двоеточия, адрес IPv6 заключён в квадратные скобки: [fe80::1] . Это сделано для того, чтобы отличить двоеточие в адресе от двоеточия при указании порта.
Протоколы ssl:// и tls:// (доступны, только если поддержка openssl включена в PHP) являются расширениями tcp:// , дополняющими его SSL-шифрованием.
ssl:// будет пытаться использовать соединение SSL V2 или SSL V3, в зависимости от возможностей и настроек удалённого хоста. sslv2:// и sslv3:// позволяют явно указать использование SSL V2 или SSL V3.
How to Use SSL Sockets with PHP
SSL sockets are perfect for sending secure data. With certificates, you can verify the identify of the host, the client, or both. Signed certificates cost money but you can create and self-sign a certificate. Check out the code samples below to see how to generate SSL certificates and create SSL clients and servers. Examples include raw socket communication as well as the common HTTPS protocol.
Generating an OpenSSL PEM
$certificateData = array(
"countryName" => "US",
"stateOrProvinceName" => "Texas",
"localityName" => "Houston",
"organizationName" => "DevDungeon.com",
"organizationalUnitName" => "Development",
"commonName" => "DevDungeon",
"emailAddress" => "nanodano@devdungeon.com"
);
// Generate certificate
$privateKey = openssl_pkey_new();
$certificate = openssl_csr_new($certificateData, $privateKey);
$certificate = openssl_csr_sign($certificate, null, $privateKey, 365);
// Generate PEM file
$pem_passphrase = 'abracadabra'; // empty for no passphrase
$pem = array();
openssl_x509_export($certificate, $pem[0]);
openssl_pkey_export($privateKey, $pem[1], $pem_passphrase);
$pem = implode($pem);
// Save PEM file
$pemfile = './server.pem';
file_put_contents($pemfile, $pem);
SSL Client Socket
$socket = stream_socket_client("ssl://192.168.1.2:5522", $errno, $errstr);
if ($socket) echo fread($socket, 2000);
>
$host = '192.168.1.2';
$port = 5522;
$timeout = 30;
$cert = 'e:\www\workspace\php\sockets\server.pem'; // Path to certificate
$context = stream_context_create(
array('ssl'=>array('local_cert'=> $cert))
);
if ($socket = stream_socket_client(
'ssl://'.$host.':'.$port,
$errno,
$errstr,
30,
STREAM_CLIENT_CONNECT,
$context)
) fwrite($socket, "\n");
echo fread($socket,8192);
fclose($socket);
> else echo "ERROR: $errno - $errstr\n";
>
SSL Server Socket
$context = stream_context_create();
// local_cert must be in PEM format
stream_context_set_option($context, 'ssl', 'local_cert', $pemfile);
// Pass Phrase (password) of private key
stream_context_set_option($context, 'ssl', 'passphrase', $pem_passphrase);
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
// Create the server socket
$socket = stream_socket_server(
'ssl://0.0.0.0:9001',
$errno,
$errstr,
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
$context
);
// fwrite/fread to $socket
HTTPS Client
// Get port and IP address
$service_port = getservbyname('www', 'tcp');
$address = gethostbyname('www.google.com');
// Bind socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
>
// Connect
$result = socket_connect($socket, $address, $service_port);
if ($result === false) echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
>
// Create HTTP request
$in = "HEAD / HTTP/1.1\r\n";
$in .= "Host: www.google.com\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';
// Send HTTP request
socket_write($socket, $in, strlen($in));
// Read and output response
while ($out = socket_read($socket, 2048)) echo $out;
>
// Close socket
socket_close($socket);
HTTPS Server
This is just a slight extension of the SSL Server code above to accept and respond as an HTTP server
$context = stream_context_create();
// local_cert must be in PEM format
stream_context_set_option($context, 'ssl', 'local_cert', '/path/to/pem/file');
stream_context_set_option($context, 'ssl', 'passphrase', $pem_passphrase);
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
// Create the server socket
$server = stream_socket_server('ssl://192.168.1.96:9001', $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
while(true)
$buffer = '';
$client = stream_socket_accept($server);
if($client) // Read until double CRLF
while( !preg_match('/\r?\n\r?\n/', $buffer) )
$buffer .= fread($client, 2046);
// Respond to client
fwrite($client, "200 OK HTTP/1.1\r\n"
. "Connection: close\r\n"
. "Content-Type: text/html\r\n"
. "\r\n"
. "Hello World! " . microtime(true)
. "\n ");
fclose($client);
>
>