8.1.4 等安全发布技术信息
CVE-2006-2313
攻击者能够向应用程序提交精心制作的字符串,这些字符串将被嵌入到 SQL 命令中,可以使用无效编码的多字节字符绕过标准字符串转义方法,从而导致可能将恶意 SQL 命令注入数据库。此处介绍的攻击适用于任何多字节编码(但另请参阅 CVE-2006-2314)。影响所有早于 8.1.4、8.0.8、7.4.13、7.3.15 的 PostgreSQL 版本。
CVE-2006-2314
在允许 0x5c(反斜杠的 ASCII 代码)作为多字节字符尾字节的多字节编码中,将 ASCII 单引号“'”转义为“\’”的普遍做法是不安全的;这至少包括 SJIS、BIG5、GBK、GB18030 和 UHC。如果应用程序在将不受信任的字符串嵌入到 SQL 命令中时使用此转换,并且它与服务器以这些编码之一进行通信,则该应用程序容易受到 SQL 注入攻击。虽然与 PostgreSQL 一起使用的标准客户端库在一段时间内以安全的 SQL 标准方式对“'”进行了转义为“”,但旧的实践仍然很常见。从 PostgreSQL 版本 8.1.4、8.0.8、7.4.13、7.3.15 开始,服务器已修改为在客户端使用这些编码之一时拒绝“\’” 。这本身并不能解决所有问题的变体,但它将使这种客户端明显存在故障,需要修复。受影响的客户端的可能解决方法是避免使用易受攻击的字符编码。 详细信息
根本问题是,客户端代码和服务器端代码在遇到无效编码的多字节数据时可能具有不同的行为。鉴于客户端转义通常由临时代码完成,因此“确保它们始终表现一致”的解决方案似乎不可行,我们改为尝试通过拒绝可能损坏的查询来从服务器端关闭问题。
CVE-2006-2313 中描述的特定问题源于以下事实:客户端转义代码通常根本不使用任何编码知识,只是将任何具有高位设置的字节视为单个非 ASCII 字符。在 UTF8 等编码中,这种做法没有明显错误(但请参见下文了解其他编码)。但是,PostgreSQL 服务器的行为有所不同,这种不匹配会打开一个漏洞。例如,假设客户端在 UTF8 编码中运行,并且攻击者提交包含以下内容的数据字符串
0xc8 ' some text
(其中 0xc8 表示具有该十六进制值的单个字节)。在 UTF8 中,0xc8 开始一个两字节字符,但第二个字节的值应在 0xA0-0xFF 之间;因此,上面的字符串不是有效编码的。不了解编码的客户端将忽略此问题并尝试转义引号,从而产生
0xc8 ' ' some text 0xc8 \ ' some text
先前版本的 PostgreSQL 服务器将接受 0xc8 后面跟随任何字节值作为两字节字符,因此服务器将看到上述内容为一个两字节字符,后面跟着一个字符串文字结束引号。然后,“some text” 将成功注入到提供给服务器的 SQL 查询字符串中。(在某些情况下,服务器会发出有关无效两字节字符的警告,然后将其丢弃,但仅仅警告并不能阻止攻击。)
我们对 CVE-2006-2313 的解决方案是修改服务器以更仔细地检查多字节编码,并将无效输入视为停止查询的错误,而不仅仅是警告。现在它将拒绝 0xc8 后面跟随的字节不在 0xA0-0xFF 之间的字节。这消除了问题,而无需对客户端是否已修补以明确了解多字节编码做出任何假设。在 UTF8 等“安全”编码中,我们现在可以确定客户端和服务器对字符串中哪些字符是引号和反斜杠达成一致。
但是,在几个远东字符编码中,0x5c(反斜杠的 ASCII 代码)是两字节字符的有效第二个字节。例如,两字节序列 0x95 0x5c 是 SJIS 中的有效字符。在这些编码中,存在另外两个风险,我们将其描述为 CVE-2006-2314。
首先,不了解编码的客户端可能会尝试转义它认为是反斜杠的内容。例如,攻击者发送完全合法的 SJIS 字符串
0x95 0x5c ' some text
如果客户端不了解编码,它将把 0x5c 视为一个单独的反斜杠字符并将其加倍。然后,它还将转义引号,从而产生
0x95 0x5c \ ' ' some text 0x95 0x5c \ \ ' some text
由于服务器将正确地将 0x95 0x5c 视为单个字符,因此两种情况都成功地注入了“some text”:最后一个引号将以未转义的形式显示在服务器上。一个更微妙的情况是 0x95 0x5c 出现在字符串的末尾:添加的反斜杠将有效地引用预期的字符串结束引号。如果攻击者还可以控制查询中的下一个字符串文字,那么他就会获胜,例如
WHERE key1 = '0x95 0x5c \' AND key2 = 'injected text here' ...
其次,如果客户端将“'”转义为“\’”,则它可以产生一个以前不存在的有效多字节字符。再次考虑 SJIS,假设攻击者能够发送(无效)字符串
0x95 ' some text
如果客户端不了解编码,并且将引号视为一个单独的字符需要转义,它将产生
0x95 \ ' some text
服务器将其视为
0x95 0x5c ' some text
因此,注入攻击再次成功。
解决这些问题的唯一真正方法是修复客户端:转义必须在了解字符集编码的情况下完成(因此,作为多字节字符一部分的“反斜杠”不会被转义),并且引号需要使用 SQL 标准表示“”而不是“\’”转义。请注意,在所有这些编码中,0x27 都不是有效的尾字节,因此只要服务器检查多字节编码的有效性,就不会出现相应的“”转义问题。
但是,等待客户端修复并不是一个非常可行的安全方法。我们改为修改服务器,以便(默认情况下)它在以允许嵌入 0x5c 的编码运行时拒绝“\’”作为引号的表示。此更改消除了与不了解编码的客户端尝试加倍嵌入的 0x5c 相关的安全漏洞,只要客户端将“'”转义为“”即可;请注意,在客户端未使用“\’”的上述情况下,服务器看到了“\’”作为 0x5c 错误加倍的结果。但是请注意,在客户端尝试使用“\’”的示例中,服务器看到了一个完全有效的查询,没有明显使用“\’” 。因此,拒绝“\’”本身并不能阻止注入攻击。相反,这是一种部分解决方案,它还将使在正常使用中明显地使用这种表示法的不安全的编码的客户端存在故障。这应该允许它们在受到攻击之前得到修复。
(拒绝“\’”的另一个原因是它符合我们向 SQL 标准字符串文字规则过渡的长期计划,其中反斜杠不是特殊字符。在此时仍然使用“\’”的客户端将面临新的 SQL 注入风险。)
有许多缓解因素可能会阻止特定应用程序受到这些安全风险的影响
* If application always sends untrusted strings as out-of-line parameters, instead of embedding them into SQL commands, it is not vulnerable. * If client_encoding is a single-byte encoding (e.g., one of the LATINx family), there is no vulnerability. * If application cannot pass invalidly encoded data to the server, there is no vulnerability (this probably includes all Java applications, for example, because of Java's handling of Unicode strings).
很明显,CVE-2006-2314 是一个相当严重的问题,因为正确的修复可能需要更改应用程序代码,而不仅仅是快速更新库或服务器。这可以通过以下事实来缓解:它仅在使用某些字符编码时才会发生,并且(至少在远东以外)这些编码不是 Web 公开应用程序的流行编码。对于无法立即更新的应用程序,一个解决方法是在处理不受信任的数据时避免使用这些编码。
PostgreSQL 全球开发小组感谢石田昭夫和荻垣康雄发现并报告这些问题。