C#中的撇号通过了过滤器。

10

非常抱歉,我必须这么做,因为我在工作的网站上发现了一个可能可利用的安全问题,我会用新帐户发布此内容。

我们有一个脚本来接收用户评论(所有评论都是英文)。过去两年中,我们已经积累了大约3,000,000个评论。我正在检查评论表是否存在任何恶意行为的迹象,这一次我扫描了撇号字符。在所有情况下,它应该被转换为HTML实体('),但我发现了18条记录(300万条评论中的18条记录)中该字符幸存了。真正困扰我的是,在这18个评论中,有一个撇号实际上被成功转换了 - 另一个幸存了。

这对我来说表示我们可能有一个XSS漏洞。

我对于发生了什么的理论是:用户在使用非西方代码页的计算机系统上访问页面,并且他们的浏览器忽略了我们页面的utf-8字符集规范,直到输入到达数据库之前,其输入没有被转换为服务器的本地代码页(因此C#无法识别该字符为撇号,因此无法将其转换,但是当尝试将其写入LATIN1表时,数据库可以)。但这只是一个猜测。

有人遇到过这种情况吗?或者知道发生了什么?

更重要的是,有人知道我该如何测试我的脚本吗?切换到HttpUtility可能会修复此问题,但在我知道发生了什么之前,我无法确定问题是否已经解决。我需要能够测试此功能以确保我们的解决方案有效。

编辑

哇,已经有20个点,所以我可以编辑我的问题了。

我在其中一个评论中提到,我发现了几个似乎存在问题的字符。它们包括:0x2019、0x02bc、0x02bb、0x02ee、0x055a和0xa78c。这些字符能够通过我们的过滤器。不幸的是,它们也能够通过所有 HttpUtility 编码方法。但是一旦它们被插入数据库,它们会被转换成实际的撇号或“?”。

回顾一下,我认为问题在于这些字符本身并没有构成威胁,所以 HttpUtility 没有理由将它们转换。在 JavaScript 代码块中,它们是无害的。在 HTML 代码块中,它们只是字符数据,也是无害的。在 SQL 代码块中,它们也是无害的(如果数据库共享相同的编码页)。对我们来说的问题是,因为我们在数据库中使用的编码页不同,插入过程会将这些“不可打印”字符转换为“已知等价物”(在这种情况下是“劣质”的)和“未知等价物”(渲染为“?”)。这完全出乎我们的意料,我有点失望微软没有在他们的 HttpUtility 编码函数中做更多的工作。

我认为解决方案是更改受影响表的排列规则。但如果有其他更好的想法,请在下面发表评论。


对不起,你说这个网站的“名称”是什么? - Rick Sladkey
1
数据库中的撇号字符有什么问题? - zerkms
300万中的18表明这不是一次有组织的XSS攻击。只需使用HttpUtility.HtmlEncode,并将其留给ASP.NET安全团队(希望他们能看到这个问题)。 - Xaqron
@Rick - 没错!! @zerkms - 有两个原因:它最终被用来构建发送给用户的JSON结构,而且假设如果在进入数据库之前进行转换,那么在使用时直接使用是安全的。此外,一般来说,我希望确保如果将来的开发人员决定从原始表数据编写构建SQL语句的脚本(例如进行转换或移动),那么撇号将成为无害的HTML实体。这是为了避免潜在的SQL注入漏洞。 - Anonymous
我找到了一个能够通过的Unicode字符。Unicode 0x02BC可以在我们的过滤器中不变地通过,并在插入LATIN1表时转换为0x37。现在我至少有一个字符可以测试了。这些其他字符要么在C#中被转换,要么在代码页转换失败并以“?”的形式出现在数据库中:0x0027、0x2019、0x02BB、0x02EE、0x055A和0xA78C。 - Anonymous
显示剩余2条评论
3个回答

3

我认为你的过滤位置不正确。数据库应该包含用户输入的实际字符。你应该将HTML转义留给表示层处理,因为它更了解如何处理。


1
此外,如果您后来发现需要进行额外的转义,那么在未转义的内容输出后再进行转义是非常简单的。对“部分转义”内容进行转义将会失败。 - Hans Kesting
我有些犹豫。一方面,我认为你显然是正确的,过早地进行HTML格式化或防止XSS是不明智的。另一方面,我个人认为用户提交的内容是如此固有的危险,以至于必须尽早永久性地“去除危险”,以防止潜在的注入。代价是,以后可能需要额外的处理来撤销“去除危险”的操作,但好处是,我永远不必担心甚至已经存在于数据库中的用户文本涉及潜在的SQL注入问题。 - Anonymous
问题在于,对用户输入进行消毒取决于上下文。有时您必须剥离HTML标签/ JavaScript。其他时候,您正在防止SQL注入。如果仅在DB的一个中心位置完成,则很难正确执行。我无法计算我在网站上输入文本并返回双倍转义的次数,因为有人对需要完成多少次未转义感到困惑,只是为了确保加入了一些额外的内容。 - artbristol
你完全正确。每当我测试一个网站时,我总是以“Patty O'Brien”的身份登录,但我发誓有50%的时间,在保存几次后,我的名字被改成了“Patty O\\\\'Brien”。我知道有时候这样破坏用户输入会导致严重的问题(比如,当用户讨论SQL注入或HTML编码问题时会发生什么)。但这就是我对用户感到恐惧的原因。 - Anonymous

1

这似乎是因为你的DBMS存储使用了非Unicode列类型,而.NET使用Unicode。

你可以在.NET中最初将Unicode转换为你的DBMS排序规则,然后再转换回Unicode,以在应用程序级别上删除任何不受支持的字符,而不是留给DBMS/连接器处理。

var encoding = Encoding.GetEncoding("Latin1") //this should be matched to the column's collation
foo = encoding.GetString (encoding.GetBytes (foo)); // couldn't see a more efficient way to do this.

虽然如先前所述,理想情况下应将实际字符存储在DBMS中,并将编码留给表示步骤。你要尝试以这样的方式设置框架,以便你不会忘记对字符串数据进行编码,例如asp.net 4使用<%: %>,JSON使用JSON.Net而不是字符串连接,对于XML XLINQ等。


砰!你一针见血地击中了要害。这对我来说可行。不幸的是,我们还没有迁移到.NET 4.0。然而,我肯定期待着因为这个原因迁移过去。 - Anonymous
我应该说你和@artbristol关于何时进行编码的观点是完全正确的。在这种情况下,答案解决了即时问题。稍后,当我们使用4.0(和MVC)时,我们可以减轻反用户编码实践,但这将使我们回到安全领域。 - Anonymous

0

虽然尝试过滤用户内容总是好的,但假设您可以可靠且安全地“捕捉”所有内容并不现实。

始终假定您数据库中的用户数据已经损坏、被黑客攻击、包含纯HTML或其他浏览器特定代码,而不是了解这些内容,确保所有用户数据的输出都得到安全编码。

例如-在渲染页面时对所有数据进行HtmlEncode(),并为用户可以编辑的每个字段执行此操作。即使是基本的名字字段等,而不仅仅是评论正文数据。

此外,单引号不是XSS问题,允许标签和浏览器特定代码才是问题,您可以完全未编码地显示任意数量的单引号,而不会出现问题,并且您无法使用此方法形成XSS攻击。但是,您可以轻松地使用标签进行XSS攻击,而不需要任何单引号(甚至双引号)。我认为您可能将SQL注入问题(SQL字符串中的单引号)与XSS问题混淆了。


谢谢。我在处理用户生成的内容时非常谨慎,所以我认为我们做得很好。不幸的是,这段文本的使用实际上是一个XSS问题(或者如果用户能够找出如何在数据库中获得类似过滤字符的话,就可能是)。对我来说,基本问题是一个字符通过了我们的过滤器,我不知道它是如何做到的,也不知道如何重现它(因此我无法测试我们的修复是否有效)。 - Anonymous
Unicode 单引号不匹配单引号,所以很可能是这样通过的,还有基于单词的正向和反向引号。无论如何,也许这就是你想要的东西?http://wpl.codeplex.com/ - White Dragon
除此以外,我认为感到100%安全的唯一方法是编写自己的编码函数,HtmlEncode并不会编码所有内容,尤其是许多Unicode字符-它们不大,不需要太多CPU。您可以列出a-z0-9空格和几个基本字符!@#$〜等白名单,并且要严格编码其他所有内容!然后,无论它是否为Unicode,任何代码页都将是安全的。 - White Dragon

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