捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP
PHP 网站如何防范 XSS?从输出过滤理解跨站脚本攻击
图片说明:示意图 图片来源:Public Domain(公有领域)
XSS,也就是跨站脚本攻击,表面上看是网页里出现了一段不应该出现的JavaScript,实际问题却发生在浏览器对输出内容的解析过程中。攻击者提交的数据本身未必具有攻击性,真正危险的是网站把这些数据放进HTML、HTML属性、JavaScript、URL等特定上下文之后,浏览器把原本应该当成普通数据的内容解释成了代码。
因此,PHP网站防范XSS时最重要的概念并不是“把用户输入过滤干净”,而是“在输出到特定上下文时进行正确编码”。这是很多老式PHP教程最容易讲错的地方。HTML文本、HTML属性、JavaScript字符串、CSS和URL并不是同一种语法环境,针对其中一种环境有效的编码方法,并不能自动适用于另外一种环境。
例如用户提交下面这样的评论:
如果PHP直接输出:
echo $comment;
浏览器会把其中的
那么仅仅使用htmlspecialchars()并不能建立一个可靠的JavaScript上下文安全边界。更合适的方式是让PHP把数据序列化为JavaScript值:
这里的json_encode()承担的是数据序列化,而不是HTML过滤。它能够把字符串、数字、数组等PHP数据转换为合法的JSON/JavaScript表示形式。需要根据实际嵌入位置选择合适的安全策略,尤其不要把未经处理的用户输入直接拼接进JavaScript源码。
原文提出“脚本标签内使用htmlspecialchars_decode()”则是完全相反的方向。htmlspecialchars_decode()的作用是把HTML实体解码回特殊字符,它不是XSS防御函数。攻击者提交的数据如果已经被编码成安全文本,开发者再调用htmlspecialchars_decode(),反而可能把原本已经失去HTML语义的字符重新恢复成危险字符。因此,不能把这个函数作为JavaScript上下文的安全处理方式。
如果数据只是为了传递给前端JavaScript,更推荐通过JSON数据结构或者data-*属性传递,而不是把用户输入拼接成一段JavaScript源码。例如:
前端再读取这个JSON数据。这样可以把“数据”和“代码”分开,减少字符串拼接带来的上下文混乱。
CSS上下文同样需要单独处理。不能认为只要调用一次htmlspecialchars(),任何位置都安全。例如把用户输入直接拼进style属性或者