我不久前发布了这个问题,它非常适合从用户生成的帖子中查找和“链接”链接。 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专业知识的人来确认这一点。谢谢!
答案 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中设置字符集真是太棒了,因为大多数程序员都不明白为什么他们应该这样做。