Урок 5 из 8 · 25 мин · обновлено
Уязвимости веб-приложений на примерах: SQL-инъекция, XSS, доступ к чужим данным
Самые частые уязвимости сайтов из списка OWASP Top 10 на примерах: SQL-инъекция, XSS, доступ к чужим данным и хранение паролей — и как каждую из них закрывают.
Список OWASP Top 10
OWASP — международное сообщество специалистов по безопасности приложений. Оно ведёт Top 10 — список самых опасных и частых уязвимостей сайтов. Его используют разработчики, тестировщики и безопасники по всему миру, а полное описание лежит на сайте owasp.org.
Почти все пункты списка сводятся к одному правилу: никогда не доверять данным, которые пришли от пользователя. Всё, что человек вводит в форму, передаёт в адресной строке или присылает в запросе, может быть составлено специально, чтобы обмануть программу.
Ниже — четыре примера на сайте «Чайной лавки». Пример уязвимого кода показан, чтобы понять, как не надо писать, — и сразу исправление.
SQL-инъекция
На странице «Мои заказы» сайт ищет заказы по почте. Разработчик склеил запрос к базе из строки и того, что ввёл пользователь:
query = "SELECT * FROM orders WHERE email = '" + email + "'"
Если ввести обычную почту, всё работает. Но если ввести в поле текст ' OR '1'='1, запрос превратится в такой:
SELECT * FROM orders WHERE email = '' OR '1'='1'
Условие '1'='1' верно всегда, и база вернёт все заказы всех клиентов. Ввод пользователя стал частью команды — это и есть инъекция.
Как закрыть: использовать параметризованные запросы. Команда и данные передаются базе отдельно, и любой ввод остаётся просто данными:
cursor.execute("SELECT * FROM orders WHERE email = %s", (email,))
XSS: чужой код на странице
Под каждым сортом чая можно оставить отзыв. Если сайт выводит текст отзыва на страницу как есть, кто-то может написать в отзыве не текст, а код:
<script>alert('XSS')</script>
Этот код выполнится в браузере у каждого, кто откроет страницу. Вместо безобидного окошка злоумышленник может вставить код, который крадёт данные входа или подменяет кнопку оплаты. Такая уязвимость называется XSS (межсайтовый скриптинг).
Как закрыть: экранировать всё, что ввёл пользователь, перед выводом. Тогда браузер покажет символы <script> как обычный текст, а не выполнит их. Современные фреймворки делают это по умолчанию — важно не отключать эту защиту.
Доступ к чужим данным
Клиентка открыла свой заказ по адресу /account/orders/1042. Ради интереса поменяла число на 1043 — и увидела чужой заказ с именем, адресом и телефоном.
Сайт проверял, что клиентка вошла в аккаунт, но не проверял, чей это заказ. Это самая частая уязвимость из списка OWASP — нарушение контроля доступа.
Как закрыть: на каждый запрос сервер проверяет, имеет ли этот пользователь право на этот объект. Спрятать номер или сделать его длинным — недостаточно.
Пароли
Если пароли клиентов хранятся в базе как есть, любая утечка отдаёт их злоумышленникам. А многие люди используют один пароль везде — значит, под угрозой их почта и банк.
Как закрыть:
- хранить не пароль, а хеш — результат специального медленного алгоритма (например, bcrypt или Argon2), из которого пароль обратно не получить;
- ограничивать число попыток входа, чтобы нельзя было подбирать пароли;
- предлагать второй фактор входа — код в приложении или по СМС.
Проверять сайты на такие уязвимости можно только свои или по договору. Искать их на чужих сайтах «из интереса» — нарушение закона, даже если вы ничего не сломали.
Попробуйте сами. В «Чайной лавке» есть форма «Подписаться на рассылку» с полем для почты, страница поиска по каталогу, которая показывает «Вы искали: …», и ссылка для скачивания счёта вида /invoice?id=555. Для каждой страницы назовите, какую уязвимость стоит проверить в первую очередь и как её закрыть.
Пример ответа
- Подписка на рассылку: запись почты в базу — проверить на SQL-инъекцию; закрыть параметризованным запросом и проверкой формата почты.
- «Вы искали: …»: сайт выводит на страницу то, что ввёл пользователь, — проверить на XSS; закрыть экранированием вывода.
/invoice?id=555: можно подставить чужой номер — проверить контроль доступа; закрыть проверкой на сервере, что счёт принадлежит вошедшему клиенту.
Мини-тест · 4 вопроса
Проверьте себя
Выберите вариант, который кажется вам правильным. В конце вы увидите свой результат и объяснение к каждому вопросу.
-
Что лежит в основе большинства веб-уязвимостей?
Инъекции, XSS и доступ к чужим данным возникают, когда сайт принимает ввод пользователя или номер из адреса «как есть».
-
Как правильно защититься от SQL-инъекции?
В параметризованном запросе база получает команду и данные отдельно, поэтому никакой ввод не станет частью команды.
-
Клиент поменял в адресе /orders/1042 на /orders/1043 и увидел чужой заказ. Что это за уязвимость?
Сервер должен на каждый запрос проверять, принадлежит ли заказ этому пользователю. Скрыть номер в адресе недостаточно.
-
Как правильно хранить пароли пользователей?
Из хеша нельзя получить пароль обратно. Если база утечёт, пароли не попадут к злоумышленникам в открытом виде.