【问题标题】:Delphi - prevent against SQL injectionDelphi - 防止 SQL 注入
【发布时间】:2011-05-14 08:24:42
【问题描述】:

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

从我到现在为止的阅读中,最好的方法是使用参数,而不是将整个 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 之外的任何其他组件。

【问题讨论】:

    标签: sql oracle delphi sql-injection


    【解决方案1】:

    安全

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

    据我所知,这是唯一的方法。

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

    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

    【讨论】:

    • 但是如果用户在这种输入框中输入了name; Drop table_name;,你仍然可以注入SQL参数
    • @daemon_x:在第一种情况下,在文本框中输入name; Drop table_name; 将导致查询select * from table_name where name='name; Drop table_name;';,这将是非常安全的。
    • @Allan - 如果我使用 name'; Drop table_name; Select '0 结果会是 select * from table_name where name='name'; Drop table_name; Select '0' 吗?我不是参数用户,所以我不知道单引号是否会加倍(但我希望它们是)。
    • @daemon_x,参数永远不会进入实际的 SQL,总是被扔到现场。就像字段的内容永远不是 SQL 语句的一部分一样。 select * from table1 where a = 'hello'. If field a` 包含 name'; Drop table1; 它不会做任何坏事,这就是参数的美妙之处。
    • @Johan - 现在我明白了;我认为客户端通过替换参数值来准备最终查询,但它们作为真正的 SQL 参数 (@) 传递给数据库引擎。所以+1(明天我会得到投票时:))
    【解决方案2】:

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

    如果您使用的是 Oracle,那么您需要有充分的理由使用绑定变量:Tom Kyte 在他的网站http://asktom.oracle.com 上有大量关于此的有用信息。只需在搜索框中输入“绑定变量”即可。

    【讨论】:

    • +1 - 避免 SQL 注入的最佳方法是不要通过网络发送 SQL。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2011-06-12
      • 1970-01-01
      • 2018-03-23
      • 2019-06-22
      • 2013-08-31
      • 2019-06-03
      • 2015-09-07
      • 1970-01-01
      相关资源
      最近更新 更多