【问题标题】:Mitigate xss attacks when building links在构建链接时减轻 xss 攻击
【发布时间】:2012-05-06 08:25:56
【问题描述】:

我不久前发布了这个问题,它非常适合从用户生成的帖子中查找和“链接”链接。 Linkify Regex Function PHP Daring Fireball Method

   <?php
if (!function_exists("html")) {
function html($string){
    return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
}

if ( false === function_exists('linkify') ):   
  function linkify($str) {
$pattern = '(?xi)\b((?:(http)s?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))';
return preg_replace_callback("#$pattern#i", function($matches) {
    $input = $matches[0];
    $url = $matches[2] == 'http' ? $input : "http://$input";
    return '<a href="' . $url . '" rel="nofollow" target="_blank">' . "$input</a>";
}, $str); 
}
endif;

echo "<div>" . linkify(html($row_rsgetpost['userinput'])) . "</div>";

?>

我担心将用户生成的内容插入链接可能会带来安全风险。在通过 linkify 函数运行它并回显到页面之前,我已经使用 htmlspecialchars($string, ENT_QUOTES, 'UTF-8') 转义了来自我的数据库的用户内容,但是我在 OWASP 上读到需要对链接属性进行特殊处理以缓解 XSS。我认为这个功能没问题,因为它将用户生成的内容放在双引号内,并且已经用htmlspecialchars($string, ENT_QUOTES, 'UTF-8') 转义,但非常感谢具有 xss 专业知识的人来确认这一点。谢谢!

【问题讨论】:

  • 如果不受信任的输入被放置到 href、src 或其他基于 URL 的属性中,则应该对其进行验证以确保它没有指向意外的协议,尤其是 Javascript 链接。然后应该像任何其他数据一样根据显示的上下文对 URL 进行编码。例如,HREF 链接中的用户驱动 URL 应该是属性编码的。给出的示例是 Java 中的。不知道如何在 PHP 中实现...code.google.com/p/owasp-esapi-java/source/browse/trunk/src/main/…
  • -1 这很尴尬,你需要测试你的代码。
  • 请查看完整代码的已编辑问题。
  • @Jeff,您的代码实际上很不安全,因为 htmlspecialchars 不会删除任何标签。所以有人可以添加&lt;script&gt;...&lt;/script&gt;。在该脚本中可能很难做任何事情,因为不允许使用引号,但我不会依赖它。请改用htmlentities()

标签: php security xss linkify


【解决方案1】:

数据进入数据库前必须NEVER转义,这是非常严重的错误。这不仅不安全,而且会破坏功能。链接字符串的值是数据损坏并影响字符串比较。这种方法是不安全的,因为XSS is an output problem。当您将数据插入数据库时​​,您不知道它出现在页面上的什么位置。例如,即使您使用此函数,以下代码仍然容易受到 XSS 攻击:

例如:

<a href="javascript:alert(1)" \>

就您的正则表达式而言。我最初的反应是,这是一个可怕的想法。没有关于其应该如何工作以及大量使用 NOT 运算符的问题,黑名单总是比白名单更糟糕

所以我加载了Regex Buddy,在大约 3 分钟我用这个输入绕过了你的正则表达式:

https://test.com/test'onclick='alert(1);//

没有开发人员愿意编写易受攻击的代码,因此程序员认为他的应用程序如何工作以及它实际上是如何工作的会导致他们崩溃。在这种情况下,我会假设您从未测试过这个正则表达式,并且它对问题的过度简化。

HTMLPurifer 是一个用于清理 HTML 的 php 库,它由 THOUSANDS 个正则表达式组成。它非常慢,并且经常被绕过。所以如果你走这条路,一定要定期更新。

在修复这个缺陷方面,我认为最好使用htmlspecialchars($string, ENT_QUOTES, 'UTF-8'),然后强制字符串以“http”开头。 HTML 编码是一种转义形式,其值会被自动解码,从而使 URL 不受干扰。

【讨论】:

    【解决方案2】:

    因为数据要进入属性,所以应该是 url(或百分比)编码:

    return '<a href="' . urlencode($url) . '" rel="nofollow" target="_blank">' . "$input</a>";
    

    技术上它也应该是 html 编码的

    return '<a href="' . htmlspecialchars(urlencode($url)) . '" rel="nofollow" target="_blank">' . "$input</a>";
    

    但我知道没有浏览器关心,因此没有人这样做,听起来您可能已经在执行此步骤并且您不想想要执行两次

    【讨论】:

    • 这些方法都不会产生有效的 http 链接。这种方法会弄巧成拙。
    • 谢谢@tobyodavies。我认为你在正确的轨道上,我之前尝试过。不幸的是,它破坏了 'linkify' 函数,因为 urlencoded url 不再与确定 preg_replace_callback 函数中链接的正则表达式匹配。不幸的是,这有点超出我的想象......不确定是否可能。
    • @Rook 什么可能的无效 URL 可以转义 URL 编码?它最终可能是荒谬的,但它始终是合法的,并且永远不会是 JS 链接或对 DOM 有任何副作用)
    【解决方案3】:

    您的正则表达式正在寻找 http 或 https 的 url。该表达式似乎相对安全,因为 in 不会检测到任何不是 url 的内容。

    XSS 漏洞来自将 url 转义为 html 参数。这意味着确保 url 不能过早地转义 url 字符串,然后向@Rook 提到的 html 标记添加额外的属性。

    所以我真的想不出如何按照@tobyodavies 的建议使用以下代码执行 XSS 攻击,但没有 urlencode,它会做其他事情:

    $pattern = '(?xi)\b((?:(http)s?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))';
    return preg_replace_callback("#$pattern#i", function($matches) {
        $input = $matches[0];
        $url = $matches[2] == 'http' ? $input : "http://$input";
        return '<a href="' . htmlspecialchars($url) . '" rel="nofollow" target="_blank">' . "$input</a>";
    }, $str); 
    

    请注意,我还添加了一个小快捷方式来检查 http 前缀。

    现在您生成的锚链接是安全的。

    但是,您还应该清理其余文本。我想您根本不想允许任何 html 并将所有 html 显示为明文。

    【讨论】:

    • 谢谢@d_inevitable。我希望你能看看这个,因为原来的 linkify 函数是你的。不幸的是,当我尝试上述操作时(在修复 $mathes --> $matches 上的错字之后),linkify 函数不再起作用。例如,当我单击输入的“www.google.com”文本时,链接会转到mysite.com/directory/http%3A%2F%2Fwww.google.com。这是因为原始 $pattern 正则表达式不再能够匹配 urlencoded 字符吗?由于用户生成的内容在双引号内,您认为原始的 'linkify' 功能是否足够?
    • 是的,原件就足够了,但效率较低。你是否也像我一样改变了模式? http 周围的括号:(http).
    • 其实我发现了这个问题。它带有不应采用协议的 url 编码。将进行编辑以使其正常工作。
    • 是的,我尝试了新旧版本,但不幸的是,当我尝试使用 urlencode 时,它​​们都破坏了 linkify 功能。
    • 好的,urlencode 不相关。我被误导使用它,但它只是编码 url 查询字符串参数,这不是你的意图。所以把它拿出来是安全的。 htmlspecialchars,将完成这项工作。
    【解决方案4】:

    首先,PHP 文档 states htmlspecialchars 只转义 " '&' (和号) 变成 '&' 当 ENT_NOQUOTES 未设置时,'"'(双引号)变为 '"'。 仅当设置了 ENT_QUOTES 时,“'”(单引号)才变为 '''(或 ')。 '' (大于)变成 '>' "。javascript: 仍然在常规编程中使用,所以为什么 : 没有被转义是我无法理解的。

    其次,如果 !html 只期望您认为将被输入的字符,而不是那些可以输入并被视为有效的字符的表示。 utf-8 character set 和其他所有字符集都支持同一字符的多种表示形式。此外,您的虚假陈述允许 0-9 和 a-z,因此您仍然需要担心base64 characters。我认为你的代码是一个很好的尝试,但它需要大量的改进。那或者你可以使用htmlpurifier,人们仍然可以绕过它。我确实认为您在 htmlspecialchars 中设置字符集真是太棒了,因为大多数程序员不明白他们为什么要这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-30
      • 2022-01-08
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多