如何使用 Java 在 PostgreSQL 中安全地转义 SQL 的任意字符串

Posted

技术标签:

【中文标题】如何使用 Java 在 PostgreSQL 中安全地转义 SQL 的任意字符串【英文标题】:How to safely escape arbitrary strings for SQL in PostgreSQL using Java 【发布时间】:2012-04-02 05:29:18 【问题描述】:

我有一个特殊情况,要求我从用户提供的输入值生成 SQL WHERE 子句的一部分。我想防止任何类型的 SQL 注入漏洞。我想出了以下代码:

private String encodeSafeSqlStrForPostgresSQL(String str) 

  //Replace all apostrophes with double apostrophes
  String safeStr = str.replace("'", "''");

  //Replace all backslashes with double backslashes
  safeStr = safeStr.replace("\\", "\\\\");

  //Replace all non-alphanumeric and punctuation characters (per ASCII only)
  safeStr = safeStr.replaceAll("[^\\pAlnum\\pPunct]", "");

  //Use PostgreSQL's special escape string modifier
  safeStr = "E'" + safeStr + "'";

  return safeStr;

问题:

您是否发现任何问题? 你们能提供更好的解决方案吗? 是否有任何现有的库可以帮助解决这个问题?

注意事项:

这是关于 SO 和其他地方的常见问题,但我看到的唯一答案是始终使用 PreparedStatements。 Fwiw,我正在使用 JasperReports。我想将查询保留在 JasperReports 中。用于查询参数处理的内置 Jasper 参数函数(包括 X 函数)不足以满足我需要参数化的内容。我可以尝试创建一个自定义 Jasper QueryExecutor,它允许我注入我自己的 X 函数,但这比使用 Jasper 的 $P! 语法生成动态 SQL where 子句更复杂。

我查看了OWASP libraries。他们还没有 PostgresSQL 编解码器。我查看了OracleCodec,但它的转义似乎很简单。我不确定这对防止 SQL 注入攻击有多大帮助。

在我的代码中,我添加了 E,以便不依赖于 PostgreSQL 的 standard_conforming_strings 设置。理想情况下,我不必添加它,然后该函数不必是 PostgreSQL 特定的。更多信息:http://www.postgresql.org/docs/9.0/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE。

理想情况下,我会想要一个更通用、更强大的解决方案,我知道它是安全的并支持所有可能的 UTF-8 字符串。

【问题讨论】:

BaseConnection.escapeString() 似乎涵盖了这个jdbc.postgresql.org/development/privateapi/org/postgresql/core/… @FrankFarmer 查看 JDBC 驱动程序源代码的好主意。查看 BaseConnection 将我带到 Utils 类:jdbc.postgresql.org/development/privateapi/org/postgresql/core/…。查看源代码,他们打开符合标志,然后转义引号/撇号,类似于我所做的。他们只对 \0 字符进行特殊处理,并让其他所有内容都通过。所以...我想这是安全的,我删除 \0 之外的每个非标准字符是矫枉过正的?随意发表您的评论作为我可以接受的答案。 【参考方案1】:

我在这里问了similar question,但我认为最好的办法是使用org.postgresql.core.Utils.escapeLiteral。这是一个 Postgres 库,因此使用它应该是安全的。如果/当 Postgres 添加新的字符串分隔符时,应该更新此方法。

【讨论】:

【参考方案2】:

最简单的方法是使用 PostgreSQL 的 Dollar Quoting 与一个小的 random 标签:

为每个调用计算一个小的随机标签(例如 4 个字符)(冗余) 查看引号标记是否是输入字符串的一部分。 如果是,重新计算一个新的随机标签。

否则像这样构建您的查询:

$tag$inputString$tag$

通过这种方式,您可以避免使用不同的嵌套引用技术带来的麻烦并且您还可以使用随机标签设置移动目标。

根据您的安全要求,这可能会起作用或不起作用。 :-)

【讨论】:

有趣。我不知道美元引用的字符串语法。我刚才试过了,它似乎确实有效。但是有两个注意事项:1)您指出出于安全原因需要随机化周围的标签是正确的。我认为这是这种方法的主要负面因素。 2)通过测试,我确定美元语法仍然需要从字符串中删除 \0 字符(否则 Postgres 会抛出异常......这仍然比允许漏洞通过更好)。 +1 用于美元报价。但是,您的逻辑存在冗余。如果您仍然检查用户输入的报价标签,则最初不需要随机化。如果引用标签应该弹出,你只需要改变,这实际上只有在攻击者阅读了你的代码时才会发生。只有这样你才需要变异,这会挫败攻击向量。我冒昧地把多余的步骤删掉了。 @ErwinBrandstetter:如果攻击者可能以某种方式掩盖他对任务的使用,则不从一开始就使用随机标签可能会打开一些漏洞。现在这有点手忙脚乱,但我可以想象服务器编码和客户端编码之间的一些编码问题可能会解决问题。 ISO 2022(原文如此?)将是我的第一个猜测。但我会在慢线的时候考虑这个:-) @kaliatech:关于\0 的删除:我猜是这样,因为Java 没有使用严格意义上的UTF-8——它没有将Unicode 代码点0x0000 编码为字节@ 987654324@ 但作为一个两字节序列。其他 UTF-8 接收器当然不知道这一点。在 C 语言中,\0 永远不能成为有效字符串的一部分。因此,这不仅是 SQL/PostgreSQL 的安全问题,也是任何离开 Java 域的安全问题。

以上是关于如何使用 Java 在 PostgreSQL 中安全地转义 SQL 的任意字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何在Ubuntu 16.04和14.04 LTS中安装PostgreSQL 9.5

在 docker 容器中安装 PostgreSQL

如何允许远程访问 PostgreSQL 数据库

怎样在centos中安装postgresql

在Fedora中安装PostgreSQL并配置密码和开启远程登陆

在 PostgreSQL 中安装 utf8 排序规则