Фильтрация и проверка данных PHP. Частые ошибки

Материал предназначен в основном для начинающих веб-программистов.

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

Здесь я постараюсь описать как можно подробнее частые ошибки при фильтрации данных в PHP скрипте и дать простые советы как правильно выполнить фильтрацию данных.

В сети много статей по поводу фильтрации данных, но они как правильно не полные и без подробные примеров.

Разбор полетов.

Фильтрация. Ошибка №1

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

$number = $_GET['input_number'];
if (intval($number))
{
... выполняем SQL запрос ...
}


Почему она приведет к SQL инъекции? Дело в том, что пользователь может указать в переменной input_number значение:
1'+UNION+SELECT


В таком случаи проверка будет успешно пройдена, т.к. функция intval получает целочисленное значение переменной, т.е. 1, но в самой переменной $number ничего не изменилось, поэтому весь вредоносный код будет передан в SQL запрос.
Правильная фильтрация:
$number = intval($_GET['input_number']);
if ($number)
{
... выполняем SQL запрос ...
}


Конечно, условие может меняться, например если вам нужно получить только определенный диапазон:
if ($number >= 32 AND $number <= 65)



Если вы используете чекбоксы или мультиселекты с числовыми значениями, выполните такую проверку:
$checkbox_arr = array_map('intval', $_POST['checkbox']);


array_map
Так же встречаю фильтрацию в виде:
$number = htmlspecialchars(intval($_GET['input_number']));

htmlspecialchars
Или:
$number = mysql_escape_string(intval($_GET['input_number']));


mysql_escape_string

Ничего кроме улыбки это не может вызвать :)

Фильтрация. Ошибка №2.

Для стринг-переменных используется такая фильтрация:
$input_text = addslashes($_GET['input_text']);


Функция addslashes экранирует спец. символы, но она не учитывает кодировку БД и возможен обход фильтрации. Не стану копировать текст автора, который описал данную уязвимость и дам просто ссылку Chris Shiflett (перевод можно поискать в рунете).

Используйте функцию mysql_escape_string или mysql_real_escape_string, пример:
$input_text = mysql_escape_string($_GET['input_text']);


Если вы не предполагаете вхождение html тегов, то лучше всего сделать такую фильтрацию:
$input_text = strip_tags($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);

strip_tags — убирает html теги.
htmlspecialchars — преобразует спец. символы в html сущности.
Так вы защитите себя от XSS атаки, помимо SQL инъекции.
Если же вам нужны html теги, но только как для вывода исходного кода, то достаточно использовать:
$input_text = htmlspecialchars($_GET['input_text']);
$input_text = mysql_escape_string($input_text);



Если вам важно, чтобы значение переменной не было пустой, то используйте функцию trim, пример:
$input_text = trim($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);



Фильтрация. Ошибка №3.

Она касается поиска в БД.
Для поиска по числам используйте фильтрацию, описанную в первой ошибке.
Для поиска по тексту используйте фильтрацию, описанную во второй ошибке, но с оговорками.
Для того, чтобы пользователь не смог выполнить логическую ошибку, нужно удалять или экранировать спец. символы SQL.
Пример без доп. обработки строки:
$input_text = htmlspecialchars($_GET['input_text']); // Поиск: "%"
$input_text = mysql_escape_string($input_text);


На выходе у нас получится запрос вида:
... WHERE text_row LIKE '%".$input_text."%' ... // WHERE text_row LIKE '%%%'


Это значительно увеличит нагрузку на базу.
В своём скрипте я использую функцию, которая удаляет нежелательные мне символы из поиска:
function strip_data($text)
{
    $quotes = array ("\x27", "\x22", "\x60", "\t", "\n", "\r", "*", "%", "<", ">", "?", "!" );
    $goodquotes = array ("-", "+", "#" );
    $repquotes = array ("\-", "\+", "\#" );
    $text = trim( strip_tags( $text ) );
    $text = str_replace( $quotes, '', $text );
    $text = str_replace( $goodquotes, $repquotes, $text );
    $text = ereg_replace(" +", " ", $text);
            
    return $text;
}


Конечно, не все из выше перечисленных символов представляют опасность, но в моём случаи они не нужны, поэтому выполняю поиск и замену.
Пример использования фильтрации:
$input_text = strip_data($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);


Также советую сделать ограничение по количеству символов в поиске, хотя бы не меньше 3-х, т.к. если у вас будет большое количество записей в базе, то поиск по 1-2 символам будет значительно увеличивать нагрузку на БД.

Фильтрация. Ошибка №4.

Не фильтруются значения в переменной $_COOKIE. Некоторые думаю, что раз эту переменную нельзя передать через форму, то это гарантия безопасности.
Данную переменную очень легко подделать любым браузером, отредактировав куки сайта.
Например, в одной известной CMS была проверка, используемого шаблона сайта:
if (@is_dir ( MAIN_DIR . '/template/' . $_COOKIE['skin'] )){
	$config['skin'] = $_COOKIE['skin'];
}
$tpl->dir = MAIN_DIR . '/template/' . $config['skin'];


В данном случаи можно подменить значение переменной $_COOKIE['skin'] и вызвать ошибку, в результате которой вы увидите абсолютный путь до папки сайта.
Если вы используете значение куков для сохранения в базу, то используйте одну из выше описанных фильтраций, тоже касается и переменной $_SERVER.

Фильтрация. Ошибка №5.

Включена директива register_globals. Обязательно выключите её, если она включена.
В некоторых ситуациях можно передать значение переменной, которая не должна была передаваться, например, если на сайте есть группы, то группе 2 переменная $group должна быть пустой или равняться 0, но достаточно подделать форму, добавив код:
<input type="text" name="group" value="5" />


В PHP скрипте переменная $group будет равна 5, если в скрипте она не была объявлена со значением по умолчанию.

Фильтрация. Ошибка №6.

Проверяйте загружаемые файлы.
Выполняйте проверку по следующим пунктам:
1. Расширение файла. Желательно запретить загрузку файлов с расширениями: php, php3, php4, php5 и т.п.
2. Загружен ли файл на сервер move_uploaded_file
3. Размер файла

Проверка. Ошибка №1.

Сталкивался со случаями, когда для AJAX запроса (например: повышение репутации) передавалось имя пользователя или его ID (кому повышается репутация), но в самом PHP не было проверки на существование такого пользователя.
Например:
$user_id = intval($_REQUEST['user_id']);
... INSERT INTO REPLOG SET uid = '{$user_id}', plus = '1' ...
... UPDATE Users SET reputation = reputation+1 WHERE user_id = '{$user_id}' ...


Получается мы создаем запись в базе, которая совершенно бесполезна нам.

Проверка. Ошибка №2.

При выполнении различного рода действий (добавление, редактирование, удаление) с данными не забывайте проверять права пользователя на доступ к данной функции и дополнительные возможности (использование html тегов или возможность опубликовать материал без проверки).

Давно исправлял в одном модуле форума подобную ошибку, когда любой пользователь мог отредактировать сообщение администрации.

Проверка. Ошибка №3.

При использовании нескольких php файлов сделайте простую проверку.
В файле index.php (или в любом другом главном файле) напишите такую строчку перед подключением других php файлов:
define ( 'READFILE', true );


В начале других php файлов напишите:
if (! defined ( 'READFILE' ))
{
	exit ( "Error, wrong way to file.<br><a href=\"/\">Go to main</a>." );
}


Так вы ограничите доступ к файлам.

Проверка. Ошибка №4.

Используйте хеши для пользователей. Это поможет предотвратить вызов той или иной функции путём XSS.
Пример составления хеша для пользователей:
$secret_key = md5( strtolower( "http://site.ru/" . $member['name'] . sha1($password) . date( "Ymd" ) ) ); // $secret_key - это наш хеш


Далее во все важные формы подставляйте инпут со значением текущего хеша пользователя:
<input type="hidden" name="secret_key" value="$secret_key" />


Во время выполнения скрипта осуществляйте проверку:
if ($_POST['secret_key'] !== $secret_key)
{
exit ('Error: secret_key!');
}



Проверка. Ошибка №5.

При выводе SQL ошибок сделайте простое ограничение к доступу информации. Например задайте пароль для GET переменной:
if ($_GET['passsql'] == "password")
{
... вывод SQL ошибки ...
}
else
{
... Просто информация об ошибке, без подробностей ...
}


Это позволит скрыть от хакера информацию, которая может ему помочь во взломе сайта.

Проверка. Ошибка №5.
Старайтесь не подключать файлы, получая имена файлов извне.
Например:
if (isset($_GET['file_name']))
{
include $_GET['file_name'] .'.php';
}


Используйте переключатель switch:
switch($_GET['file_name'])
{         
         case 'file_1':
         include 'file_1.php';    
         break;     
         
         default:
         include 'file_0.php';    
         break;
}


В таком случаи вы предотвратите подключение файлов, которые не были вами предусмотрены.

Совет.

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

UPD: Поправил пост. Перенес все советы по поводу функций и переменных, которые были в комментариях.

Добавлено: 19 Ноября 2021 07:27:05 Добавил: Андрей Ковальчук

Обнаружение 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 Добавил: Андрей Ковальчук