クライアントサイドからサーバサイドまで破壊するテンプレートエンジンを利用した攻撃と対策- 3つのテンプレート -

自己紹介
•

Name : azu•

Twi+er : @azu_re•

Website: Web scratch, JSer.info

アジェンダ•テンプレートとは•テンプレートの種類(レベル)•テンプレートをコンパイルする場所と問題•

信頼できない入力ソース: テンプレート or データモデル•

Programming Language Templateの危険性

今日覚えること•

テンプレートエンジンには3つのレベルがある•

テンプレートエンジンのレベルとテンプレートをコンパイルする場所によって必要なセキュリティ対策は異なる•

テンプレートファイルはプログラムファイルである

DEMO: Server Side Template
Injec7onテンプレートエンジン•

テンプレートとデータモデル(パラメータ)を組み合わせてViewを作る処理を行うものをテンプレートエンジンと呼ぶ•

テンプレートの種類が多いが、今回はWeb template system(HTML)を中心に扱う

3種類のテンプレートエンジン•

表示とビジネスロジックを分けるのがテンプレートの目的•

テンプレートエンジンには3種類のレベル分けがあると考えられる

1•

Enforcing Strict Model-View Separa7on inTemplate Engines byTerence Parr1•

Web Content Management - O'Reilly Media

StringTemplateやANTLRの作者。30年以上パースについての研究をしている

3種類(レベル)のテンプレートエンジン•

Simple Token Replacement••

Limited Control Structures••

単純な置換: This is {{ name }}. => This is pen.

if文やfor文など制御構文が付いたもの

Programming Language•

プログラミング言語みたいなものSimple Token Replacement

Simple Token Replacement•

テンプレートとデータモデルが完全に分離されている状態•

単純な文字列を置換をするだけのテンプレート•

特定の用途にしか使えないように厳しく制限されている•

任意のコードは実行できないように制限されている状態

Limited Control Structures

Limited Control Structures•

テンプレートにも表示に関する制御構文を持つ•

if文やfor文などの制限されたコントールフロー(構造)をもつテンプレート•

logic less templateとも呼ばれる(持っているのは表示に関するロジックのみであるため)•

任意のコードは実行できないように制限されている状態•

evalを全く使ってないもの or evalを使っていても実行できるものをホワイリスト制限

Programming Language TemplateProgramming Language Template•

テンプレートにはビジネスロジックが書ける•

チューリング完全な言語•

何でもできる•

Evalを使っていてかつなにも制限していないテンプレートが言語になることもある
I would argue that PHP is a templa4ng syntax that grew into a
programming language.
— Web template system•

PHPは言語に進化したテンプレートエンジンとも考えられる

テンプレートと実装•

Simple Token Replacement•••単純な文字列置換

Limited Control Structures•

テンプレートエンジンっぽいテンプレートエンジン•

HandlebarsやLiquidなど制限を意識して作られたもの•

Sandboxモードが有効状態のSmartyやTwigなど(SandboxなしならProgramming Language)

Programming Language•

柔軟なテンプレートエンジン、言語、UIフレームワーク•

HTMLやPHP、各種UIフレームワークなどテンプレートのレベル
•

↑ほど厳密

•

Simple Token Replace••

Limited control structures••

Liquid, Handlebars, StringsTemplate

Programming Language Template••

replace関数など単純な置換に近いもの

Nunjucks, Lodash, template, Pug, React, Angular, Vue...

↓ほど柔軟テンプレートエンジンって言っても…
•

一言にテンプレートエンジンって言っても機能の幅が広い•

テンプレートエンジンのレベルごとにできることとできないことがある•

テンプレートエンジンのレベルごとにセキュリティの考え方も異なる•攻撃するときにもできること、できないことが変わってくるためテンプレートをコンパイルする場所と入力ソース

テンプレートをどこでコンパイルするかコンテキスト: ウェブテンプレートなので、どこかでテンプレートをHTMLにコンパイルして、ブラウザでレンダリングするのは同じ
•

•

•

ローカル
•

手元でテンプレートをHTMLにコンパイルして、その結果を使う•

事前にコンパイルしてその結果を使うので、オフラインコンパイルやAOT(Ahead Of Time)コンパイルと呼ばれるブラウザ•

テンプレートとデータモデルを受け取りブラウザでコンパイルしてレンダリングする•クライアントサイドレンダリング

サーバ•

テンプレートとデータモデルを使ってサーバでコンパイルしたHTMLをレスポンスとして返す•

サーバサイドレンダリング入力ソース x コンパイルする場所 x テンプレートのレベル = 安全性

入力ソース•テンプレートエンジンは次の２つの入力から結果を出力する••

View = テンプレート + データモデル

入力として テンプレートそのもの とデータモデル(パラメータ) がある•

ユーザー入力(つまり任意のもの)として受け取る入力ソースによって安全レベルは変わる入力ソース: データモデル

入力ソース: データモデル•

ユーザー入力(信用できない)としてデータモデルを受け取るケース••

テンプレートは信用できるという前提

多くのテンプレートエンジンはデータモデルを安全に扱う方法を持っている•

簡単に言えばHTMLタグにはならないよ
うにエスケープする方法を持ってる•

そのため、データモデルに悪意あるものがあっても、結果のViewは安全を担保できるユーザー入力としてデータモデルを受け取って、テンプレートをコンパイルするケース
ローカルブラウザ

サーバ

Simple Token Replace ✔

✔

✔

Limited controlstructures

✔

✔

✔

Programming
Language Template✔

✔

✔

✔: エスケープすれば問題ないデータモデルのエスケープ
Template:
Hello {{ name }}!

DataModel:{"name": "<script>alert(1)</script>"}

View(結果):
Hello &lt;script&gt;alert(&#39;1&#39;)&lt;/script&gt;!

テンプレートエンジンのエスケープ•

多くのテンプレートエンジンはデータモデル(パラメータ)の展開はデフォルトで安全•{{ name }} で name にHTMLが入ってもエスケープして出力する•=> データモデルにHTMLが入ってても、HTMLではなくただの文字列として扱われる•

細かく言うと、パラメータが入る場所によってエスケープ方法は異なる•

そのため、Contextual Escaping(href属性はjavascript:もエスケープするなど)をサポートしているかによって安全性に多少のブレがある入力ソース: テンプレート

入力ソース: テンプレート••

入力ソースがテンプレートそのもの場合•ユーザーにテンプレートの構文を書かせて、それを扱う場合•

ブログのテーマなど

テンプレートそのものが安全にコンパイルできるかが安全性に影響するユーザー入力がテンプレート
ローカル

ブラウザ

サーバ

Simple Token
Replace

Safe

Safe

Safe

Limited control
structures

Safe

Safe

Safe

Programming
Language
Template⚠Danger⚠Danger⚠Danger

ユーザー入力として受け取ったテンプレートをコンパイル•

テンプレートレベルがProgramming Language Templateは安全にコンパイルするのが難しい•

高度なテンプレートはプログラミング言語そのもの•

コンパイル時に任意のコードが実行できてしまう =>⚠DangerProgramming LanguageTemplateをコンパイルする危険例: ローカル•

メールでマクロ付きのWordファイルを開いて、ローカルで任意のプログラムが実行されてしまうケース•

マクロはプログラムそのもの•

偽装メールが再び拡散、不正マクロを仕込んだ添付ファ
イルでマルウエア感染：マクロウイルスの再来？ - ＠IT•

デフォルトではマクロは無効化されており、オプトインで有効化になっている•

Oﬃce ドキュメントのマクロを有効または無効にする Oﬃce サポート

Programming Language Templateをコンパイルする危険例: ブラウザ•

意図的にサーバサイドでテンプレートを作成して、クライアントサイドでレンダリングするケースは多くない••

最初からサーバサイドでコンパイルしてHTMLを返せばいいだけであるため

Vue.js や AngularJS(1.x) と 古典的なサーバサイドレンダリングの組み合わせでこの問題が発生しやすい•一部だけサーバサイドでレンダリングして、クライアントサイドでレンダリング済みのHTMLを元にSPAするケース•

検索ワード: Client side Template Injec1on

Vue + Client Side Template Injec4on•

h#ps://vue-client-side-template-injec5on-example.azu.now.sh/?
name=xxx•

クエリのnameをサーバサイドレンダリングして、クライアントサイドはVueでアプリを作るイメージ

const express = require('express');
const escapeHTML = require('escape-html');const app = express();
app.get('/', (req, res) => {res.set('Content-Type', 'text/html');const name = req.query.nameres.status(200).send(`<div id="app"><h1>Hello ${escapeHTML(name)}</h1></div></footer><script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script><script>
new Vue({el: '#app'});</script>`);});
module.exports = app;

Vue + Client Side Template Injec4on•

ユーザー入力(?name)HTMLエスケープはされているのでサーバサイドレンダリングとしては問題ない••h)ps://vue-client-side-template-injec;on-example.azu.now.sh/?
name=%3Cscript%3Ealert(1)%3C/script%3E

サーバサイドがレンダリングした結果をVueがクライアントサイドレンダリングしている•h)ps://vue-client-side-template-injec;on-example.azu.now.sh/?
name=%7B%7Bthis.constructor.constructor(%27alert(%22foo%22)%27)()%7D%7D•Vueをmountするときにテンプレート構文{{ expression }} が入ってると、それをテンプレートとして評価してしまう！

h"ps://vue-client-side-template-injec4on-example.azu.now.sh/?
name=%7B%7Bthis.constructor.constructor(%27alert(%22foo%22)%27)()%7D%7D

Client Side Template Injec1onの原因•?name={{ this.constructor.constructor('alert(1)')() }} のようにテンプレート構文を入れてる•

サーバサイドでレンダリングすると {{, (, ), }} などはHTMLタグではないエスケープされない•

次のようなVueのテンプレートが生成され、Vueによってテンプレートとして評価され、任意のJavaScriptが実行される<div id="app"><h1>Hello {{this.constructor.constructor(&#39;alert(&quot;foo&quot;)&#39;)()}}</h1></div>

Client Side Template Injec1onの対処••

テンプレート と データモデル が混在してしまったのが原因•

サーバサイドで意図せず ユーザー入力を元に テンプレートを作成してしまった•

ユーザー入力は データモデル(パラメータ) として扱うように統一すればいい

サーバでは初期Stateをデータモデルとしてクライアントに伝える•

要はJSONとしてHTML data属性に書き込み、クライアント側でテンプレートとデータモデルを使ってレンダリングする•

ﬁx(server): separate ini:al state for vue by azu · Pull Request #1 · azu/vue-clientside-template-injec:on-example

Fix: Vue + Client Side Template Injec7on
const express = require('express');
const escapeHTML = require('escape-html');const app = express();
app.get('/', (req, res) => {res.set('Content-Type', 'text/html');const name = req.query.nameres.status(200).send(`<div id="app"><h1>Hello {{name}}</h1></div><script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script><script data-initial-state="${escapeHTML(JSON.stringify({ name }))}"></script><script>
new Vue({el: '#app',data: JSON.parse(document.querySelector("[data-initial-state]").dataset.initialState)});</script>`);});
module.exports = app;

DEMOG: Client Side Template
Injec7onProgramming Language Templateをコンパイルする危険例: サーバ

Programming Language Templateをコンパイルする危険例: サーバ•一番ヤバい•信頼できないテンプレートをコンパイルするだけで、サーバで任意のコード実行(Remote Code Execu/on)ができてしまう•

検索ワード: Server side Template Injec3on•

Server-Side Template Injec/on | Blog - PortSwigger•

Server-Side Template Injec/on - DARK MATTER

lodash.template + Server Side Template
Injec7on•h#ps://server-side-lodash-template-injec6on.azu.now.sh/?
name=xxx•

クエリのnameをサーバサイドレンダリングしてHTMLを返すシンプルなアプリ•

テンプレートエンジンにlodash.templateを使ってるconst express = require('express');
const _ = require('lodash');const escapeHTML = require('escape-html');const app = express();
app.get('/', (req, res) => {res.set('Content-Type', 'text/html');const name = req.query.name// Create a template from user input
const compiled = _.template("Hello " + escapeHTML(name) + ".");
res.status(200).send(compiled());});
module.exports = app;

lodash.template + Server Side Template
Injec7on•

色々使い方がおかしい•

ユーザー入力からテンプレートを作成してしまってる•

lodash.templateは任意のJavaScriptコードを実行できる•

Remote Code Execu9onが可能

h"ps://server-side-lodash-template-injec5on.azu.now.sh/?name=${JSON.stringify(process.env)}
注意: 今は修正されたものをデプロイしてありますlodash.template + Server Side Template
Injec7on•_.template("Hello ${JSON.stringify(process.env)}.");••

process.env を返してくれる

Fix: ユーザー入力からテンプレートを作成しない•

ﬁx: ﬁx server side template injec5on by azu · Pull Request #1 · azu/server-sidelodash-template-injec5on•

テンプレートとデータモデルは分ける•

ユーザー入力はデータモデル(パラメータ)に移動する

FIX: lodash.template + Server Side Template
Injec:onconst express = require('express');
const _ = require('lodash');const app = express();
app.get('/', (req, res) => {res.set('Content-Type', 'text/html');const name = req.query.name
const compiled = _.template("Hello <%- name %>.");
res.status(200).send(compiled({ name }));});
module.exports = app;Server Side Template Injec2on••

ユーザー入力からテンプレートを作ってしまうと問題が起きる•

SQL Injec+onとよく似ている•

基本的な対策はユーザー入力をテンプレートにわたすときにプレースホルダーを使う！

事例: 有名なサイトでも結構やらかしている. $10,000 > ぐらいの報奨金がでる•

Yahoo! RCE via Spring Engine SSTI – ∞ Growing Web Security Blog•

Artsploit: [demo.paypal.com] Node.js code injec+on (RCE)•#125980 uber.com may RCE by Flask Jinja2 Template Injec+on•#423541 H1514 Server Side Template Injec+on in Return Magic email templates?

Template Injec-on•

多くのProgramming Language Templateの攻撃方法が確立されている•

Tplmapなどの検査ツールもある(テンプレート構文っぽい文字列をクエリに入れまくる)•

参考: DiogoMRSilva/websitesVulnerableToSSTI: Simple websites
vulnerable to Server Side Template InjecCons(SSTI)EJS<%% global.process.mainModule.require('fs').readFileSync('/etc/passwd').toString() %%>Nunjucks{% global.process.mainModule.require('fs').readFileSync('/etc/passwd').toString() }Slim#{ File.open('/etc/passwd').read }erb<%= File.open('/etc/passwd').read %>Vue(SSR){{ global.process.mainModule.require('fs').readFileSync('/etc/passwd').toString() }}User Deﬁned Template と Server Side
Template Injec6on•

ユーザーがテンプレートを書ける機能を提供している場合•

ブログのテーマ機能•

管理画面でテンプレート構文自体を入力させているようなケース•

ユーザーに定義させるテンプレートをUser Deﬁned Templateと呼ぶ•

検索キーワード: User Deﬁned Template

User Deﬁned Template と Server Side
Template Injec6on•

User Deﬁned Template が Programming Language Template ならアウト••

ユーザーがサーバで任意のコード実行ができる•

サーバ自体を乗っ取ることが可能

テンプレートのレベルをちゃんと選択することが重要

DEMO: User Deﬁned Template とServer Side Template Injec:onUser Deﬁned Template として書けるテンプレートの事例•Shopify: Liquid••

Drupal: Twig••liquidjs: JS実装

Twig in Drupal 8 | Drupal 8 guide on Drupal.org

どちらもLimited control structuresなテンプレート

User Deﬁned Template として書けるテンプレートで安全を保証する手法•セキュリティレビュー

•ホワイトリストでの機能制限•

隔離環境での実行

セキュリティレビュー方式••

Wordpress: テーマは独自のProgramming Language Template•

テーマにPHPのコードも普通に書ける•

Theme Security | Theme Developer Handbook | WordPress Developer Resources•

ホスティング版では、利用できるテーマが審査済みのサードパーティに限定される•

The WordPress.com Business Plan Now Supports Plugins and Third-Party Themes —The WordPress.com Blog•

レビューによって安全を保証してる

OSS版Wordpressにおいてはテーマのインストール = 任意のPHPプログラム実行

ホワイトリスト/バリデーション方式•

利用できる機能をホワイトリストで制限する•

ブラックリストは抜け道が出やすいのでセキュリティでは避ける•

Twig、Smarty、JinjaのSandboxはこの方式•

Sandboxと言うけど、実際にはホワイトリ
ストでの機能制限 + アクセスエラー(実行させないようにしてる)•

Programming Language Templateから
Limited control structuresにレベルダウン⬇

ホワイトリスト/バリデーション方式の問題•

Valida'onによる制限はレベルを下げる道具•

全てを網羅してないとレベルを下げることができないため、結構難しい•

抜け穴一つでもあれば、レベルは下がらない = Programming Language Templateのまま•

バリデーションで後から制限より、最初からLimited control structuresを選ぶべき••最初に適切なレベルを選択して、不必要にレベルを上げないようにする

レベルを下げるのは、レベルを上げることよりも難しい•

生活水準と同じ

隔離環境での実行
Containers are not a sandbox— gVisor•

VM、Serverlessなど隔離した環境を用意しそこでテンプレートをコンパイルする•

悪意あるテンプレートをコンパイルしてもできることを制限する•

オーバーヘッドがある入力ソース: テンプレートのまとめ
ローカルブラウザ

サーバ

Simple Token
Replace

Safe

Safe

Safe

Limited controlstructures

Safe

Safe

Safe

Programming
Language
Template⚠Danger⚠Danger⚠Danger

Limited control structuresとProgramming
Language Template•

Limited control structuresは機能として提供しやすい•Programming Language Templateは機能として提供しにくいLimited control structuresとProgramming
Language Templateの見分け方•

それがプログラムに見えるならプログラム•

テンプレート内で言語のネイティブコードが呼べるなら
Programming Language Template•{{ new Date().getTime() }}•Evalを使ってるならProgramming Language Templateになる可能性があるLimited control structuresとProgramming
Language Templateの見分け方•

細かいところまで見ないと区別はつきにくい- 実装に依存してしまう•

検索キーワード•{{テンプレート名}} Untrusted template•{{テンプレート名}} User Defeined template考え方: テンプレートと実行•

自分のマシンでテンプレートをコンパイルして、その結果を提供するのはOK••

デプロイ時にコンパイルしてから提供するのはOK

他人が書いたテンプレートを、自分のマシン/ブラウザ/サーバでコンパイルするのはNG•

Programming Language Templateの場合は任意のコードが実行できる•

ユーザー入力としてテンプレートを受け取ってコンパイルする時はプログラムを実行するのと同じ気持ち考え方: ユーザー入力とテンプレート••

ユーザー入力は全てUntrustedなデータ•

ユーザー入力からテンプレートは作成しない•

ユーザー入力はデータモデル(パラメータ)として渡す

テンプレート機能をユーザーに提供する場合•

Programming Language Templateを避ける•

ユーザーにプログラムを書かせない考え方: テンプレートと複雑性•

厳密にデータとロジックを分離するのはセキュリティと同じ•

無制限なテンプレートはテンプレートがデータモデルに影響を与えることができる•

複雑な問題に対処するなら複雑な機能を持ったテンプレートが必要になる•

逆に複雑ではない問題に対して複雑なテンプレートはオーバース
ペック

まとめ
•

テンプレートは3つのレベルがあると考えられる•

テンプレートのレベルとコンパイルする場所によって影響度は異なる•

ローカルコンパイルとサーバコンパイルを同じものとして扱わない•

レベルを下げるのは難しいため、必要なレベルのものを最初に選択する•

Untrustedなテンプレートをコンパイルするときには制限がある状態で行う••

Sandbox、ホワイリスト、マニュアルレビュー

テンプレートファイルはプログラムファイル

Simple Token Replacement

Limited Control Structures

Programming Language

より良いテンプレートライフを！

参考文献
•

Enforcing Strict Model-View Separa6on inTemplate Engines•

Web Content Management - O'Reilly Media•

Client-side Template Injec6on - YouTube•

JSMVCOMFG - To sternly look at JavaScript MVC and Templa6ng
Frameworks•

PayloadsAllTheThings/Server Side Template Injec6on at master ·swisskyrepo/PayloadsAllTheThings•Web template system - Wikipedia