Рецепты программирования на PHP или практические советы по программированию → Защита сайта от атак или немного о межсайтовом скриптинге (XSS)
XSS - межсайтовый скриптинг (Сross Site Sсriрting) — тип уязвимости сата, используется при хакерской атаке.
Специфика подобных атак заключается в том, что плохие мальчики используют уязвимый сайт в качестве средства атаки на клиента. XSS-атака обычно проводится путём конструирования специального URL, который атакующий предъявляет своей жертве, либо вставки в формы специальных html-символов.
Сейчас XSS составляют около 15% всех обнаруженных уязвимостей сайтов.
Если скрипты никак не защищать, то хакеры смогут многое. Например, удалить или подменить файлы на вашем сервере, залезть в ваше базу данных и изменить данные, получить пароли пользователей или использовать ваш сайт для рассылки спама. И многое другое.
100% защиты не существует, но дам несколько практических рекомендаций.
Защита от подстановки URL (метод $_GET)
Все, что скрипт получает в строке URL надо проверять на допустимость.
Прежде всего, необходимо настроить .htaccess - включить проверку допустимых символов в строке url. Если ваш сайт целиком обрабатывает один скрипт (index.php), то можно сделать так:
| 1 | RewriteEngine on |
| 2 | RewriteRule ^([A-z0-9/_-]*)$ index.php [L,QSA] |
В этом случае в URL допускаются только латинские символы, символ "/" как разделитель каталога, нижний прочерк и тире. Все остальные URL вызовут ошибку и выполнение скрипта будет прервано.
Например, ссылка вида http://domain.ru/news/125 - пройдет, а ссылка вида http://domain.ru/news/125APA-script+AD4-alert(document.cookie)+ADsAPA-/script+AD4, которая при определенных обстоятельствах выведет куки сайта, не пройдет.
Если в строке URL предполагается передача параметров (что выглядит менее красиво и наглядно), то надо добавить в регулярное выражение ? и & (не забывая экранировать эти символы).
Теперь займемся самим файлом index.php, который в начале получает строку URL.
Считываем введенный пользователем адрес в массив $auri
| 1 | $auri = explode("/", strtolower($_SERVER['REQUEST_URI'])); |
| 2 | array_shift($auri); |
| 3 | array_shift($auri); |
После удаления двух первых элементов, которые содержали протокол и домен, первый элемент массива теперь содержит название первого каталога.
Например, для ссылки http://domain.ru/news/125 первый элемент будет содержать "news", второй - 125.
Теперь нам надо проверить каждый из элементов на его корректность. Предположим, у вас на сайте 4 основных раздела: news,pics,aboutme,contacts. Первые 2 раздела имеют много подразделов, 2 последних - по одной страничке. Соответственно, допустимыми ссылками будут являться
http://domain.ru/news/... http://domain.ru/pics/... http://domain.ru/aboutme и http://domain.ru/contacts. Под "..." в данном случае подразумеваем либо что-то, либо ничего. Все другие варианты запрещаем. Для этого, определяем навигацию сайта:
| 1 | $anavi = array("news","pics","aboutme","contacts"); |
Теперь легко проверить допустимость каталога:
| 1 | $dir = array_shift($auri); |
| 2 | if(!$dir) echo "Вы попали на главную страницу сайта"; |
| 3 | elseif(in_array($dir,$anavi)) echo "Вы попали в раздел $dir"; |
| 4 | else echo "Такой страницы у нас нет"; |
Далее, если определен следующий уровень каталога, проверяем его корректность. Тут все зависит от структуры ссылок, которую вы задумали сделать у себя на сайте.
Если дальше дожно быть передано число, то проверяем, что это целое положительное число.
| 1 | $item = array_shift($auri); |
| 2 | if(!$item) echo "Это список новостей/картинок"; |
| 3 | elseif($item && (int)$item > 0) echo "Это новость/картинка №".(int)$item; |
| 4 | else echo "Такой страницы у нас нет"; |
Защита от подстановки в формы и при запросах к БД
Все, что скрипт может получить через формы (например, поиск, или форму комментария и пр.) и все, что можно получить из кук, также нуждается в проверке. Верить нельзя ничему.
Давайте попробуем написать простой класс, который займется чисткой получаемых от пользователя значений.
| 1 | function sanitize($val) { |
| 2 | //если ничего не передано, возвращаем "ничего" |
| 3 | if (!$val) return false; |
| 4 | |
| 5 | |
| 6 | if(is_array($val)) foreach($val as $k => &$v) sanitize($v); |
| 7 | |
| 8 | if (is_numeric($val)) { |
| 9 | if (is_int($val)) return (int)$val; |
| 10 | |
| 11 | //обрабатываем float |
| 12 | $val = str_replace(",",".",$val); |
| 13 | return (float) $val; |
| 14 | } |
| 15 | |
| 16 | if (get_magic_quotes_gpc()) $val = stripslashes($val); |
| 17 | $val = strip_tags(trim($val)); |
| 18 | $val = mysqli_real_escape_string($this->conn, $val); |
| 19 | return $val; |
| 20 | } |

15.04.2012 → RedPage написал:
А как будет выглядеть защита от дописывания разной ерунды, например: На сайте чпу, все страницы имеют вид index.php (без переменных) и index-001.html хотелось бы правило которое редиректило бы на морду (любое место) если кто-то сделал запрос вида index-001.html+++ или index-001.html?хлам или index-001.html&хлам