Обнаружение SSL-соединения

Задача
Необходимо узнать, использует ли прибывший запрос SSL.

Решение
Проверьте значение переменной $_SERVER['HTTPS']:

if ('on' == $_SERVER['HTTPS']) {
print "The secret ingredient in Coca-Cola is Soylent Green.";
} else {
print "Coca-Cola contains many delicious natural and artificial flavors.";
}

Обсуждение
SSL действует на более низком уровне, чем HTTP. Веб-сервер и броузер обмениваются информацией через соответствующее безопасное соединение, основанное на их возможностях, и сообщения HTTP передаются через это безопасное соединение. Для взломщика, перехватывающего трафик, они представляют просто поток бессмысленных байтов, которые невозможно прочитать.

Разные веб-серверы предъявляют различные требования к использованию SSL, поэтому посмотрите документацию к вашему веб-серверу для уточнения деталей. Чтобы работать с SSL, в PHP ничего менять не нужно.

Кроме изменения программы на основании значения переменной $_SERVER['HTTPS'], можно также потребовать, чтобы и значения cookies изменялись только через SSL-соединение. Если последний аргумент функции setcookie() равен 1, то броузер будет передавать cookie обратно на сервер только через безопасное соединение:

/* устанавливаем SSL-только cookie с именем "sslonly" в значение "yes",
которое теряет силу по окончании текущей сессии броузера */
setcookie('sslonly','yes','','/','sklar.com',1);


Хотя броузер посылает эти cookies обратно на сервер только через SSL-соединение, сам сервер посылает их броузеру (когда вы вызываете функцию setcookie() на вашей странице) независимо от того, использует SSL-соединение или нет запрос страницы, которая устанавливает cookie. Если вы помещаете секретные данные в cookie, проверьте, что
вы устанавливаете cookie только с помощью SSL-запроса. Не забудьте также, что информация в cookie расшифровывается на компьютере пользователя.

Добавлено: 22 Июля 2018 14:01:50 Добавил: Андрей Ковальчук

Совместное использование зашифрованных данных с другим веб-сайтом

Задача
Необходимо организовать безопасный обмен данными с другим веб-сайтом.

Решение
Если другой веб-сайт получает информацию с вашего веб-сайта, то поместите эти данные на страницу, защищенную паролем. Можно также предоставлять информацию в зашифрованном виде, с паролем или без него. Если необходимо доставить данные на другой веб-сервер, то передавайте потенциально шифруемые данные с помощью метода POST на защищенный паролем URL.

Обсуждение
Следующая страница требует имя пользователя и пароль, а затем кодирует и показывает содержимое файла, содержащего информацию о вчерашней активности учетной записи:

$user = 'bank';
$password = 'fas8uj3';
if (! (($_SERVER['PHP_AUTH_USER'] == $user) &&
($_SERVER['PHP_AUTH_PW'] == $password))) {
header('WWW-Authenticate: Basic realm="Secure Transfer"');
header('HTTP/1.0 401 Unauthorized');
echo "You must supply a valid username and password for access.";
exit;
}
header('Content-type: text/plain');
$filename = strftime('/usr/local/account-activity.%Y-%m-%d',time() - 86400);
$data = join('',file($filename));
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
$key = "There are many ways to butter your toast.";
// шифруем данные
$iv = $iv = mcrypt_create_iv(mcrypt_get_iv_size($alg,$mode),
MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt($alg, $key, $data, $mode, $iv);
print base64_encode($iv.$ciphertext);


Ниже приведен соответствующий фрагмент кода для получения зашифрованной страницы и расшифровки информации:

$user = 'bank';
$password = 'fas8uj3';
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
$key = "There are many ways to butter your toast.";
$fh = fopen("http://$user:$password@bank.example.com/accounts.php",'r')
or die($php_errormsg);
$data = '';
while (! feof($fh)) { $data .= fgets($fh,1048576); }
fclose($fh) or die($php_errormsg);
$binary_data = base64_decode($data);
$iv_size = mcrypt_get_iv_size($alg,$mode);
$iv = substr($binary_data,0,$iv_size);
$ciphertext = substr($binary_data,$iv_size,strlen($binary_data));
print mcrypt_decrypt($alg,$key,$ciphertext,$mode,$iv);


Запрашивающая программа выполняет те же шаги, что и программа шифрования, но в обратном порядке. Она извлекает зашифрованные данные, закодированные алгоритмом кодирования Base64, передавая имя пользователя и пароль. Затем она декодирует данные с помощью алгоритма Base64 и выделяет вектор инициализации. Наконец, она расшифровывает данные и печатает их.

В предыдущем примере имя пользователя и пароль посылается по сети открытым текстом, пока не будет установлено SSL-соединение. Однако если вы используете SSL, то, возможно, нет необходимости шифровать содержимое файла. Мы включили в этот пример и запрос пароля и шифрование файла, чтобы показать, как это можно сделать. Но есть одна ситуация, когда полезна и парольная защита и шифрование файла: если файл не расшифровывается автоматически при получении. Автоматическая программа может получить зашифрованный файл и сохранить его в зашифрованном виде для последующей обработки. Таким образом, ключ для дешифрования запрашивающей программе не требуется.

Добавлено: 22 Июля 2018 13:54:25 Добавил: Андрей Ковальчук

Хранение зашифрованных данных в файле или базе данных

Задача
Требуется сохранить зашифрованные данные, которые в дальнейшем будут затребованы и расшифрованы вашим веб-сервером.

Решение
Вместе с зашифрованными данными сохраните дополнительную информацию, необходимую для расшифровки (такую как алгоритм, режим кодировки и вектор инициализации), но не ключ:

// шифруем данные $alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
$iv = mcrypt_create_iv(mcrypt_get_iv_size($alg,$mode),MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt($alg,$_REQUEST['key'],
$_REQUEST['data'],$mode,$iv);
// записываем зашифрованные данные
$dbh->query('INSERT INTO noc_list (algorithm,mode,iv,data)
values (?,?,?,?)',
array($alg,$mode,$iv,$ciphertext));


Для расшифровки данных получите от пользователя ключ и примените его к сохраненным данным:

$row = $dbh->getRow('SELECT * FROM noc_list WHERE id = 27');
$plaintext = mcrypt_decrypt($row->algorithm,$_REQUEST['key'],$row->data,
$row->mode,$row->iv);


Обсуждение
Программа save-crypt.php, показанная в примере 14.2, записывает зашифрованные данные в файл.

Пример 14.2. save-crypt.php
function show_form() {
print<<<_FORM_
<form method="post" action="$_SERVER[PHP_SELF]">
<textarea name="data" rows="10" cols="40">
Enter data to be encrypted here.
</textarea>


Encryption Key: <input type="text" name="key">
<input name="submit" type="submit" value="save">
</form>
_FORM_;
}
function save_form() {
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
// шифруем данные
$iv = mcrypt_create_iv(mcrypt_get_iv_size($alg,$mode),MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt($alg, $_REQUEST['key'],
$_REQUEST['data'], $mode, $iv);
// записываем зашифрованные данные
$filename = tempnam('/tmp','enc') or die($php_errormsg);
$fh = fopen($filename,'w') or die($php_errormsg);
if (false === fwrite($fh,$iv.$ciphertext)) {
fclose($fh);
die($php_errormsg);}
fclose($fh) or die($php_errormsg);
Return $filename;
}
if ($_REQUEST['submit']) {
$file = save_form();
print "Encrypted data saved to file: $file";
} else {
show_form();
}


Пример 14.3 показывает соответствующую программу, get-crypt.php, которая принимает имя файла и ключ и выдает расшифрованные данные.

Пример 14.3. get-crypt.php
function show_form() {
print<<<_FORM_
<form method="post" action="$_SERVER[PHP_SELF]">
Encrypted File: <input type="text" name="file">


Encryption Key: <input type="text" name="key">
<input name="submit" type="submit" value="display">
</form>
_FORM_;
}
function display() {
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
$fh = fopen($_REQUEST['file'],'r') or die($php_errormsg);
$iv = fread($fh,mcrypt_get_iv_size($alg,$mode));
$ciphertext = fread($fh,filesize($_REQUEST['file']));
fclose($fh);
$plaintext = mcrypt_decrypt($alg,$_REQUEST['key'],$ciphertext,$mode,$iv);
print "<pre>$plaintext</pre>";
}
if ($_REQUEST['submit']) {
display();
} else {
show_form();
}

Эти две программы имеют свои собственные алгоритмы шифрования и режимы, жестко в них зашитые, поэтому нет необходимости сохранять эту информацию в файле. Файл состоит из вектора инициализации, за которым сразу следуют шифрованные данные. Специальный разделитель после вектора инициализации (IV) не требуется, поскольку функция mcrypt_get_iv_size() возвращает ровно столько байт, сколько требуется программе шифрования, чтобы прочитать весь вектор IV. Все, что находится после вектора, является зашифрованными данными.

Метод шифрования файла, реализованный в данном рецепте, предлагает защиту на случай, если атакующий получает доступ к серверу, на котором хранится файл. Без соответствующего ключа или огромных вычислительных затрат атакующий не сможет прочитать файл. Однако безопасность, которую обеспечивает этот зашифрованный файл, находится под угрозой, если данные, которые надо зашифровать, и ключ шифрования передаются между вашим сервером и броузерами пользователей в открытом виде. Любой, кто перехватывает или отслеживает сетевой трафик, получит возможность увидеть данные до того, как они будут зашифрованы. Чтобы предотвратить такой способ перехвата сообщений, следует использовать SSL.

Дополнительный риск на время шифрования информации вашим вебсервером зависит от того, насколько легко просмотреть эти данные до того, как они будут зашифрованы и записаны в файл. Любой, кто получит доступ к серверу с правами root или администратора, может просмотреть память, используемую процессом сервера, и увидеть незашифрованные данные и ключ. Если операционная система использует файл подкачки, записывая образ памяти серверного процесса на диск, то к незашифрованным данным можно также получить доступ через своп-файл. Атаку такого типа трудно отразить, и она может быть опустошительной. После того как информация записана в файл, ее не сможет прочитать даже взломщик с правами root к веб-серверу, но если атакующий доберется до информации раньше, чем она будет записана в файл, то шифрование будет слабой защитой.

Добавлено: 22 Июля 2018 13:53:46 Добавил: Андрей Ковальчук

Шифрование и дешифрование данных

Задача
Необходимо зашифровать и расшифровать информацию с помощью одного из множества популярных алгоритмов.

Решение
Используйте расширение PHP mcrypt:

$key = 'That golden key that opens the palace of eternity.';
$data = 'The chicken escapes at dawn. Send help with Mr. Blue.';
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
$iv = mcrypt_create_iv(mcrypt_get_iv_size($alg,$mode),MCRYPT_DEV_URANDOM);
$encrypted_data = mcrypt_encrypt($alg, $key, $data, $mode, $iv);
$plain_text = base64_encode($encrypted_data);
print $plain_text."\n";
$decoded = mcrypt_decrypt($alg,$key,base64_decode($plain_text),$mode,$iv);
print $decoded."\n";
NNB9WnuCYjyd3Y7vUh7XDfWFCWnQY0BsMehHNmBHbGOdJ3cM+yghABb/ XyrJ+w3xz9tms74/a70=
The chicken escapes at dawn. Send help with Mr. Blue.

Обсуждение
Расширение mcrypt представляет собой интерфейс библиотеки mcrypt, включающей множество различных алгоритмов шифрования. Данные шифруются и расшифровываются с помощью функций mcrypt_encrypt() и mcrypt_decrypt() соответственно. Каждая их них принимает пять аргументов. Первый аргумент представляет применяемый алгоритм.
Чтобы определить, какие алгоритмы поддерживает библиотека mcrypt в вашей системе, вызовите функцию mcrypt_list_algorithms(). Полный список алгоритмов библиотеки mcrypt представлен в табл. 14.1. Второй аргумент – это ключ шифрования, а третий – данные для шифрования или расшифровки. Четвертый аргумент устанавливает режим шифрования или расшифровки (список режимов возвращает функция mcrypt_list_modes()). Пятый аргумент – это вектор инициализации (IV), используемый некоторыми режимами в качестве составляющей процесса шифрования или дешифрования.За исключением шифруемой и дешифруемой информации, все остальные аргументы должны быть одинаковыми как при шифровании, так и при расшифровке. При использовании режима, требующего вектор
инициализации, можно передать этот вектор в открытом виде вместе с зашифрованным текстом.

Различные режимы соответствуют разным обстоятельствам. Режим цепочки кодовых блоков (Cipher Block Chaining – CBC) кодирует данные по блокам и использует зашифрованное значение каждого блока (как и ключа) для вычисления зашифрованного значения следующего блока. Вектор инициализации оказывает влияние на первый блок. Режимы обратной кодовой связи (Cipher Feedback – CFB) и выходной обратной связи (Output Feedback – OFB) также используют вектор инициализации, но они шифруют данные частями меньшей длины, чем размер блока. Обратите внимание, что режим OFB небезопасен, если
шифровать данные порциями, меньшими, чем блок. Режим электронной кодовой книги (Electronic Code Book – ECB) шифрует данные блоками, не зависящими друг от друга. Кроме того, режим ECB не использует векторы инициализации. Он также менее безопасен, чем дру-гие методы, если его применять регулярно, поскольку для одного и того же исходного текста с данным ключом он всегда генерирует тот же самый зашифрованный текст.Первый аргумент функции mcrypt_create_iv() – длина вектора, а второй – это источник случайности. Имеется три варианта источника случайности: MCRYPT_DEV_RANDOM читает из псевдоустройства /dev/random, MCRYPT_DEV_URANDOM читает из псевдоустройства /dev/urandom, а MCRYPT_RAND использует встроенный генератор случайных чисел. Не все операционные системы поддерживают псевдоустройства случайных генераторов. Не забудьте вызвать функцию srand() перед использованием MCRYPT_RAND, для того чтобы получить неповторяющуюся последовательность случайных чисел.

Код и примеры этого рецепта совместимы с mcrypt 2.4. Интерфейс mcrypt в PHP поддерживает библиотеки mcrypt 2.2 и mcrypt 2.4, но между ними есть некоторые различия. В случае библиотеки mcrypt 2.2 PHP поддерживает только следующие mcrypt-функции: mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb(), mcrypt_ofb(), mcrypt_get_key_size(), mcrypt_get_block_size(), mcrypt_get_cipher_name() и mcrypt_create_iv(). Чтобы зашифровать и расшифровать данные с помощью библиотеки mcrypt 2.2,вызовите соответствующую функцию mcrypt_MODE() на базе выбранного вами режима, и передайте ей аргумент, определяющий шифрование или дешифрование. Следующий фрагмент представляет собой версию программы из раздела «Решение», совместимую с mcrypt 2.2:

$key = 'That golden key that opes the palace of eternity.';
$data = 'The chicken escapes at dawn. Send help with Mr. Blue.';
$alg = MCRYPT_BLOWFISH;
$iv = mcrypt_create_iv(mcrypt_get_block_size($alg),MCRYPT_DEV_URANDOM);
$encrypted_data = mcrypt_cbc($alg,$key,$data,MCRYPT_ENCRYPT);
$plain_text = base64_encode($encrypted_data);
print $plain_text."\n";
$decoded = mcrypt_cbc($alg,$key,base64_decode($plain_text),MCRYPT_DECRYPT);
print $decoded."\n";

Добавлено: 22 Июля 2018 13:52:58 Добавил: Андрей Ковальчук

Хранение паролей

Задача
Необходимо сохранять пароли пользователей, чтобы они могли заходить на ваш веб-сайт.

Решение
Когда пользователь регистрируется, зашифруйте выбранный им пароль с помощью функции crypt() и сохраните зашифрованный пароль в базе данных пользователей:

// шифруем пароль
$encrypted_password = crypt($_REQUEST['password']);
// заносим переменную $encrypted_password в базу данных пользователей
$dbh->query('INSERT INTO users (username,password) VALUES (?,?)',
array($_REQUEST['username'],$encrypted_password));


Затем, когда пользователь попытается войти на ваш веб-сайт, зашифруйте представленный им пароль с помощью функции crypt() и сравните его с сохраненным и зашифрованным ранее паролем. Если два за-шифрованных значения совпадают, значит, пользователь предоставил верный пароль:

$encrypted_password =
$dbh->getOne('SELECT password FROM users WHERE username = ?',
array($_REQUEST['username']));
if (crypt($_REQUEST['password'],$encrypted_password) == $encrypted_password) {
// успешный вход
} else {
// неудачный вход
}


Обсуждение
Хранение паролей в зашифрованном виде предотвращает раскрытие пользовательских учетных записей, если кто-нибудь получит несанкционированный доступ к имени пользователя и паролю в вашей базе данных. (Хотя такие неавторизованные доступы могут предвещать и другие проблемы безопасности.)

После первого шифрования пароля функция crypt() предоставляет два случайным образом сгенерированных базовых символа, которые помещаются в начало зашифрованного пароля. Передача функции crypt() переменной $encrypted_password при проверке предоставленного пользователем пароля заставляет функцию crypt() снова использовать те
же самые символы для базиса шифрования. Случайный базис уменьшает уязвимость к словарным атакам, в которых сравниваются зашифрованные пароли с зашифрованными значениями общеупотребительных слов. Тем не менее имеет смысл помешать пользователям выбирать в качестве паролей простые слова или другие легкие для взлома комбинации символов. Рецепт 14.5 предоставляет функцию для фильтрования легко угадываемых паролей.

Функция crypt() использует односторонний алгоритм шифрования. Это означает, что в настоящее время невозможно (или, по крайней мере, непомерно дорого с вычислительной точки зрения) превратить сгенерированный функцией crypt() зашифрованный текст обратно в простой текст. Это делает сохраненные пароли более защищенными, но это также означает, что вам не удастся получить пользовательские пароли в легко читаемом виде, даже если это будет необходимо. Поэтому, если пользователь, к примеру, забыл свой пароль, вы не сможете ему сообщить этот же самый пароль. Лучшее, что вы сможете сделать, – это установить новое значение пароля и сообщить пользователю его новый па-
роль.

Добавлено: 22 Июля 2018 13:49:39 Добавил: Андрей Ковальчук

Не храните пароли на своем сайте

Задача
Пароль может быть необходим, например для соединения с базой данных. А хранить пароль в используемых для этого файлах PHP вы не хотите, ведь злоумышленник может получить к ним доступ.

Решение
Сохраните пароль в переменной окружения в файле, который сервер загружает при старте, а затем просто сошлитесь на нее в сценарии:

mysql_connect('localhost',$_ENV['MYSQL_USER'],$_ENV['MYSQL_PASSWORD']);


Обсуждение
Хотя эта методика исключает пароли из исходного кода ваших страниц, она делает их доступными из других мест, которые также требуют защиты. Тут очень важно обеспечить отсутствие общедоступных страниц, которые включают в себя вызов функции phpinfo(). Функция phpinfo() показывает переменные окружения, доступные в сценарии, поэтому она покажет и пароли, размещенные в таких переменных.

Далее, особенно если вы работаете в схеме с разделяемым хостингом, установите переменные окружения таким образом, чтобы они были доступны только вашему виртуальному хосту, а не всем пользователям, работающим в разделяемом пространстве хостинга. В случае работы с веб-сервером Apache это можно сделать, установив переменные в отдельном файле, а не в главном файле конфигурации:

SetEnv MYSQL_USER "susannah"
SetEnv MYSQL_PASSWORD "y23a!t@ce8"


Включите этот отдельный файл в директиву <VirtualHost> вашего сайта в главном файле конфигурации следующим образом:

Include "/usr/local/apache/database-passwords"


Убедитесь, что этот отдельный файл, содержащий пароль (т. е. /usr/local/apache/database-passwords), доступен для чтения только пользователю, управляющему соответствующим виртуальным хостом. СамApache обычно запускается и читает файлы конфигурации как root, поэтому он сможет прочитать включенный файл.

Добавлено: 22 Июля 2018 13:47:50 Добавил: Андрей Ковальчук