解决 php 中跨站脚本(XSS)的最佳方法/实践?

问题描述 投票:0回答:5

我有一个 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。

php security xss
5个回答
3
投票

这是 OWASP XSS 预防备忘单 (https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) 中的规则 1。

这里,建议对

&
<
>
'
"
/
等特殊字符进行编码。除了正斜杠(这对于编码来说并不是严格必要的)之外,这就是函数
htmlspecialchars
htmlentities
所做的事情。

之前运行

strip_tags
的唯一区别是,不是将
<
编码为
&lt;
,将
>
编码为
&gt;
,而是将它们以及它们之间的其他内容从字符串中删除。这并没有提供更多的安全性,因为字符串
&lt;
在这种情况下与空字符串一样安全。它的缺点是会破坏有效输入,因为
<
>
可能出现在普通文本中,因此不能一致地用作输出编码策略。

此外,对于 HTMLPurifier,这在这里不合适,因为目的是将 HTML 输入转换为 HTML 输出,但您有纯文本输入而不是 HTML。 HTMLPurifier 将保持城市名称

<b>Somecity</b>
不变,并且根本不进行任何编码。这可能是安全的,因为它不能包含脚本,但不适合在此处允许任何 HTML 格式更改,并且应该提前编码或拒绝作为无效输入。


1
投票

最好的方法是使用成熟且值得信赖的库,例如 HTMLPruifier 来清理来自不受信任来源的任何内容。仅仅运行 strip_tags 是不够的,那里有很多创造性和阴险的 XSS 攻击。我建议您查看OWASP 建议,以缓解 XSS。值得花时间小心这种事情并在开发过程中实际测试漏洞。

如果您对此不熟悉,我认为还值得研究一些白帽捕获标志风格的信息安全培训(有大量可用的免费资源),以便您了解此类攻击如何在真实世界。看到他们能变得多么聪明,真是令人大开眼界。


1
投票

strip_tags()
仅删除标签,但不删除其他特殊字符。另一方面,
htmlspecialchars()
将 HTML 中具有特殊意义的字符视为 HTML 实体。您可以在此处找到更多信息。

一般来说,

htmlspecialchars()
就足够了。如果您想允许某些标签,您应该使用库 HTMLPurifier,正如 Rob Ruchte 建议的那样。


1
投票

我相信在所提供的情况下最好的答案是使用这两个函数。首先使用 strip_tags() 去除所有标签,然后使用 htmlspecialchars() 对剩余的情况进行排序。上面提供了顺序。


0
投票

对于已知的字符串列表(不是非结构化文本),您可以使用如下方法:

$city= $_GET['city'];
$checkCity = array("New York", "Paris", "London"); // Add Cities as needed
$city= in_array($city, $checkCity) ? $city: "none"; 

它检查数组中的字符串,如果找到则返回城市名称,否则不返回。这可以防止页面上出现 URL 的任何欺骗行为。

© www.soinside.com 2019 - 2024. All rights reserved.