XSS 安全漏洞 - HTMLPurifier
作为一个合格的 Web 开发工程师,必须遵循一个安全原则:
永远不要信任用户提交的数据。
有两种方法可以避免 XSS 攻击:
- 第一种,对用户提交的数据进行过滤;
- 第二种,Web 网页显示时对数据进行特殊处理,一般使用 htmlspecialchars() 输出。
Laravel 的 Blade 语法 {{ }}
会自动调用 PHP htmlspecialchars
函数来避免 XSS 攻击。但是因为我们支持 WYSIWYG 编辑器,我们使用的是 {!! !!}
来打印用户提交的话题内容:
<div class="topic-body">
{!! $topic->body !!}
</div>
Blade 的{!! !!}
语法是直接数据,不会对数据做任何处理。在我们这种场景下,因为业务逻辑的特殊性,第二种方法不适用,我们将使用第一种方法,对用户提交的数据进行过滤来避免 XSS 攻击。
接下来我们一起解决此问题。
HTMLPurifier
PHP 一个比较合理的解决方案是 HTMLPurifier 。HTMLPurifier 本身就是一个独立的项目,运用『白名单机制』对 HTML 文本信息进行 XSS 过滤。这种过滤机制可以有效地防止各种 XSS 变种攻击。只通过我们认为安全的标签和属性,对于未知的全部过滤。
『白名单机制』指的是使用配置信息来定义『HTML 标签』、『标签属性』和『CSS 属性』数组,在执行 clean()
方法时,只允许配置信息『白名单』里出现的元素通过,其他都进行过滤。
如配置信息:
'HTML.Allowed' => 'div,em,a[href|title|style],ul,ol,li,p[style],br',
'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family',
当用户提交时:
<a someproperty="somevalue" href="http://example.com" style="color:#ccc;font-size:16px">
文章内容<script>alert('Alerted')</script>
</a>
会被解析为:
<a href="http://example.com" style="font-size:16px">
文章内容
</a>
以下内容因为未指定会被过滤:
- someproperty 未指定的 HTML 属性
- color 未指定的 CSS 属性
- script 未指定的 HTML 标签
HTMLPurifier for Laravel 5
HTMLPurifier for Laravel 是对 HTMLPurifier 针对 Laravel 框架的一个封装。本章节中,我们将使用此扩展包来对用户内容进行过滤。
1. 安装 HTMLPurifier for Laravel 5
使用 Composer 安装:
$ composer require "mews/purifier:~2.0"
2. 配置 HTMLPurifier for Laravel 5
命令行下运行
$ php artisan vendor:publish --provider="Mews\Purifier\PurifierServiceProvider"
请将配置信息替换为以下:
config/purifier.php
<?php
return [
'encoding' => 'UTF-8',
'finalize' => true,
'cachePath' => storage_path('app/purifier'),
'cacheFileMode' => 0755,
'settings' => [
'user_topic_body' => [
'HTML.Doctype' => 'XHTML 1.0 Transitional',
'HTML.Allowed' => 'div,b,strong,i,em,a[href|title],ul,ol,ol[start],li,p[style],br,span[style],img[width|height|alt|src],*[style|class],pre,hr,code,h2,h3,h4,h5,h6,blockquote,del,table,thead,tbody,tr,th,td',
'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,margin,width,height,font-family,text-decoration,padding-left,color,background-color,text-align',
'AutoFormat.AutoParagraph' => true,
'AutoFormat.RemoveEmpty' => true,
],
],
];
配置里的 user_topic_body
是我们为话题内容定制的,配合 clean()
方法使用:
$topic->body = clean($topic->body, 'user_topic_body');
开始过滤
一切准备就绪,现在我们只需要在数据入库前进行过滤即可:
app/Observers/TopicObserver.php
<?php
namespace App\Observers;
use App\Models\Topic;
// creating, created, updating, updated, saving,
// saved, deleting, deleted, restoring, restored
class TopicObserver
{
public function saving(Topic $topic)
{
$topic->body = clean($topic->body, 'user_topic_body');
$topic->excerpt = make_excerpt($topic->body);
}
}
浏览器访问话题创建页面,并填入测试内容:
提交表单后的结果,只剩下正常内容,并且没有弹框:
数据库里可以看到我们提交的 XSS 注入内容已被过滤:
HTMLPurifier 把白名单里设定的内容通过,script 标签未设定,自动过滤掉。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: