Курс PHP для начинающих - с нуля до джуниора за 2 месяца. Жми!
base19 7

Безопасность

Курс PHP для начинающих
Внимание! Данный курс устарел!
Переходите к новому курсу "PHP для начинающих".

Во всем мире в аэропортах можно найти лозунг "Security is not a joking matter" (Безопасность — прежде всего). Такой же лозунг каждый системный администратор должен был бы закрепить рядом со своим сервером PHP. А любой, кто подключается к серверу, находящемуся в Интернете, должен принимать надлежащие меры защиты или рисковать потерей данных и даже денег из-за того, что злонамеренные взломщики программного обеспечения сумеют нанести ущерб, пользуясь клавиатурой своего компьютера.

Разработчик сайта, озабоченный проблемами защиты, обязан постоянно повторять: "Не доверяйте сети". Если вы беспокоитесь о защите своего сайта, повторяйте это высказывание, разрабатывая код будущих страниц сайта. Любая информация, передаваемая на сервер по сети (будь то URL, данные из формы HTML или данные, поступающие через какой-то другой сетевой порт), должна рассматриваться как потенциально опасная. В настоящей статье предложено несколько методов, позволяющих обезопасить поступающую информацию. Необходимо не только применять эти методы, но и уделять определенное время для того, чтобы обнаружить другие потенциальные опасности и найти способы их предотвращения.

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

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

Но все эти соображения не должны препятствовать вашим намерениям, например, касающимся вывода своего сайта электронной коммерции в оперативный режим.

Курс PHP для начинающих

Возможные нападения

Подключение сервера к Интернету можно сравнить с открытием магазина на оживленной улице. Вы хотели бы иметь много посетителей, но, не принимая мер предосторожности, можете столкнуться с тем, что вашей беспечностью воспользуются не обычные посетители, а весьма нежелательные гости.

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

Не понимая того, насколько унизительным является звание взломщика программного обеспечения, многие начинающие программисты вступают на эту стезю, прибегая к использованию инструментальных средств и сценариев, которые они находят в веб. Таких начинающих взломщиков называют script-kiddie или по нашему кулхацкерами. Эти люди часто сами почти не понимают, что они делают. Обычно именно такая категория нарушителей стоит за примитивными атаками, такими как компрометация сайта, XSS и SQL-инъекции.

Компрометация сайта и атаки XSS

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

Страница с простой формой добавления комментариев
<?php
	// Предполагается наличие MySQL-базы данных testing с root-доступом, 
	// с таблицей comments, имеющей поля id и text
	$link = mysqli_connect('localhost', 'root', '', 'testing') or die('Нет подключения');

	// Добавление комментария в базу данных
	$comment = (isset($_POST['comment'])) ? $_POST['comment'] : '';
	
	if (!empty($comment)) {
		$query = "INSERT INTO comments(text) VALUES ('".$comment."')";
		mysqli_query($link, $query) or error_log("Значение '$comment' не вставилось в бд");
	}
	
	// Извлечение комментариев из базы данных
	$comments = '';
	$query = "SELECT text FROM comments";
	if ($result = mysqli_query($link, $query)) 
	{
		while($row = mysqli_fetch_array($result)) 
		{
			$comments .= '<li>'.$row['text'].'</li>';
		}
	}
?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Основы PHP</title>
</head>
<body>
<ul>
   <?php echo $comments; ?>
</ul>
<form method="post">
    <textarea placeholder="Введите комментарий" cols="30" rows="10" name="comment"></textarea>
    <br>
    <input type="submit" value="Добавить комментарий">
</form>
</body>
</html>

Эта программа реализует систему комментариев в очень примитивной форме (если вы изучаете мое руководство по PHP с начала, то возможно вы еще не знакомы с операциями работы с базами данных; если это так, то рекомендую вам вернуться к этой статье после ознакомления с соответствующим материалом по MySQL).

Читая этот код, опытный программист начинает чувствовать себя не совсем уверенно (помните — "Не доверяйте сети"). Такая программа принимает данные формы, которые, согласно ожиданиям, должны содержать текст комментария. Этот текст присваивается переменной $comment и сохраняется в базе данных для отображения перед следующими посетителями. Если введенные данные будут такими, какие мы ожидаем, то проблемы не возникнут.

А теперь поставьте себя на мгновение на место кулхацкера и представьте, что произойдет, если во входных данных будут содержаться дескрипторы HTML. Эта простая программа механически вставит такие дескрипторы в формируемую страницу, и эта искаженная страница будет разворачиваться в браузерах других посетителей вместо обычной. Одним из дескрипторов, которые могут оказаться особенно опасными с точки зрения защиты данных, является дескриптор <script>. Взломщик программной защиты может вставить такой комментарий:

Внедрение вредоносного JavaScript-кода

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

Решение подобной проблемы состоит в том, что входные данные следует предварительно обрабатывать в целях обеспечения безопасности. В данном случае необходимо преобразовать в какую-то безопасную форму любые символы, имеющие особый смысл для браузера. К счастью, в языке PHP предусмотрен способ выполнения именно такого преобразования. Функция htmlentities() преобразует символы <, >, " и & в представления этих символов в виде так называемых символьных сущностей HTML (такие как &lt;). В рассматриваемом случае необходимо изменить первую часть программы, для того чтобы в ней использовалась новая функция:

Код PHP
...
$comment = (isset($_POST['comment'])) ? htmlentities($_POST['comment']) : '';
...

Таким образом мы сможем ликвидировать на своем сайте весьма существенную брешь в защите. После добавления вредоносного комментария он просто отобразится на экране, а не будет внедрен в исходную разметку:

Экранирование HTML-символов

SQL-инъекции

Внедрение SQL-кода один из распространённых способов взлома сайтов и программ, работающих с базами данных, основанный на внедрении в запрос произвольного SQL-кода. Чтобы предотвратить внедрение escape-символов в строку, которая будет представлена MySQL, можно воспользоваться функцией mysqli_real_escape_string(). Для того чтобы избавиться от нежелательных слэш-символов, используется функция stripslashes(). Ниже приведен видоизмененный код из предыдущего примера, обеспечивающий защиту от SQL-инъекций:

Код PHP
function cleanSQL($var, $link)
{
		$var = mysqli_real_escape_string($link, $var);
		$var = stripslashes($var);
		$var = htmlentities($var);
		$var = strip_tags($var);
		return $var;
}
	
$comment = (isset($_POST['comment'])) ? cleanSQL($_POST['comment'], $link) : '';

Подключаемые файлы

Вряд ли кто-то захочет «изобретать колесо», имея возможность использовать готовый программный код. Может, это и похоже на плагиат, но в мире программного обеспечения с открытым исходным кодом многократное использование кода в виде подключаемых файлов – благо.

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

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

Чтобы избежать этих проблем, всегда используйте для подключаемых файлов расширение имени .php и никогда не давайте им другие расширения, например .inc, которые не обрабатываются интерпретатором PHP при непосредственном обращении к этим файлам.

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

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

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

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

  • Указание минимально допустимой длины пароля в форме. Заполняя формы регистрации на различных сайтах вы наверняка встречали подсказки, типа "Пароль должен быть не менее 6 символов". Это сделано не просто так, в интернете есть куча онлайн-сервисов, представляющих хеш-коды коротких паролей, которые легко получить, поэтому более длинный пароль является безопаснее. Например, если в пароле можно вводить символы [a-z] и символы дефиса/подчеркивания, то 5-значный пароль представлен 285 = 1.7e7 возможными комбинациями, а 6-значный пароль 286 = 4.8e8.

  • Добавление префиксов и суффиксов для паролей. Например:

    Код PHP
    // Простой пароль, если сохранить его хэш в базе данных,
    // то злоумышленник может легко его идентифицировать
    $password = '123456';
    $insertToDb = md5($password);
    
    // Исходное значение хэша 'myprefix_123456_mysuffix' куда сложнее вычислить
    $suffix = '_mysuffix';
    $prefix = 'myprefix_';
    $insertToDb = md5($prefix.$password.$suffix);
  • Использование отличных от md5 и sha1 алгоритмов шифрования через функции crypt() или hash():

    Код PHP
    $insertToDb = hash('ripemd160', $prefix.$password.$suffix);

Безопасность сеанса

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

Перехват сеанса и фиксация сеанса

Перехват сеанса (session hijacking) – это получение доступа к cookie или идентификатору сеанса клиента, а затем попытка использовать эти данные. Фиксация сеанса (session fixation) – это попытка установить собственный идентификатор сеанса. Перехват и фиксацию сеанса легко отразить. Для обеспечения безопасности мы будем отслеживать IP-адрес клиента и тип используемого им браузера с помощью суперглобальных переменных.

В примере ниже информация шифруется с помощью функции md5, чтобы закрыть эти бреши в системе безопасности:

Проверка на перехват сеанса
session_start();
$user_check = md5($_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']);

if (empty($_SESSION['user_data'])) {
	session_regenerate_id();
	echo ("Открыт новый сеанс, идентификатор пользователя сохранен. ");
	$_SESSION['user_data'] = $user_check;
}
if (strcmp($_SESSION['user_data'], $user_check) !== 0) {
	session_regenerate_id();
	echo ("Предупреждение, дублирующий сеанс!");
	$_SESSION = array();
	$_SESSION['user_data'] = $user_check;
}
else 
{
	echo ("Соединение проверено!");
}

Когда браузер впервые запрашивает страницу из примера, открывается новый сеанс:

Начало сеанса

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

Тот же пользователь обновляет страницу, нарушений в работе нет

Результат на рисунке ниже был получен в результате копирования файла cookie из браузера Firefox в Opera на той же клиентской машине и попытки послать запрос с прежним идентификатором сеанса:

Попытка фиксации сеанса

Так как сценарий проверяет тип браузера, а мы сменили Firefox на Opera, сеанс был создан заново для предотвращения незаконного доступа к секретной информации.

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

Проблемы размещения нескольких сайтов на одном сервере

Если у вас нет собственного выделенного сервера или у вашего сервера много пользователей, может быть небезопасно использовать настройки PHP по умолчанию, отвечающие за хранение данных сеансов во временном каталоге. Обычно у всех пользователей есть доступ к временному каталогу, поэтому им легко похитить данные сеанса, в том числе его идентификатор.

Чтобы надежнее обезопасить данные сеанса, можно изменить путь к каталогу, где должны храниться данные сеанса (значение параметра настройки session.save_path), с помощью функции ini_set, как показано в примере ниже. Вы должны обеспечить сохранение этих данных за пределами корневого каталога с веб-документами:

Код PHP
ini_set('session.save_path', '/home/user/sessions/');
session_start();

Сценарий из примера сохраняет данные сеанса в каталоге /home/user/sessions/. Важно, чтобы каталог имел корректные права доступа (permissions), в противном случае интерпретатор PHP не сможет записать данные сеанса. Обычно это означает, что каталог должен быть доступен для записи группе www-data, но недоступен обычным пользователям для чтения и записи.

Предотвращение несанкционированного доступа к базе данных

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

Чтобы PHP-сценарий предотвратил вывод стандартного сообщения об ошибке, перед вызовами функций обращения к базе данных следует поместить оператор управления выводом сообщений об ошибках – символ (@). В примере ниже выводится более скрытное сообщение об ошибке, за которым следует вызов функции die и завершение работы сценария:

Подавление вывода стандартного сообщения об ошибке
$link = @mysqli_connect('localhost1', 'root', '', 'testing') or die('Нет подключения');

Если бы перед вызовом функции не было оператора (@), появилось бы сообщение об ошибке:

В сообщении указана ошибка

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

Доверие к данным пользователя

Вы уже знаете, что не стоит слепо доверять данным, полученным от пользователя. Но какие данные в действительности являются пользовательскими, а какие – системными, которым можно доверять? Перечислим различные виды пользовательских данных и их назначение:

GET

Данные, полученные методом GET, – пользовательские данные, которые обычно поступают вместе с формой и в виде параметров URL.

POST

Данные, полученные методом POST, – пользовательские данные, которые обычно поступают вместе с формой.

Cookies

Казалось бы, данным в cookies можно доверять, поскольку они передаются автоматически, – но ведь эти данные хранятся на стороне клиента и могут быть изменены, поэтому их всегда следует считать пользовательскими данными.

Данные сеанса

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

Суперглобальный массив $_SERVER[]

В массиве $_SERVER есть элементы, значения которых поставляются браузером клиента. Эти данные приходят со стороны клиента – значит, пользователь мог изменить их, поэтому им тоже нельзя доверять.

Курс PHP для начинающих

Информация к размышлению - защита веб-сайта

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

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

  1. Computer Emergency Response Team (CERT). Сайт CERT — один из самых широко известных архивов официальных описаний инцидентов, связанных с нарушением защиты. На этом сайте публикуются рекомендации по устранению проблем защиты любого типа, которые включают очень четкое описание проблем, систем, подверженных воздействию указанных нарушений, и возможных решений.

  2. Security-focus.com. Здесь приведен большой объем информации обо всех аспектах компьютерной защиты, начиная с правовых и организационных и заканчивая техническими аспектами. Кроме того, на этом сайте базируется известный список рассылки по вопросам защиты, BugTraq (который можно найти в разделе меню Forums).

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

Отладка программ
PHP и JavaScript

Комментарии (0)

Результаты поиска по запросу

Система Orphus