在PHP中解决跨站脚本攻击(XSS)的最佳方法/实践是什么?

3
我有一个使用URL参数设置变量并在页面内显示的PHP网页。 URL:webaddress.com/page.php?id=someCity
我们采用 $_GET['id'] 并将其分配为变量 ($city),然后在页面上以某种动态方法重构静态文本。
例如:
欢迎来到我们关于Somecity的页面。我们可以帮助您找到与someCity相关的产品,因为我们在Somecity有着丰富的经验。显然,这可以使用 <?php echo $city; ?> 来实现。
我的客户被告知他存在跨站脚本(XSS)漏洞。我的研究表明,然后可以使用 iFrame 来窃取 cookie 并执行恶意操作。推荐的解决方案是使用 PHP 函数 htmlspecialchars() 将字符更改为“HTML 实体”。我不明白这比使用 strip_tags() 删除所有标签更安全。
因此,我同时使用两者以及字符串替换和大写字母,因为这也是必需的。
 $step1 = str_replace('_', ' ', $_GET['id']); // Remove underline replace with space
 $step2 = strip_tags($step1); // Strip tags
 $step3 = htmlspecialchars($step2); // Change tag characters to HTML entities
 $city = ucwords($step3);

问题: 这样做足以防止XSS攻击吗?htmlspecialchars()比strip_tags()更有优势吗? 我理解了类似问题的其他提交中的区别,但想知道每个函数(特别是htmlspecialchars())如何防止XSS攻击。


其他函数类似,但没有提供“为什么”htmlspecialchars()比看起来最正确的strip_tags()更可靠。 - Burndog
1
你确定吗?那里接受的答案解释得相当清楚。 - Wesley Smith
@WesleySmith,建议的类似问题并不相同,因为它涉及两种情况(或者/或者)。对那个答案和我的情况进行更仔细的审查后发现,使用两者顺序是最好的方法,这回答了我的问题,并希望能帮助其他类似情况的人。 - Burndog
4个回答

3
这是OWASP XSS Prevention Cheat Sheet (https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)中的规则1。
推荐对&<>'"/这些特殊字符进行编码。除了斜杠,不强制要求对其进行编码,这正是htmlspecialcharshtmlentities函数所做的。
运行strip_tags的唯一区别是,它将把<编码为&lt;,将>编码为&gt;,并将它们之间的其他内容从字符串中删除。这样做并不能提供更多的安全性,因为在这种情况下,字符串&lt;与空字符串一样安全。它的缺点是会破坏有效输入,因为<>可能出现在普通文本中,因此不能始终用作输出编码策略。
另外,对于HTMLPurifier,在这里不合适,因为目的是将HTML输入转换为HTML输出,但你有纯文本输入而不是HTML。HTMLPurifier会保留<b>Somecity</b>这样的城市名称,不做任何编码。虽然它可能是安全的,因为它不能包含脚本,但在这里允许任何HTML格式更改是不合适的,应该在较早的阶段对其进行编码或拒绝作为无效输入。

感谢您的输入。我同意您发布的内容,但在我们讨论的情况下,按顺序删除所有标记以及任何其他可能的脚本实体可以解决任何潜在的XSS问题。此外,由于范围是简单文本,所建议的两步骤过程是最有效的解决方法(在我看来)。 - Burndog

1
最好的方法是使用成熟且可信赖的库,例如HTMLPruifier来清理来自不受信任的来源的内容。仅运行strip_tags是不够的,因为有很多创造性和隐秘的XSS攻击存在。我建议查看OWASP建议以减轻XSS攻击。值得花时间小心处理这种事情并在开发过程中实际测试漏洞。
如果您是新手,我认为值得研究一些白帽子的夺旗式信息安全培训(有大量免费资源可用),以便了解这些攻击在现实世界中是如何工作的。看到他们可以变得多么聪明真是令人大开眼界。

由于我的情况非常简单(需要将输入字符数限制到变量中),也许库有点过头了?我可以将输入字符限制在25个以内,但这似乎不足以防范恶意代码...无论如何,我会研究你的建议。谢谢。 - Burndog
好的,你要求最好的方法,而不是最简单的,哈哈。 - Rob Ruchte
1
HTML Purifier非常过度,除非您希望用户能够注入一些但不是所有的HTML标记。在这种情况下,我建议使用它,但不用于此。 - Wesley Smith
1
我的意思是,我明白这一点,但问题特别在于最佳方法和最佳实践是什么,而最佳实践是将所有内容都视为不可信的,甚至包括存储在您的数据库中的内容,以及您的用户正在存储的内容(也许尤其如此)。我建立CMS已有20年了,相信我,您永远不能够过于谨慎。在我看来,如果您不过滤进出系统的内容,则是专业过失。 - Rob Ruchte

1

strip_tags()函数只会删除标签,但不会删除其他特殊字符。而htmlspecialchars()函数会将在HTML中具有特殊意义的字符视为HTML实体来处理。你可以在这里找到更多信息。

通常情况下,使用htmlspecialchars()函数就足够了。如果你想允许某些标签,则应该像Rob Ruchte建议的那样使用HTMLPurifier库。


1
我认为在这种情况下最好的答案是同时使用这两个函数。首先使用strip_tags()去除任何标签,然后使用htmlspecialchars()对剩余的情况进行排序。顺序如上所述。

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