SQL-инъекции при авторизации

Самой распространенной связкой для работы веб-приложения с базами данных является PHP + MySQL. За годы их существования они обросли огромным количеством мистических предположений.

Для работы с MySQL существует множество плагинов и драйверов. Обычно используют стандартный MySQL (MySQLi с недавних пор). Начнем с него.

Стандартный модуль MySQL с 5 версии признан нерекомендуемым, а уже с 7 версии вовсе перестал поддерживаться. На замену ему пришел MySQLi (MySQL Improved). Потому будем работать с ним. И я надеюсь, что хотя бы начальные знания в описываемых здесь языках у вас присутствуют.

Начнем с авторизации. Каким образом можно проверить, авторизовывать ли пользователя? Запрос может выглядеть, например, так:

SELECT * FROM user WHERE login = $_GET['login'] AND password = $_GET['password']

Это сверхглупый пример, но давайте начнем с него. Во-первых, практически самым худшим, что может сделать злоумышленник в данном случае, это авторизоваться от имени любого пользователя (с любыми привилегиями). Достаточно прописать что-нибудь для того, чтобы запрос нам вернул не 0 строк, и мы уже авторизованы. Например, ставший уже классическим пример:

SELECT * FROM user WHERE login = 123 AND password = 123 OR 1=1

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

Допишите запрос так, чтобы пользователь смог войти в систему (не зная логин и пароль)

SELECT * FROM table WHERE login = " ... " AND password = " ... "

Почему же нельзя, например, сделать вот так (хотя некоторые статьи в интернете говорят обратное):

SELECT * FROM user WHERE login = 123 AND password = 123; DROP DATABASE db_name; --

А все потому, что множественные запросы можно вызывать только в специальных функциях (например, mysqli::multi_query). Естественно, если вам не нужно в одном вызове совершать несколько запросов, то и пользоваться этой функцией не стоит. Поэтому, насчет удаления кем-то вашей базы данных можно не переживать (но бэкапы делать все же стоит).

Теперь пару слов о том, как защититься от подобной атаки. Для этого необходимо использовать приведение типов (в случае с числовыми данными) и/или подготовленные запросы. Желательно всё вместе. А использовать различные экранируемые функции, например addslashes(), практически бессмысленно.