Delphi - 防止SQL注入

RBA*_*RBA 27 sql delphi oracle sql-injection

我需要保护应用程序免受SQL注入.应用程序使用ADO连接到Oracle,并搜索用户名和密码以进行身份​​验证.

从我读到现在开始,最好的方法是使用参数,而不是将整个SQL分配为字符串.像这样的东西:

query.SQL.Text := 'select * from table_name where name=:Name and id=:ID'; 
query.Prepare; 
query.ParamByName( 'Name' ).AsString := name; 
query.ParamByName( 'ID' ).AsInteger := id; 
query.Open;
Run Code Online (Sandbox Code Playgroud)

另外,我正在考虑验证来自用户的输入,并删除删除,插入,选择等SQL关键字......任何不同于普通ASCII字母和数字的输入字符都将被删除.

这样可以保证我的安全级别最低?

我不想使用除Delphi 7标准和Jedi之外的任何其他组件.

Joh*_*ica 44

安全

query.SQL.Text := 'select * from table_name where name=:Name';
Run Code Online (Sandbox Code Playgroud)

此代码是安全的,因为您正在使用参数.
从SQL注入开始,参数始终是安全的.

不安全

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='+ UserName;
Run Code Online (Sandbox Code Playgroud)

是不安全的,因为用户名可能name; Drop table_name; 导致执行以下查询.

select * from table_name where name=name; Drop table_name;
Run Code Online (Sandbox Code Playgroud)

此外不安全

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='''+ UserName+'''';
Run Code Online (Sandbox Code Playgroud)

因为如果用户名是' or (1=1); Drop Table_name; -- ,它将导致以下查询:

select * from table_name where name='' or (1=1); Drop Table_name; -- '
Run Code Online (Sandbox Code Playgroud)

但是这段代码是安全的

var id: integer;
...
query.SQL.Text := 'select * from table_name where id='+IntToStr(id);
Run Code Online (Sandbox Code Playgroud)

因为IntToStr()只接受整数所以没有SQL代码可以这种方式注入查询字符串,只有数字(这正是你想要的,因此允许)

但我想做一些无法用参数完成的事情

参数只能用于值.它们不能替换字段名称或表名称.所以如果你想执行这个查询

query:= 'SELECT * FROM :dynamic_table '; {doesn't work}
query:= 'SELECT * FROM '+tableName;      {works, but is unsafe}
Run Code Online (Sandbox Code Playgroud)

第一个查询失败,因为您不能使用表或字段名称的参数.
第二个查询是不安全的,但这是完成此任务的唯一方法.
如何保持安全?

您必须tablename根据已批准的名称列表检查字符串.

Const
  ApprovedTables: array[0..1] of string = ('table1','table2');

procedure DoQuery(tablename: string);
var
  i: integer;
  Approved: boolean;
  query: string;
begin
  Approved:= false;
  for i:= lo(ApprovedTables) to hi(ApprovedTables) do begin
    Approved:= Approved or (lowercase(tablename) = ApprovedTables[i]);
  end; {for i}
  if not Approved then exit;
  query:= 'SELECT * FROM '+tablename;
  ...
Run Code Online (Sandbox Code Playgroud)

这是我知道的唯一方法.

BTW您的原始代码有错误:

query.SQL.Text := 'select * from table_name where name=:Name where id=:ID'; 
Run Code Online (Sandbox Code Playgroud)

应该

query.SQL.Text := 'select * from table_name where name=:Name and id=:ID'; 
Run Code Online (Sandbox Code Playgroud)

你不能where在一个(子)查询中有两个

  • @daemon_x,参数永远不会进入实际的SQL,总是直接进入字段.就像字段的内容永远不会是SQL语句的一部分.`select*from table1,其中a ='hello'.如果字段"a"包含"name"; 删除table1;`它没有做任何坏事,这是参数之美. (2认同)

dav*_*vek 12

如果您允许用户影响将使用占位符绑定到sql命令文本的参数值,那么您实际上不需要检查用户输入的内容:正如您所提到的,避免SQL注入的最简单方法是,是避免连接SQL,并使用绑定变量(或调用过程)这样做(它还有一个优点 - 里程/相关性取决于数据库 - 允许引擎重用查询计划).

如果您使用的是Oracle,那么您需要有一个非常好的理由使用绑定变量:Tom Kyte在他的网站http://asktom.oracle.com上有很多关于此的信息.只需在搜索框中输入"绑定变量"即可.


Dar*_*rov 6

这样可以保证我的安全级别最低?

是参数化查询应该保护您免受SQL注入,这很容易测试.只需在name变量中输入一些危险的字符串,看看会发生什么.通常你应该返回0行而不是错误.