Статьи

Можете ли вы взломать свой собственный сайт? Взгляд на некоторые важные соображения безопасности

Дважды в месяц мы возвращаемся к любимым постам наших читателей на протяжении всей истории Nettuts +. Этот учебник был впервые опубликован в июле 2008 года.

Первая версия становится золотой! Посетители приземляются со всего мира. Вы знаете, что, вероятно, будет несколько проблем с прорезыванием зубов; Я имею в виду, что это 1.0.0.0 … все эти нули предназначены для того, чтобы дать нам немного изящества, верно?

Возможно, эта непристойная таблица стилей просто не будет элегантно каскадироваться в браузере X. Неполный комментарий отбрасывает некоторую неработающую наценку. Возможно, вам следовало бы сохранить эти подключения к базе данных в конце концов. Эй, мы все упускаем из виду волнение запуска нашей первой версии — но сколько из этих упущений мы можем с радостью перенести, и сколько из них могут просто оставить горький вкус у нас, и еще более болезненно для наших клиентов …

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

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


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

После быстрого разговора с клиентом у нас есть следующая спецификация, которую нужно реализовать, и всего двадцать четыре часа, чтобы сделать это:

Примечание: сервер клиента работает под управлением PHP5 и MySQL, но эти детали не важны для понимания ошибок, описанных в этой статье.


Наш клиент дал нам PHP для включения доступа к базе данных:

Нам не нужен исходный код этого файла, чтобы использовать его. Фактически, если бы клиент просто сказал нам, где он живет, мы могли бы использовать его с оператором include и переменной $db .

В отношении авторизации … в схеме с данными мы имеем дело со следующими именами столбцов:

  • username , varchar (128) — хранится в виде обычного текста.
  • пароль , varchar (128) — хранится в виде простого текста.

Учитывая, что мы работаем не по времени … давайте как можно быстрее напишем функцию PHP, которую мы можем использовать повторно для аутентификации наших пользователей:


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

Почему я выделил не очень опасные переменные $_REQUEST ?

Хотя это не представляет никакой реальной опасности, он допускает слабый подход к клиентскому коду. В PHP есть три массива, которые большинство из нас использует для получения публикуемых данных от пользователей, и чаще всего у нас может возникнуть соблазн использовать $_REQUEST. Этот массив удобно дает нашему PHP доступ к переменным POST и GET, но в этом и заключается потенциальное зависание …

Рассмотрим следующий сценарий. Вы пишете свой код на стороне клиента для использования запросов POST, но вы передаете проект, когда захватываете перерыв — и когда вы возвращаетесь, ваш собеседник записал в проект пару запросов GET. Все работает хорошо, но не должно.

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

$_POST переменным $_POST вместо $_REQUEST , мы исключаем случайную публикацию любого рабочего кода, который может выявить рискованный запрос GET.

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


Снова обращаясь к PHP-коду, у некоторых из вас выскочила красная выделенная линия? Для тех, кто не заметил проблему, я приведу пример и оттуда посмотрим, не кажется ли вам что-то рискованным.

Самая быстрая защита — лишить персонажей корпуса или убежать от них.

Этот образ проясняет недостаток встраивания переменных непосредственно в операторы SQL. Хотя нельзя точно сказать, what именно контролем может обладать злонамеренный пользователь — если вы используете этот метод для объединения операторов SQL, гарантированно, что ваш сервер едва защищен. Приведенный выше пример достаточно опасен для учетной записи только для чтения; Полномочия соединения чтения / записи ограничены только вашим воображением.

Защита от SQL-инъекций на самом деле довольно проста. Давайте сначала посмотрим на случай заключенных в кавычки строковых переменных:

Самое быстрое решение состоит в том, чтобы раздеть символы вложения или избежать их. Начиная с PHP 4.3.0, функция mysql_real_escape_string была доступна для очистки входящих строк. Функция принимает необработанную строку в качестве единственного параметра и возвращает строку с экранированными символами. Однако mysql_real_escape_string не экранирует все символы, которые являются допустимыми управляющими символами в SQL … выделенные элементы на изображении ниже показывают методы, которые я использую для очистки значений String, Number и Boolean .

Первое выделение, строка, которая устанавливает $string_b использует функцию PHP под названием addcslashes . Эта функция была частью PHP начиная с версии 4 и, как написано в приведенном выше примере, является моим предпочтительным методом для обеспечения безопасности и безопасности строк SQL.

addcslashes информация доступна в документации PHP, но я кратко объясню, что делает addcslashes и как она отличается от mysql_real_escape_string .

Из приведенной выше диаграммы видно, что mysql_real_escape_string не добавляет косую mysql_real_escape_string к символу процента (%).

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

Второй параметр я addcslashes в addcslashes , который на изображении выделен жирным шрифтом ; группа символов, для которой PHP будет добавлять косые черты В большинстве случаев он разбивает предоставленную вами строку на символы , а затем обрабатывает каждый из них. Стоит отметить, что эта группа символов также может содержать различные символы, хотя это выходит за рамки данной статьи. В обсуждаемых нами сценариях мы можем использовать буквенно-цифровые символы буквально, например, «abcd1234» и все другие символы, в качестве их литерала в стиле C «\ r \ n \ t» или их индекса ASCII «\ x0A \ x0D \ x09» ,

Следующее выделение делает наши числовые значения безопасными для операторов SQL.

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

Возможно, вы заметили строку 10 и, возможно, задались вопросом, какова ее цель. Несколько лет назад я работал над системой регистрации в колл-центре, которая использовала variable += 0; обеспечить числовые значения. Почему это было сделано, я не могу честно сказать … разве до PHP 4 мы так и сделали ?! Может быть, кто-то читает, может пролить свет на эту тему. Кроме этого, если вы, как и я, встретите такую ​​линию в дикой природе, вы будете знать, что она пытается сделать .

Двигаясь вперед, тогда; строки 11 и 12 — все, что нам нужно, чтобы подготовить наши числовые входные значения для SQL. Я должен сказать, что если бы входная строка $number_i содержала какие-либо нечисловые символы впереди или слева от числовых … наши значения $number_a , $number_b и $number_c бы равны 0 .

Мы будем использовать floatval для очистки наших входных чисел; PHP печатает только десятичные разряды, когда они существуют во входном значении, поэтому печать их в операторе SQL не вызовет никаких ошибок, если во входных данных нет десятичного знака. Пока наш серверный код безопасен, мы можем оставить более привередливую проверку нашего клиентского кода.

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

Подобно эквиваленту C ++, логическое значение в PHP на самом деле является целым числом. Например, True + True = Два. Есть бесчисленное множество способов перевести входную строку в булевский тип, мой личный любимый способ: содержит ли строчная строка слово true?

У всех вас могут быть свои предпочтительные методы; делает ли входная строка явно равной «true» или является входной строкой «1» и так далее … важно то, что входящее значение, каким бы оно ни выглядело, было представлено булевым (или целым) перед тем, как мы его используем ,

Моя личная философия проста: если X true или false , то X является логическим значением. Я блаженно напишу весь код, который мне может понадобиться позже, с булевыми значениями, а не с short, int, tinyint или чем-то, что не является булевым. То, что происходит с металлом, меня не волнует, поэтому то, как оно выглядит для человека, гораздо важнее .

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


Теперь, когда мы защитили наш SQL от инъекций и убедились, что только логин POST может работать с нашим сценарием, мы готовы реализовать нашу функцию отправки обзора.

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

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

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

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

Эти регулярные выражения не будут давать безупречный результат, но в большинстве случаев — они должны выполнять почти элегантную работу.

Давайте посмотрим на регулярное выражение, которое мы будем использовать в нашем PHP. Вы заметите, что два массива были объявлены. $safelist_review и $safelist_comment — так мы можем использовать одни и те же функции для проверки отзывов и последующих комментариев:

… и вот основная функция, которую мы будем вызывать для очистки данных обзора и комментариев:

Входные параметры я выделил красным и синим. $input — это необработанные данные, отправленные пользователем, а $list — ссылка на массив выражений; $safelist_review или $safelist_comment зависимости от того, какой тип представления мы хотим проверить.

Функция возвращает переформатированную версию отправленных данных — любые теги, которые не проходят ни одного из регулярных выражений в нашем выбранном списке, преобразуются в эквиваленты в кодировке HTML. Что в самых простых сроках превращает < и > в &lt; и &gt; другие символы тоже изменены, но ни один из них не представляет угрозы для безопасности нашего клиента или пользователей.

Note: The functions: cleanWhitespace and getTags are included in the article's source files.

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

Этот конкретный недостаток может быть исправлен с помощью нескольких регулярных выражений и / или модификаций с теми, которые мы уже используем. Наше регулярное выражение привязки допускает только значения «/ …», «h …» и «# …» в качестве атрибута href что на самом деле является лишь примером решения. Браузеры понимают огромное количество атрибутов, script visible , таких как , onLoad и так далее.

В сущности, мы создали непростую проблему для себя. Мы хотели разрешить HTML, но теперь у нас есть почти бесконечный список ключевых слов для удаления. Существует, конечно, далеко не идеальный — но довольно быстро написанный способ сделать это:

Подумав, вы будете абсолютно правы, спросив: «Почему мы просто не использовали BBCode или Textile или …?»

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

Тем не менее, эта статья не предназначена для того, чтобы научить нас, как регулярное выражение, как PHP или как написать что-нибудь на одном конкретном языке. Причина этого в том, чтобы просто не оставлять двери приоткрытыми.

Итак, давайте закончим тогда; с кратким обзором того, на что мы смотрели:

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

До тех пор, безопасное кодирование!