构建链接时缓解xss攻击

时间:2012-04-25 15:57:09

标签: php security xss linkify

我不久前发布了这个问题,它非常适合从用户生成的帖子中查找和“链接”链接。 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>";

?>

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

4 个答案:

答案 0 :(得分:1)

在进入数据库之前,首先必须从不转义数据,这是非常严重的错误。这不仅不安全,而且会破坏功能。链接字符串的值是数据损坏并影响字符串比较。这种方法不安全,因为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不受干扰。

答案 1 :(得分:1)

因为数据是进入属性的,所以它应该是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>";

但没有我知道关心的浏览器,因此没有人这样做,听起来你可能已经做了这一步而且想要这样做两次

答案 2 :(得分:0)

您的正则表达式正在查找http或https的网址。该表达似乎相对安全,因为没有检测到任何不是网址的内容。

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显示为明文。

答案 3 :(得分:0)

首先,由于PHP文档states htmlspecialchars只能转义 “'&amp;' (&符号)成为'&amp;'     '''(双引号)在未设置ENT_NOQUOTES时变为'''。     “'”(单引号)仅在设置ENT_QUOTES时变为'''(或')。     '&LT;' (小于)成为'&lt;'     '&GT;' (大于)成为'&gt;' “.javascript:仍然用于常规编程,所以为什么:没有转义是超出我的。

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