[ SQL-инъекция ]

Инъекция SQL (англ. SQL injection) — один из распространённых способов взлома программ, работающих с базами данных. 
Атака типа инъекции SQL может быть возможна из-за некорректной обработки входящих данных, используемых в SQL-запросах. 
Инъекция SQL часто даёт возможность атакующему выполнить произвольный запрос к базе данных, например, прочитать 
содержимое любых таблиц или удалить все данные.

Разработчик прикладных программ, работающих с базами данных, должен знать о таких уязвимостях и принимать меры 
противодействия инъекции SQL.

Строковые параметры

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

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 si of
      ' : Dest := Dest + '\;
      '"' : Dest := Dest + '\"';
      '\' : Dest := Dest + '\\';
    else Dest := Dest + si;
    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.

Скалярные параметры

Возьмём другой запрос:

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.Params0.AsInteger := StrToInt(id);



В случае ошибки функция StrToInt возбудит исключение EConvertError, и в его обработчике можно будет вывести сообщение 
об ошибке. Двойная конверсия (из числа в строку и обратно), выполняющаяся в первом фрагменте явно и во втором неявно, 
обеспечивает корректную реакцию на числа в формате 132AB (шестнадцатеричная система счисления). На стандартном Паскале, 
не умеющем обрабатывать исключения, код несколько сложнее.

Более крупные куски SQL-кода

Например, следует такой запрос:

statement := 'SELECT * FROM users WHERE ' + expression + ';';



В роли expression может быть, например, строка id=1 or id=2. В данном случае приходится делать более сложную проверку 
корректности — тем не менее, она нужна.


(c) Pupkin-Zade