PHP防止XSS攻击之过滤、验证和转义之转义的例子
本文章来为各位介绍一篇关于PHP防止XSS攻击之过滤、验证和转义之转义的例子,希望这篇教程能够帮助到各位朋友。
PHP 转义实现
把输出渲染成网页或API响应时,一定要转义输出,这也是一种防护措施,能避免渲染恶意代码,造成XSS攻击,还能防止应用的用户无意中执行恶意代码。
我们可以使用前面提到的htmlentities函数转移输出,该函数的第二个参数一定要使用ENT_QUOTES,让这个函数转义单引号和双引号,而且,还要在第三个参数中指定合适的字符编码(通常是UTF-8),下面的例子演示了如何在渲染前转义HTML输出:
$output = '
<script>alert(“欢迎来到Laravel学院!")</script>
';echo htmlentities($output, ENT_QUOTES, ‘UTF-8');
如果不转义直接输出,会弹出提示框:
alert
转义之后输出变成:
<script>alert("欢迎访问Laravel学院!");</script>
现代PHP支持许多模板引擎,这些模板引擎在底层已经为了做好了转义处理,比如现在流行的twig/twig和smarty/smarty都会自动转义输出。这种默认处理方式很赞,为PHP Web应用提供了有力的安全保障。
Blade 模板引擎避免XSS攻击原理
Laravel使用的模板引擎是Blade,关于Blade的使用可以参考其官方文档,这里我们简单探讨下Laravel底层如何对输出进行转义处理。
一般我们在Laravel中返回视图内容会这么做:
return view(’test’, [‘data’=>$data]);
这是一个很简单的例子,意味着我们会在resources/views目录下找到test.blade.php视图文件,然后将$data变量传入其中,并将最终渲染结果作为响应的内容返回给用户。那么这一过程经历了哪些底层源码的处理,如果$data变量中包含脚本代码(如JavaScript脚本),又该怎么去处理呢?接下来我们让来一窥究竟。
首先我们从辅助函数view入手,当然这里我们也可以使用View:make,但是简单起见,我们一般用view函数,该函数定义在Illuminate\Foundation\helpers.php文件中:
function view($view = null, $data = [], $mergeData = [])
{
$factory = app(ViewFactory::class);
if (func_num_args() === 0) {
return $factory;
}
return $factory->make($view, $data, $mergeData);
}
该函数中的逻辑是从容器中取出视图工厂接口ViewFactory对应的实例$factory(该绑定关系在Illuminate\View\ViewServiceProvider的register方法中注册,此外这里还注册了模板引擎解析器EngineResolver,包括PhpEngine和载入BladeCompiler的CompilerEngine,以及视图文件查找器FileViewFinder,一句话,这里注册了视图解析所需的所有服务),如果传入了参数,则调用$factory上的make方法:
public function make($view, $data = [], $mergeData = [])
{
if (isset($this->aliases[$view])) {
$view = $this->aliases[$view];
}
$view = $this->normalizeName($view);
$path = $this->finder->find($view);
$data = array_merge($mergeData, $this->parseData($data));
$this->callCreator($view = new View($this, $this->getEngineFromPath($path), $view, $path, $data));
return $view;
}
这个方法位于Illuminate\View\Factory,这里所做的事情是获取视图文件的完整路径,合并传入变量,$this->getEngineFromPath会通过视图文件后缀获取相应的模板引擎,比如我们使用.blade.php结尾的视图文件则获得到的是CompilerEngine(即Blade模板引擎),否则将获取到PhpEngine,然后我们根据相应参数实例化View(Illuminate\View\View)对象并返回。需要注意的是View类中重写了__toString方法:
public function __toString()
{
return $this->render();
}
所以当我们打印$view实例的时候,实际上会调用View类的render方法,所以下一步我们理所应当研究render方法做了些什么:
public function render(callable $callback = null)
{
try {
$contents = $this->renderContents();
$response = isset($callback) ? call_user_func($callback, $this, $contents) : null;
// Once we have the contents of the view, we will flush the sections if we are
// done rendering all views so that there is nothing left hanging over when
// another view gets rendered in the future by the application developer.
$this->factory->flushSectionsIfDoneRendering();
return ! is_null($response) ? $response : $contents;
} catch (Exception $e) {
$this->factory->flushSections();
throw $e;
} catch (Throwable $e) {
$this->factory->flushSections();
throw $e;
}
}
这里重点是$this->renderContents()方法,我们继续深入研究View类中的renderContents方法:
protected function renderContents()
{
// We will keep track of the amount of views being rendered so we can flush
// the section after the complete rendering operation is done. This will
// clear out the sections for any separate views that may be rendered.
$this->factory->incrementRender();
$this->factory->callComposer($this);
$contents = $this->getContents();
// Once we've finished rendering the view, we'll decrement the render count
// so that each sections get flushed out next time a view is created and
// no old sections are staying around in the memory of an environment.
$this->factory->decrementRender();
return $contents;
}
我们重点关注$this->getContents()这里,进入getContents方法:
protected function getContents()
{
return $this->engine->get($this->path, $this->gatherData());
}
我们在前面已经提到,这里的$this->engine对应CompilerEngine(Illuminate\View\Engines\CompilerEngine),所以我们进入CompilerEngine的get方法:
public function get($path, array $data = [])
{
$this->lastCompiled[] = $path;
// If this given view has expired, which means it has simply been edited since
// it was last compiled, we will re-compile the views so we can evaluate a
// fresh copy of the view. We'll pass the compiler the path of the view.
if ($this->compiler->isExpired($path)) {
$this->compiler->compile($path);
}
$compiled = $this->compiler->getCompiledPath($path);
// Once we have the path to the compiled file, we will evaluate the paths with
// typical PHP just like any other templates. We also keep a stack of views
// which have been rendered for right exception messages to be generated.
$results = $this->evaluatePath($compiled, $data);
array_pop($this->lastCompiled);
return $results;
}
同样我们在之前提到,CompilerEngine使用的compiler是BladeCompiler,所以$this->compiler也就是Blade编译器,我们先看$this->compiler->compile($path);这一行(首次运行或者编译好的视图模板已过期会进这里),进入BladeCompiler的compile方法:
public function compile($path = null)
{
if ($path) {
$this->setPath($path);
}
if (! is_null($this->cachePath)) {
$contents = $this->compileString($this->files->get($this->getPath()));
$this->files->put($this->getCompiledPath($this->getPath()), $contents);
}
}
这里我们做的事情是先编译视图文件内容,然后将编译好的内容存放到视图编译路径(storage\framework\views)下对应的文件(一次编译,多次运行,以提高性能),这里我们重点关注的是$this->compileString方法,该方法中使用了token_get_all函数将视图文件代码分割成多个片段,如果片段是数组的话则循环调用$this->parseToken方法:
protected function parseToken($token)
{
list($id, $content) = $token;
if ($id == T_INLINE_HTML) {
foreach ($this->compilers as $type) {
$content = $this->{"compile{$type}"}($content);
}
}
return $content;
}
来到这里,我们已经很接近真相了,针对HTML代码(含Blade指令代码),循环调用compileExtensions、compileStatements、compileComments和compileEchos方法,我们重点关注输出方法compileEchos,Blade引擎默认提供了compileRawEchos、compileEscapedEchos和compileRegularEchos三种输出方法,对应的指令分别是{!! !!}、{{{ }}}和{{ }},顾名思义,compileRawEchos对应的是原生输出:
protected function compileRawEchos($value)
{
$pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->rawTags[0], $this->rawTags[1]);
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3];
return $matches[1] ? substr($matches[0], 1) : 'compileEchoDefaults($matches[2]).'; ?>'.$whitespace;
};
return preg_replace_callback($pattern, $callback, $value);
}
即Blade视图中以{!! !!}包裹的变量会原生输出HTML,如果要显示图片、链接,推荐这种方式。
{{{}}}对应的CompileEscapedEchos,这个在Laravel 4.2及以前版本中用于转义,现在已经替换成了{{}},即调用compileRegularEchos方法:
protected function compileRegularEchos($value)
{
$pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]);
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3];
$wrapped = sprintf($this->echoFormat, $this->compileEchoDefaults($matches[2]));
return $matches[1] ? substr($matches[0], 1) : ''.$whitespace;
};
return preg_replace_callback($pattern, $callback, $value);
}
其中$this->echoFormat对应e(%s),无独有偶,compileEscapedEchos中也用到这个方法:
protected function compileEscapedEchos($value)
{
$pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]);
$callback = function ($matches) {
$whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3];
return $matches[1] ? $matches[0] : 'compileEchoDefaults($matches[2]).'); ?>'.$whitespace;
};
return preg_replace_callback($pattern, $callback, $value);
}
辅助函数e()定义在Illuminate\Support\helpers.php中:
function e($value)
{
if ($value instanceof Htmlable) {
return $value->toHtml();
}
return htmlentities($value, ENT_QUOTES, 'UTF-8', false);
}
其作用就是对输入的值进行转义。
经过这样的转义,视图中的{{ $data }}或被编译成,最终如何将$data传入视图输出,我们再回到CompilerEngine的get方法,看这一段:
$results = $this->evaluatePath($compiled, $data);
evaluatePath中传入了编译后的视图文件路径和传入的变量$data,该方法定义如下:
protected function evaluatePath($__path, $__data)
{
$obLevel = ob_get_level();ob_start();
extract($__data, EXTR_SKIP);
// We'll evaluate the contents of the view inside a try/catch block so we can
// flush out any stray output that might get out before an error occurs or
// an exception is thrown. This prevents any partial views from leaking.
try {
include $__path;
} catch (Exception $e) {
$this->handleViewException($e, $obLevel);
} catch (Throwable $e) {
$this->handleViewException(new FatalThrowableError($e), $obLevel);
}
return ltrim(ob_get_clean());
}
这里面调用了PHP系统函数extract将传入变量从数组中导入当前符号表(通过include $__path引入),其作用也就是将编译后视图文件中的变量悉数替换成传入的变量值(通过键名映射)。
好了,这就是Blade视图模板从渲染到输出的基本过程,可以看到我们通过{{}}来转义输出,从而达到避免XSS攻击的目的。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









ログイン画面に「組織から PIN の変更を求められています」というメッセージが表示されます。これは、個人のデバイスを制御できる組織ベースのアカウント設定を使用しているコンピューターで PIN の有効期限の制限に達した場合に発生します。ただし、個人アカウントを使用して Windows をセットアップした場合、エラー メッセージは表示されないのが理想的です。常にそうとは限りませんが。エラーが発生したほとんどのユーザーは、個人アカウントを使用して報告します。私の組織が Windows 11 で PIN を変更するように要求するのはなぜですか?アカウントが組織に関連付けられている可能性があるため、主なアプローチはこれを確認することです。ドメイン管理者に問い合わせると解決できます。さらに、ローカル ポリシー設定が間違っていたり、レジストリ キーが間違っていたりすると、エラーが発生する可能性があります。今すぐ

Windows 11 では、新鮮でエレガントなデザインが前面に押し出されており、最新のインターフェイスにより、ウィンドウの境界線などの細部をカスタマイズして変更することができます。このガイドでは、Windows オペレーティング システムで自分のスタイルを反映した環境を作成するのに役立つ手順について説明します。ウィンドウの境界線の設定を変更するにはどうすればよいですか? + を押して設定アプリを開きます。 Windows [個人用設定] に移動し、[色の設定] をクリックします。ウィンドウの境界線の色の変更設定ウィンドウ 11" width="643" height="500" > [タイトル バーとウィンドウの境界線にアクセント カラーを表示する] オプションを見つけて、その横にあるスイッチを切り替えます。 [スタート] メニューとタスク バーにアクセント カラーを表示するにはスタート メニューとタスク バーにテーマの色を表示するには、[スタート メニューとタスク バーにテーマを表示] をオンにします。

デフォルトでは、Windows 11 のタイトル バーの色は、選択したダーク/ライト テーマによって異なります。ただし、任意の色に変更できます。このガイドでは、デスクトップ エクスペリエンスを変更し、視覚的に魅力的なものにするためにカスタマイズする 3 つの方法について、段階的な手順を説明します。アクティブなウィンドウと非アクティブなウィンドウのタイトル バーの色を変更することはできますか?はい、設定アプリを使用してアクティブなウィンドウのタイトル バーの色を変更したり、レジストリ エディターを使用して非アクティブなウィンドウのタイトル バーの色を変更したりできます。これらの手順を学習するには、次のセクションに進んでください。 Windows 11でタイトルバーの色を変更するにはどうすればよいですか? 1. 設定アプリを使用して + を押して設定ウィンドウを開きます。 Windows「個人用設定」に進み、

Windows インストーラー ページに「問題が発生しました」というメッセージとともに「OOBELANGUAGE」というメッセージが表示されますか?このようなエラーが原因で Windows のインストールが停止することがあります。 OOBE とは、すぐに使えるエクスペリエンスを意味します。エラー メッセージが示すように、これは OOBE 言語の選択に関連する問題です。心配する必要はありません。OOBE 画面自体から気の利いたレジストリ編集を行うことで、この問題を解決できます。クイックフィックス – 1. OOBE アプリの下部にある [再試行] ボタンをクリックします。これにより、問題が発生することなくプロセスが続行されます。 2. 電源ボタンを使用してシステムを強制的にシャットダウンします。システムの再起動後、OOBE が続行されます。 3. システムをインターネットから切断します。 OOBE のすべての側面をオフライン モードで完了する

タスクバーのサムネイルは楽しい場合もありますが、気が散ったり煩わしい場合もあります。この領域にマウスを移動する頻度を考えると、重要なウィンドウを誤って閉じてしまったことが何度かある可能性があります。もう 1 つの欠点は、より多くのシステム リソースを使用することです。そのため、リソース効率を高める方法を探している場合は、それを無効にする方法を説明します。ただし、ハードウェアの仕様が対応可能で、プレビューが気に入った場合は、有効にすることができます。 Windows 11でタスクバーのサムネイルプレビューを有効にする方法は? 1. 設定アプリを使用してキーをタップし、[設定] をクリックします。 Windows では、「システム」をクリックし、「バージョン情報」を選択します。 「システムの詳細設定」をクリックします。 [詳細設定] タブに移動し、[パフォーマンス] の下の [設定] を選択します。 「視覚効果」を選択します

多くのユーザーはスマートウォッチを選ぶときにファーウェイブランドを選択しますが、その中でもファーウェイ GT3pro と GT4 は非常に人気のある選択肢であり、多くのユーザーはファーウェイ GT3pro と GT4 の違いに興味を持っています。 Huawei GT3pro と GT4 の違いは何ですか? 1. 外観 GT4: 46mm と 41mm、材質はガラスミラー + ステンレススチールボディ + 高解像度ファイバーバックシェルです。 GT3pro: 46.6mm および 42.9mm、材質はサファイアガラス + チタンボディ/セラミックボディ + セラミックバックシェルです。 2. 健全な GT4: 最新の Huawei Truseen5.5+ アルゴリズムを使用すると、結果はより正確になります。 GT3pro: ECG 心電図と血管と安全性を追加

Windows 11 のディスプレイ スケーリングに関しては、好みが人それぞれ異なります。大きなアイコンを好む人もいれば、小さなアイコンを好む人もいます。ただし、適切なスケーリングが重要であることには誰もが同意します。フォントのスケーリングが不十分であったり、画像が過度にスケーリングされたりすると、作業中の生産性が大幅に低下する可能性があるため、システムの機能を最大限に活用するためにカスタマイズする方法を知る必要があります。カスタム ズームの利点: これは、画面上のテキストを読むのが難しい人にとって便利な機能です。一度に画面上でより多くの情報を確認できるようになります。特定のモニターおよびアプリケーションにのみ適用するカスタム拡張プロファイルを作成できます。ローエンド ハードウェアのパフォーマンスの向上に役立ちます。画面上の内容をより詳細に制御できるようになります。 Windows 11の使用方法

画面の明るさは、最新のコンピューティング デバイスを使用する上で不可欠な部分であり、特に長時間画面を見る場合には重要です。目の疲れを軽減し、可読性を向上させ、コンテンツを簡単かつ効率的に表示するのに役立ちます。ただし、設定によっては、特に新しい UI が変更された Windows 11 では、明るさの管理が難しい場合があります。明るさの調整に問題がある場合は、Windows 11 で明るさを管理するすべての方法を次に示します。 Windows 11で明るさを変更する方法【10の方法を解説】 シングルモニターユーザーは、次の方法でWindows 11の明るさを調整できます。これには、ラップトップだけでなく、単一のモニターを使用するデスクトップ システムも含まれます。はじめましょう。方法 1: アクション センターを使用する アクション センターにアクセスできる
