如何防止跨站点脚本?

Posted

技术标签:

【中文标题】如何防止跨站点脚本?【英文标题】:How do I protect against cross-site scripting? 【发布时间】:2011-02-16 23:13:03 【问题描述】:

我正在使用 phpmysql 和 smarty 以及用户可以放置 cmets 等的地方。在插入数据库进行 SQL 注入之前,我已经对字符进行了转义。我还需要做什么?

【问题讨论】:

【参考方案1】:

XSS 主要是关于 html 转义(*)。每当您将纯文本字符串放入 HTML 页面时,无论该文本来自数据库、直接来自用户输入、来自文件还是完全来自其他地方,您都需要对其进行转义。

最小的 HTML 转义是将所有 & 符号转换为 & 并将所有 < 符号转换为 <。当您将某些内容放入属性值时,您还需要转义用于分隔属性的引号字符,通常是""。总是转义两个引号(" 和单引号撇号')并没有什么坏处,有些人也将> 转义为>,尽管这仅在 XHTML 中的一个极端情况下是必需的。

任何好的面向网络的语言都应该提供一个功能来为你做这件事。例如在 PHP 中是 htmlspecialchars():

<p> Hello, <?php htmlspecialchars($name); ?>! </p>

在 Smarty 模板中,它是 escape 修饰符:

<p> Hello, $name|escape:'html'! </p>

真的,因为 95% 的时间都需要 HTML 转义(允许包含原始 HTML 标记相对较少),这应该是默认设置。较新的模板语言已经认识到,选择加入 HTML 转义是一个巨大的错误,会导致无限的 XSS 漏洞,因此默认情况下 HTML 转义。

您可以通过将 default modifiers 更改为 html 来使 Smarty 表现得像这样。 (不要像他们建议的那样使用htmlall,除非你真的知道你在做什么,否则它可能会搞砸你所有的非ASCII字符。)

无论您做什么,都不要在输入的 HTML 被处理或放入数据库之前陷入常见的 PHP 错误,即 HTML 转义或“清理”输入。这是执行输出级编码的错误位置,会给您带来各种问题。如果您想验证您的输入以确保它是特定应用程序所期望的,那很好,但在此阶段清除或转义“特殊”字符是不合适的。

*:XSS 的其他方面存在于 (a) 您实际上 想要 允许用户发布 HTML,在这种情况下,您必须将其缩减为可接受的元素和属性,这是复杂的过程通常由 HTML Purifier 之类的库完成,即使这样也有漏洞。另一种更简单的标记方案可能会有所帮助。 (b) 当您允许用户上传文件时,这很难保证安全。

【讨论】:

【参考方案2】:

关于 SQL 注入,转义是不够的 - 您应该尽可能使用数据访问库和参数化查询。

对于 XSS(跨站脚本),从 html 编码输出数据开始。同样,反 XSS 库是您的朋友。

当前的一种方法是只允许非常有限数量的标签进入并清理流程中的标签(白名单 + 清理)。

【讨论】:

@shahinkian - 看起来很适合用于 SQL 注入保护。 @Oded:我建议使用白名单策略和非 HTML,例如 BBCode。 不要不必要地吓唬人。如果始终如一地完成,转义绝对足以防止 SQL 注入。就是丑。 @Thorarin:我强烈建议我们不要依赖转义。您可以争辩说“如果始终如一地完成”就足够了,但并不总是始终如一地完成,即使如此,也并不总是足够的。 (例如,您不能逃避表面上的数值,也不能可靠地避免所有边缘情况,例如嵌入式 NUL。) 我认为将数值转换为字符值可能存在一些问题。至少对于 MSSQL,如果可以使用索引(现在是错误的类型)或在某些情况下给出强制转换异常,它会产生影响。就个人而言,我总是更愿意将参数化的责任放在 许多 人使用的库(例如 db 访问库)上,而不是我刚刚提出的东西。【参考方案3】:

您需要确保人们不能在他们的 cmets 中发布 javascript 代码或可怕的 HTML。我建议您禁止使用非常基本的标记。

如果 cmets 不应该包含任何标记,则做一个

echo htmlspecialchars($commentText);

应该足够了,但它非常粗糙。最好在将所有输入放入数据库之前对其进行清理。 PHP strip_tags() 函数可以帮助您入门。

如果您想允许 HTML cmets,但要确保安全,您可以试试 HTML Purifier。

【讨论】:

我认为您不应该在不通知用户的情况下删除用户数据。只是错误并说这是不允许的 @d03boy:我部分同意,但您可以使用相同的功能来做到这一点。清理输入并与原始输入进行比较。如果不同,则显示错误消息。【参考方案4】:

您不应在将用户输入的数据放入数据库之前对其进行修改。修改应该在您将其输出到网站时进行。您不想丢失原始数据。

当您将其发送到网站时,您希望使用 htmlspecialchars("my output &amp; stuff", ENT_QUOTES, 'UTF-8') 之类的方式将特殊字符转义为 HTML 代码——确保指定您正在使用的字符集。该字符串将被翻译成my output &amp;amp; stuff 供浏览器读取。

【讨论】:

很明显你只是在考虑 XSS 而不是 SQL 注入! @jsd911 这是一个关于 XSS 的问题,所以我将我的信息限制在 XSS... SQL 注入是另一个话题【参考方案5】:

防止 SQL 注入的最佳方法就是不使用接受用户输入的动态 SQL。相反,将输入作为参数传递;这样它将是强类型的并且不能注入代码。

【讨论】:

我认为这完全相关,因为 shahinkian 正在寻求使他们的代码更安全,但承认使用一种方法,出于礼貌,这不是最佳实践。 好吧,我看错了他的问题。编辑你的答案,这样我就可以改变我的反对意见。 @d03boy,呵呵,别担心。无论如何,这实际上只是一句切题的话。

以上是关于如何防止跨站点脚本?的主要内容,如果未能解决你的问题,请参考以下文章

VueJS - 清理输出以防止跨站点脚本攻击

防止来自静态站点的跨站点脚本攻击

使用 html 实体执行跨站点脚本

跨站点脚本攻击(XSS)

防止 asp.net Web 表单中的跨站点请求伪造 (csrf) 攻击

如何修复跨站点脚本(XSS)?我们可以使用 .htaccess 修复吗