目前我们的项目中存在非常严重的 XSS 安全漏洞。作为一个合格的 Web 开发工程师,必须遵循一个安全原则:
- 永远不要信任用户提交的数据
XSS 也称跨站脚本攻击 (Cross Site Scripting),恶意攻击者往 Web 页面里插入恶意 JavaScript 代码,当用户浏览该页之时,嵌入其中 Web 里面的 JavaScript 代码会被执行,从而达到恶意攻击用户的目的。
一种比较常见的 XSS 攻击是 Cookie 窃取。我们都知道网站是通过 Cookie 来辨别用户身份的,一旦恶意攻击者能在页面中执行 JavaScript 代码,他们即可通过 JavaScript 读取并窃取你的 Cookie,拿到你的 Cookie 以后即可伪造你的身份登录网站。(扩展阅读 —— IBM 文档库:跨站点脚本攻击深入解析 )
有两种方法可以避免 XSS 攻击:
第一种,对用户提交的数据进行过滤
第二种,Web 网页显示时对数据进行特殊处理,一般使用 htmlspecialchars() 输出。
XSS 也称跨站脚本攻击 (Cross Site Scripting),恶意攻击者往 Web 页面里插入恶意 JavaScript 代码,当用户浏览该页之时,嵌入其中 Web 里面的 JavaScript 代码会被执行,从而达到恶意攻击用户的目的。
一种比较常见的 XSS 攻击是 Cookie 窃取。我们都知道网站是通过 Cookie 来辨别用户身份的,一旦恶意攻击者能在页面中执行 JavaScript 代码,他们即可通过 JavaScript 读取并窃取你的 Cookie,拿到你的 Cookie 以后即可伪造你的身份登录网站。(扩展阅读 —— IBM 文档库:跨站点脚本攻击深入解析 )
有两种方法可以避免 XSS 攻击:
- 第一种,对用户提交的数据进行过滤;
- 第二种,Web 网页显示时对数据进行特殊处理,一般使用
htmlspecialchars()
输出。
Laravel 的 Blade 语法 {{ }} 会自动调用 PHP htmlspecialchars 函数来避免 XSS 攻击
Blade 的 {!! !!} 语法是直接数据,不会对数据做任何处理。在我们这种场景下,因为业务逻辑的特殊性,第二种方法不适用,我们将使用第一种方法,对用户提交的数据进行过滤来避免 XSS 攻击。
HTMLPurifier#
HTMLPurifier 本身就是一个独立的项目,运用『白名单机制』对 HTML 文本信息进行 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’,
当用户提交时:
会被解析为:
以下内容因为未指定会被过滤:
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”
- 配置 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);
}
}
HTMLPurifier#
HTMLPurifier 本身就是一个独立的项目,运用『白名单机制』对 HTML 文本信息进行 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);
}
}