Drupal 使用正则表达式过滤 XSS。什么可以绕过它?

信息安全 xss html 德鲁巴
2021-08-30 14:16:23

Drupal 使用正则表达式针对 XSS 攻击过滤 HTML 字符串:http: //api.drupal.org/api/drupal/includes%21common.inc/function/filter_xss/7

然而,很多人都知道,HTML 不能用 regex 解析

这让我觉得这个filter_xss函数可以让一些无效的 HTML 在其中传递一个script标签,因此是一个安全漏洞。

但是我对正则表达式的效率不够。也许有人可以找到通过的东西?如果有,我会制作一个补丁来使用simplexml(或者如果有更好的东西)而不是正则表达式。

FWIW,这是函数的代码:

function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
  // Only operate on valid UTF-8 strings. This is necessary to prevent cross
  // site scripting issues on Internet Explorer 6.
  if (!drupal_validate_utf8($string)) {
    return '';
  }
  // Store the text format.
  _filter_xss_split($allowed_tags, TRUE);
  // Remove NULL characters (ignored by some browsers).
  $string = str_replace(chr(0), '', $string);
  // Remove Netscape 4 JS entities.
  $string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);

  // Defuse all HTML entities.
  $string = str_replace('&', '&', $string);
  // Change back only well-formed entities in our whitelist:
  // Decimal numeric entities.
  $string = preg_replace('/&#([0-9]+;)/', '&#\1', $string);
  // Hexadecimal numeric entities.
  $string = preg_replace('/&#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $string);
  // Named entities.
  $string = preg_replace('/&([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);

  return preg_replace_callback('%
    (
    <(?=[^a-zA-Z!/])  # a lone <
    |                 # or
    <!--.*?-->        # a comment
    |                 # or
    <[^>]*(>|$)       # a string that starts with a <, up until the > or the end of the string
    |                 # or
    >                 # just a >
    )%x', '_filter_xss_split', $string);
}

这个函数使用_filter_xss_split

function _filter_xss_split($m, $store = FALSE) {
  static $allowed_html;

  if ($store) {
    $allowed_html = array_flip($m);
    return;
  }

  $string = $m[1];

  if (substr($string, 0, 1) != '<') {
    // We matched a lone ">" character.
    return '&gt;';
  }
  elseif (strlen($string) == 1) {
    // We matched a lone "<" character.
    return '&lt;';
  }

  if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
    // Seriously malformed.
    return '';
  }

  $slash = trim($matches[1]);
  $elem = &$matches[2];
  $attrlist = &$matches[3];
  $comment = &$matches[4];

  if ($comment) {
    $elem = '!--';
  }

  if (!isset($allowed_html[strtolower($elem)])) {
    // Disallowed HTML element.
    return '';
  }

  if ($comment) {
    return $comment;
  }

  if ($slash != '') {
    return "</$elem>";
  }

  // Is there a closing XHTML slash at the end of the attributes?
  $attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist, -1, $count);
  $xhtml_slash = $count ? ' /' : '';

  // Clean up attributes.
  $attr2 = implode(' ', _filter_xss_attributes($attrlist));
  $attr2 = preg_replace('/[<>]/', '', $attr2);
  $attr2 = strlen($attr2) ? ' ' . $attr2 : '';

  return "<$elem$attr2$xhtml_slash>";
}
2个回答

请参阅Drupal 的 filter_xss 是否足以过滤 HTML?,其中有一些关于 Drupal 的安全性的讨论filter_xss()请务必阅读 Mike Samuel 的分析,其中指出了filter_xss(). 我不知道您是否会将它们准确地归类为漏洞,但它们是设计缺陷/缺陷,可能会导致filter_xss()效率低于开发人员的预期。


的开发人员文档filter_xss()非常糟糕。

总共有两句话:“过滤 HTML 以防止跨站点脚本 (XSS) 漏洞。” 和“对包含文本的标记使用 check_markup 或 filter_xss。”。当文档没有解释如何正确使用filter_xss()时,如果开发人员未能正确使用它,您不应该感到惊讶。这可能导致漏洞,例如 Rook 所识别的那种漏洞。

(Drupal 也有一个名为以安全方式处理文本的文档,但它甚至没有提到filter_xss()。)


我还建议任何打电话的人都filter_xss()应该确保不要将其包含!--在允许的标签列表中。验证评论的代码(如果您添加!--到允许的标签列表中,则启用)对我来说看起来超级粗略:它没有做任何事情来验证评论的内容,直觉上感觉它不可能安全的。

XSS 是一个输出问题,没有什么神奇的功能可以阻止所有的 XSS 漏洞。所有漏洞的根源在于以一种从未想过的方式使用功能。

这两个很明显:

print "<script>".filter_xss($_GET['still_xss1'])."</script>"; 
print "<a href=".filter_xss($_GET['still_xss2']).">xss</a>";

概念验证:

?still_xss1=alert(1)
?still_xss2=javascript:alert(1)

看起来你也可以注入事件处理程序:' onclick=alert(1) ',虽然我还没有尝试过......