Для защиты от инъекции SQL необходимо тщательно фильтровать входные параметры, значения которых будут использованы для построения SQL-запроса.
Целочисленные параметры
Возьмём другой запрос:
statement := 'SELECT * FROM users WHERE id = ' + id + ';';
В данном случае поле id имеет числовой тип, и его нельзя брать в кавычки. Поэтому «закавычивание» и замена спецсимволов на escape-последовательности не проходит. В таком случае помогает проверка типа; если переменная id не является числом, запрос вообще не должен выполняться.
Например, на Delphi для противодействия таким инъекциям помогает код:
id_int := StrToInt(id);
statement := 'SELECT * FROM users WHERE id = ' + IntToStr(id_int) + ';';
или
query.Params[0].AsInteger := StrToInt(id);
В случае ошибки функция StrToInt возбудит исключение EConvertError, и в его обработчике можно будет вывести сообщение об ошибке. Двойная конверсия (из числа в строку и обратно), выполняющаяся в первом фрагменте явно и во втором неявно, обеспечивает корректную реакцию на числа в формате 132AB (шестнадцатеричная система счисления). На стандартном Паскале, не умеющем обрабатывать исключения, код несколько сложнее.
Фильтрация строковых параметров
Предположим, что код, генерирующий запрос (на языке программирования Паскаль), выглядит так:
statement := 'SELECT * FROM users WHERE name = "' + userName + '";';
Если в качестве значения переменной userName задать строку 'a"; DROP TABLE users; SELECT * FROM data WHERE name LIKE "%', то будет сгенерирована такая SQL-команда:
SELECT * FROM users WHERE name = "a"; DROP TABLE users; SELECT * FROM DATA WHERE name LIKE "%";
Правильный, безопасный код должен проводить замену кавычки на \", апострофа на \', обратной косой черты на \\ (это называется «экранировать спецсимволы»). Это можно делать таким кодом:
statement := 'SELECT * FROM users WHERE name = ' + QuoteParam(userName) + ';';
function QuoteParam(s : string) : string;
{ на входе — строка; на выходе — строка в кавычках и с заменёнными спецсимволами }
var
i : integer;
Dest : string;
begin
Dest := '"';
for i:=1 to length(s) do
case s[i] of
'''' : Dest := Dest + '\''';
'"' : Dest := Dest + '\"';
'\' : Dest := Dest + '\\';
else Dest := Dest + s[i];
end;
QuoteParam := Dest + '"';
end;
Perl, PHP, Java, Delphi и другие языки, ориентированные на базы данных, имеют встроенные средства, автоматически выполняющие эту операцию:
на Delphi — свойство TQuery.Params;
на Perl — через DBI::quote или DBI::prepare;
на Java — через класс PreparedStatement;
на C# — свойство SqlCommand.Parameters;
на PHP (при работе с MySQL) — функции mysql_escape_string, mysql_real_escape_string, addslashes.
на Parser (ни при работе с SQL, ни с XML, ни с почтой) ничего делать не нужно, язык сам предотвращает атаки подобного рода.
wikipedia.org
|