2011-05-14 334 views
25

我需要保护SQL注入的应用程序。应用程序正在使用ADO连接到Oracle,并搜索用户名和密码以进行身份​​验证。Delphi - 防止SQL注入

从我读到现在,最好的方法是使用参数,而不是将整个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; 

而且,我想验证从用户的输入,并删除SQL关键字,比如删除,插入,选择,等等......不是普通的ASCII字母和数字的不同任意输入字符将被删除。

这将保证我的安全级别最低?

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

回答

40

安全

query.SQL.Text := 'select * from table_name where name=:Name'; 

此代码是安全的。
参数始终是安全的SQL注入。

不安全

var Username: string; 
... 
query.SQL.Text := 'select * from table_name where name='+ UserName; 

是不安全的,因为用户名可能是name; Drop table_name; 在下面的查询得到的执行。

select * from table_name where name=name; Drop table_name; 

而且不安全

var Username: string; 
... 
query.SQL.Text := 'select * from table_name where name='''+ UserName+''''; 

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

select * from table_name where name='' or (1=1); Drop Table_name; -- ' 

但这种代码是安全

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

因为IntToStr()将只接受整数所以没有SQL代码可注入的查询字符串这样,只有数字(这是你想要什么,从而允许)

但我想这样做不能用参数完成的东西

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

query:= 'SELECT * FROM :dynamic_table '; {doesn't work} 
query:= 'SELECT * FROM '+tableName;  {works, but is unsafe} 

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

您必须检查字符串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; 
    ... 

这是我知道的唯一方法。

BTW你原来代码中有一个错误:

query.SQL.Text := 'select * from table_name where name=:Name where id=:ID'; 

应该

query.SQL.Text := 'select * from table_name where name=:Name and id=:ID'; 

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

+0

谢谢Johan。我修正了这个问题。+ 1 – RBA

+0

+1覆盖了多个场景 –

+0

但是你仍然可以用'name; Drop table_name;'如果用户在这种类型的输入框中输入 – 2011-05-16 10:39:36

6

This will assure me a minimum of security level?

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

12

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

如果您使用的是Oracle,那么你需要有一个很好的理由使用绑定变量:汤姆凯特有一吨的这个他的网站上http://asktom.oracle.com良好的信息。只需在搜索框中输入“绑定变量”即可。因为你使用的参数

+1

+1 - 中避免SQL注入的最佳方法不是通过线路发送SQL。 – Vector