我需要保护应用程序免受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;
另外,我正在考虑验证来自用户的输入,并删除删除,插入,选择等SQL关键字......任何不同于普通ASCII字母和数字的输入字符都将被删除。
这可以保证我的安全级别最低吗?
我不想使用除Delphi 7标准和Jedi之外的任何其他组件。
答案 0 :(得分:43)
安全
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
答案 1 :(得分:12)
如果允许用户影响仅将绑定到带有占位符的sql命令文本的参数值,那么您实际上不需要检查用户输入的内容:最简单正如你所提到的,避免SQL注入的方法是避免连接SQL,并使用绑定变量(或调用过程)来做到这一点(它还有一个优点 - 里程/相关性取决于数据库 - 允许引擎重用查询计划)。
如果你使用的是Oracle,那么你需要有一个很好的理由不使用绑定变量:Tom Kyte在他的网站http://asktom.oracle.com上有大量关于此的信息。只需在搜索框中输入“绑定变量”即可。
答案 2 :(得分:6)
这可以保证我的安全级别最低吗?
是参数化查询应该保护您免受SQL注入,这很容易测试。只需在name
变量中输入一些危险的字符串,看看会发生什么。通常你应该返回0行而不是错误。