我有一个文本字段,允许用户输入他/她想要的任何内容。保存后,结果稍后会在屏幕上显示给潜在的许多人。
XSS 对我来说似乎有点像黑魔法,所以我想知道当前处理这种情况的最佳实践是什么(从清理输入的特定方法到编码 html 以显示的特定方法)?
我有一个文本字段,允许用户输入他/她想要的任何内容。保存后,结果稍后会在屏幕上显示给潜在的许多人。
XSS 对我来说似乎有点像黑魔法,所以我想知道当前处理这种情况的最佳实践是什么(从清理输入的特定方法到编码 html 以显示的特定方法)?
由于您想要当前的最佳实践,而这里的最新答案是 2012 年 8 月,我想我不妨权衡一下并更新它。
防止任何类型的 XSS 攻击(持久、反射、DOM 等)的最佳实践。
X-XSS-Protection: 1; mode=block
将反射型 XSS 浏览器保护激活为阻止模式而不是过滤模式。阻止模式阻止这样X-Content-Type-Options: nosniff
防止将 JavaScript 插入图像和其他内容类型。Content-Security-Policy:
至少有严格的script-src
和style-src
。不允许unsafe-inline
或unsafe-eval
。这是杀死 XSS 的标头之父。<body data-foo="@foo" />
@foo
将输出变量的 HTML 编码版本。例如" />
会给<body data-foo="" />" />
var foo = $("body").data("foo");
document.write
,否则您可能会引入漏洞。理想情况下,虽然使用textContent
或 JQuery 的text()
和attr()
功能。以相反的顺序处理这些问题。专注于#3,因为这是 XSS 的主要缓解措施,#2 告诉浏览器不要执行任何漏掉的东西,#1 是一个很好的纵深防御措施(如果特殊字符无法进入,它们可以)不要出去)。但是,#1 较弱,因为并非所有字段都可以严格验证,并且可能会损害功能(想象 Stack Exchange 无法允许“ <script>
”作为输入)。
function escapeHtml(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/\//g, "/")
}
使用自动转义模板语言编写 HTML,默认情况下,该语言会为您转义不受信任的输入。
例如,Django 模板转义 HTML 特殊字符:
显然,不应盲目信任用户提交的数据并将其直接插入到您的网页中,因为恶意用户可能会利用这种漏洞来做潜在的坏事。这种类型的安全漏洞称为跨站点脚本 (XSS) 攻击。
为避免此问题,您有两种选择:
- 您可以确保通过转义过滤器运行每个不受信任的变量,这会将可能有害的 HTML 字符转换为无害的字符。这是 Django 最初几年的默认解决方案,但问题是它让您(开发人员/模板作者)有责任确保您逃避一切。很容易忘记转义数据。
- 您可以利用 Django 的自动 HTML 转义。本节的其余部分描述了自动转义的工作原理。默认情况下,在 Django 中,每个模板都会自动转义每个变量标签的输出。
还有各种更智能的上下文自动转义模板,即使您的模板包含嵌入的 JavaScript、CSS 和 URL,也可以防止 XSS。
闭包模板说:
上下文自动转义通过增加闭包模板来根据其出现的上下文正确编码每个动态值,从而防御攻击者控制的值中的 XSS 漏洞。
Go 的模板语言使用上下文自动转义:
HTML 模板将数据值视为应编码的纯文本,以便它们可以安全地嵌入到 HTML 文档中。转义是上下文相关的,因此动作可以出现在 JavaScript、CSS 和 URI 上下文中。
单一的最佳实践:严格控制类型并清理您的数据。
始终使用正确的方法根据类型输出数据:永远不要将输入的内容渲染为 HTML 中的文本(即将用户输入的文本数据分配给.data
DOM 中的文本节点属性,而不是魔法.innerHTML
)。永远不要在 -like 构造中使用输入eval
或作为数据库查询的子字符串 - 适当的工具将分别是表达式解析器和占位符。
拒绝或清理从外部来源(包括您自己的内部数据库,也可能受到损害!)到严格的允许模式集的所有内容。
使用已建立且经过验证的协议和序列化程序来传输数据,最好是在发送端和接收端都有本地实现(JSON 序列化程序、protobuf、Perl 中的可存储等),而不是自制的引用。