有没有一个针对PostgreSQL或SQL等的查询参数进行清理的库,适用于FreePascal和Delphi?

7

我第一次遇到了 SQL 转义错误(早就应该遇到了),当我试图使用 FreePascal 和 Lazarus 执行包含撇号 (例如 O'Brien) 的值的 PostgreSQL 查询时,出现了问题。

SQL.Add(format('select * from zones where upper(zn_name) >=  %s and upper(zn_name) < %s order by zn_name',[sQuote(zoneMin), sQuote(zoneMax)]));

在上述查询中,SQuote是将字符串包装在单引号中的函数。是否有一些标准库可以用于Lazarus/FreePascal或Delphi来净化SQL查询参数?

你可以使用 QuotedStr 函数(尝试进行 SQL 注入...) - kobik
1个回答

18

你的应用程序存在一个严重的安全问题,被称为SQL注入。请查看http://bobby-tables.com/

当然,O'Brian会导致错误,但是如果输入');DROP SCHEMA public;-- 或者 ');DELETE FROM users;-- 呢?第一个不应该起作用,因为你的应用程序不应该以超级用户或拥有表的用户身份运行,但很少有应用程序设计人员会付出实际努力去做到这一点,并经常在生产中运行特权用户。在大多数应用程序中,第二个将起作用;有关详情,请参见文章末尾。

最简单和最好的预防措施是在客户端库中使用参数化语句*。对于Delpi,请参见此示例

To use a prepared statement, do something like this:

query.SQL.Text := 'update people set name=:Name where id=:ID';
query.Prepare;
query.ParamByName( 'Name' ).AsString := name;
query.ParamByName( 'ID' ).AsInteger := id;
query.ExecSQL;

(我从未使用过Delphi,上次编写Pascal代码是在1995年;我只是引用了给出的示例。)

您当前正在进行的操作是参数的字符串插值。 这非常危险。 只有在具有健壮的函数用于引用SQL字面量时,才能安全地执行此操作。这个函数不仅仅在每端加引号,还处理其他转义、引号重复等。这是最后的手段。最好使用参数化语句。


以下是我上面提供示例的详细说明。 假设您要按用户名将用户完全普通地插入数据库,其中“Fred”是客户端输入的示例用户名:

INSERT INTO users ( user_name ) VALUES ('Fred');

现在有一个不愉快的人发送了用户名 ');DELETE FROM users;--。突然间你的应用程序就运行了:

INSERT INTO users ( user_name ) VALUES ('');DELETE FROM users;--');

展开后是:

INSERT INTO users ( user_name ) VALUES ('');
DELETE FROM users;
--');

换句话说,这是一种插入空字符串的方式(虽然他们也可以轻松地插入有效的用户名),接着是一个DELETE FROM users;语句 - 删除users中的所有行 - 然后是一个没有任何作用的注释。砰。你的数据就没了。


*参数化语句有时被错误地称为预处理语句。这是不正确的,因为预处理语句不一定是参数化的,而参数化语句也不一定是预处理的。造成混淆的原因是许多编程语言的数据库接口没有提供使用参数化语句而不使用预处理语句的方法。


2
此外,类似的库设计也适用于PHP(是掌握PHP编程的第一步)和所有其他网络脚本语言。它还可以解决字符串转义、不同日期表示等问题。 "清理"参数是临时的“穷人”替代方法,用来分离明确的参数。如果你看到某人的代码中有“清理”参数而没有现代库,那么他很可能要么没有使用现代库,要么就是安全方面的问题没有得到妥善处理。 - Arioch 'The
我提出这个问题是因为我知道即使是那些认为自己已经解决了问题的人,也会陷入这个雷区。我知道参数化查询,但想知道它们是否保证安全,或者是否有更有经验的人也能欺骗它们。 - vfclists
2
@vfclists 合理使用参数化查询将绝对防止 SQL 注入攻击,除非在数据库中执行动态 SQL,例如 PL/pgSQL 的 EXECUTE 语句。如果您在数据库中使用动态 SQL,则还需要通过仔细使用 format 函数的 %I 格式说明符和 EXECUTE ... USING 来保护免受 SQL 注入攻击。有关此问题,请参见先前的答案:https://dev59.com/gGnWa4cB1Zd3GeqP4trK#12995424。 - Craig Ringer
1
@vfclists 您不能将参数用于诸如表名之类的内容,因此如果它们是动态的,则必须仍将其插入到查询字符串中 - 非常小心。据我所知,双引号足以用于标识符引用,例如 weird"table name" 变成 "weird""table name"""。虽然我没有详细研究过这个问题。如果我必须使用动态标识符名称并且没有适当的标识符引用函数提供,我可能会阅读 PostgreSQL quote_ident 函数的源代码,看看是否有任何遗漏的地方。 - Craig Ringer
简短版本:使用参数化语句将保护该特定SQL查询执行免受SQL注入的影响,但不会保护后续使用第一条查询中的值构造的任何其他SQL,无论是通过PL/PgSQL函数还是通过另一个消耗您语句添加到DB的值的应用程序。您永远不能偷懒并假设输入是安全的。这就像跨站点脚本攻击一样;您必须在每个地方保持警惕。 - Craig Ringer
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接