Delphi-防止SQL注入

Delphi-防止SQL注入,sql,oracle,delphi,sql-injection,Sql,Oracle,Delphi,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.Para

我需要保护应用程序不受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关键字,如delete、insert、select等。任何与普通ASCII字母和数字不同的输入字符都将被删除

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

我不想使用Delphi7 standard和Jedi之外的任何其他组件

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


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

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

如果您使用的是Oracle,那么您需要有一个非常好的理由来而不是使用绑定变量:Tom Kyte在他的网站上有大量关于这方面的好信息。只需在搜索框中输入“绑定变量”。

Safe

query.SQL.Text := 'select * from table_name where name=:Name';
此代码是安全的,因为您正在使用参数。
参数在SQL注入中总是安全的

不安全

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='+ UserName;
var Username: string;
...
query.SQL.Text := 'select * from table_name where name='''+ UserName+'''';
var id: integer;
...
query.SQL.Text := 'select * from table_name where id='+IntToStr(id);
不安全,因为用户名可能是
name;删除表格名称
导致执行以下查询

select * from table_name where name=name; Drop table_name;
不安全

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='+ UserName;
var Username: string;
...
query.SQL.Text := 'select * from table_name where name='''+ UserName+'''';
var id: integer;
...
query.SQL.Text := 'select * from table_name where id='+IntToStr(id);
因为如果用户名是
”或(1=1),则它会被忽略;删除表格名称--
它将导致以下查询:

select * from table_name where name='' or (1=1); Drop Table_name; -- '
但此代码是安全的

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='+ UserName;
var Username: string;
...
query.SQL.Text := 'select * from table_name where name='''+ UserName+'''';
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;
  ...
据我所知,这是唯一的办法

顺便说一句,您的原始代码有一个错误:

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'; 

您不能在一个(子)查询中有两个
,其中

+1-避免SQL注入的最佳方法不是通过线路发送SQL。但您仍然可以使用
名称注入SQL参数;删除表格名称如果用户在此类输入中输入box@daemon_x:在第一个场景中,输入
name;删除表格名称将导致查询
select*from table_name,其中name='name;删除表_name;',这将是非常安全的。@Allan-如果我使用
name';删除表格名称;选择“0”
结果将是
Select*from table_name where name='name';删除表格名称;选择“0”
?我不是参数用户,所以我不知道单引号是否会加倍(但我希望如此)。@daemon_x,参数永远不会进入实际的SQL,参数总是被直接抛出到字段中。就像字段的内容从来都不是SQL语句的一部分一样<代码>从表1中选择*,其中a='hello'。如果字段
a`包含
name';删除表1它不会做任何坏事,这就是参数的美妙之处。@Johan-现在我明白了;我认为客户端通过替换参数值来准备最终查询,但它们作为真实的SQL参数(
@
)传递给DB引擎。So+1(明天我什么时候能拿到选票:)