Второй классической уязвимостью веб-приложений являются SQL-инъекции (англ. SQL injection), когда пользователь имеет возможность поменять смысл запроса к базе данных веб-сервера. Запрос делается в виде текстовой строки на скриптовом языке SQL. Например, выражение
"SELECT * FROM Users WHERE Name = '" + username + "';"
предназначено для получения информации о пользователе, имя (логин) которого задан переменной username. Однако если пользователь вместо имени введёт строку вида
john'; DELETE * FROM Users; SELECT * FROM Users WHERE Name = 'john,
то выражение превратится в три SQL-операции:
-- запрос о пользователе john SELECT * FROM Users WHERE Name = 'john'; -- удаление всех пользователей DELETE FROM Users; -- запрос о пользователе john SELECT * FROM Users WHERE Name = 'john';
При выполнении этого SQL-запроса к базе данных все записи пользователей будут удалены.
Уязвимости в SQL-выражениях являются частными случаями уязвимостей, связанных с использованием сложных систем с разными языками управления данными и, следовательно, с разными системами экранирования специальных символов и контроля над типом данных. Когда веб-сервер принимает от клиента данные, закодированные обычно с помощью «application/x-www-form-urlencoded» [87], специальные символы (пробелы, неалфавитные символы и т. д.) корректно экранируются браузером и восстанавливаются непосредственно веб-сервером или стандартными программными библиотеками. Аналогично, когда SQL-сервер передаёт данные клиентской библиотеке или принимает их от неё, внутренним протоколом общения с SQL-сервером происходит кодировка текста, который является частью пользовательских данных. Однако на стыке контекстов – в тот момент, когда программа, выполняющаяся на веб-сервере, уже приняла данные от пользователя по HTTP-протоколу и собирается передать их SQL-серверу в качестве составной части SQL-команды – перед программистом стоит сложная задача учёта в худшем случае трёх контекстов и кодировок: входного контекста протокола общения с клиентом (HTTP), контекста языка программирования (с соответствующим оформлением и экранированием специальных символов в текстовых константах) и контекста языка управления данными SQL-сервера.
Ситуация усложняется тем, что программист может являться специалистом в языке программирования, но может быть не знаком с особенностями языка SQL или, что чаще, конкретным диалектом языка SQL, используемым СУБД.
Метод защиты заключается в разделении кода и данных. Для защиты от приведённых атак на базу данных следует использовать параметрические запросы к базе данных с фиксированным SQL-выражением. Например, в JDBC [4]:
PreparedStatement p = conn.prepareStatement( "SELECT * FROM Users WHERE Name=?"); p.setString(1, username);
Таким образом, задача корректного оформления текстовых данных для передачи на SQL-сервер перекладывается на драйвер общения с СУБД, в котором эта задача обычно решена корректно авторами драйвера, хорошо знающими особенности протокола и языка управления данными сервера.