Урок 5 из 8 · 25 мин · обновлено

Уязвимости веб-приложений на примерах: SQL-инъекция, XSS, доступ к чужим данным

Самые частые уязвимости сайтов из списка OWASP Top 10 на примерах: SQL-инъекция, XSS, доступ к чужим данным и хранение паролей — и как каждую из них закрывают.

Список OWASP Top 10

OWASP — международное сообщество специалистов по безопасности приложений. Оно ведёт Top 10 — список самых опасных и частых уязвимостей сайтов. Его используют разработчики, тестировщики и безопасники по всему миру, а полное описание лежит на сайте owasp.org.

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

Ниже — четыре примера на сайте «Чайной лавки». Пример уязвимого кода показан, чтобы понять, как не надо писать, — и сразу исправление.

SQL-инъекция

На странице «Мои заказы» сайт ищет заказы по почте. Разработчик склеил запрос к базе из строки и того, что ввёл пользователь:

Python
query = "SELECT * FROM orders WHERE email = '" + email + "'"

Если ввести обычную почту, всё работает. Но если ввести в поле текст ' OR '1'='1, запрос превратится в такой:

SQL
SELECT * FROM orders WHERE email = '' OR '1'='1'

Условие '1'='1' верно всегда, и база вернёт все заказы всех клиентов. Ввод пользователя стал частью команды — это и есть инъекция.

Как закрыть: использовать параметризованные запросы. Команда и данные передаются базе отдельно, и любой ввод остаётся просто данными:

Python
cursor.execute("SELECT * FROM orders WHERE email = %s", (email,))

XSS: чужой код на странице

Под каждым сортом чая можно оставить отзыв. Если сайт выводит текст отзыва на страницу как есть, кто-то может написать в отзыве не текст, а код:

HTML
<script>alert('XSS')</script>

Этот код выполнится в браузере у каждого, кто откроет страницу. Вместо безобидного окошка злоумышленник может вставить код, который крадёт данные входа или подменяет кнопку оплаты. Такая уязвимость называется XSS (межсайтовый скриптинг).

Как закрыть: экранировать всё, что ввёл пользователь, перед выводом. Тогда браузер покажет символы <script> как обычный текст, а не выполнит их. Современные фреймворки делают это по умолчанию — важно не отключать эту защиту.

Доступ к чужим данным

Клиентка открыла свой заказ по адресу /account/orders/1042. Ради интереса поменяла число на 1043 — и увидела чужой заказ с именем, адресом и телефоном.

Сайт проверял, что клиентка вошла в аккаунт, но не проверял, чей это заказ. Это самая частая уязвимость из списка OWASP — нарушение контроля доступа.

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

Пароли

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

Как закрыть:

  • хранить не пароль, а хеш — результат специального медленного алгоритма (например, bcrypt или Argon2), из которого пароль обратно не получить;
  • ограничивать число попыток входа, чтобы нельзя было подбирать пароли;
  • предлагать второй фактор входа — код в приложении или по СМС.

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

Попробуйте сами. В «Чайной лавке» есть форма «Подписаться на рассылку» с полем для почты, страница поиска по каталогу, которая показывает «Вы искали: …», и ссылка для скачивания счёта вида /invoice?id=555. Для каждой страницы назовите, какую уязвимость стоит проверить в первую очередь и как её закрыть.

Пример ответа
  • Подписка на рассылку: запись почты в базу — проверить на SQL-инъекцию; закрыть параметризованным запросом и проверкой формата почты.
  • «Вы искали: …»: сайт выводит на страницу то, что ввёл пользователь, — проверить на XSS; закрыть экранированием вывода.
  • /invoice?id=555: можно подставить чужой номер — проверить контроль доступа; закрыть проверкой на сервере, что счёт принадлежит вошедшему клиенту.

Мини-тест · 4 вопроса

Проверьте себя

Выберите вариант, который кажется вам правильным. В конце вы увидите свой результат и объяснение к каждому вопросу.

  1. Что лежит в основе большинства веб-уязвимостей?

  2. Как правильно защититься от SQL-инъекции?

  3. Клиент поменял в адресе /orders/1042 на /orders/1043 и увидел чужой заказ. Что это за уязвимость?

  4. Как правильно хранить пароли пользователей?