AngularでビューにHTML文書を「バインド」するには?(Property Binding):Angular TIPS
プロパティバインディングでHTMLタグを含む文字列にバインドした場合、デフォルトでサニタイズされる挙動を確認。逆に意図的にサニタイズさせない方法を説明する。
※現在では、Web標準技術を利用したアプリ開発が広く普及し、そのためのフレームワークも多数存在しています。その中でも主流のフレームワークの1つである「Angular」を活用し、そのための知識を備えることには大きな意味があります。本連載は、Angularユーザーに向けて、その使いこなしTIPSを紹介するものです。なお、本連載は「Build Insider」で公開していた連載「Angular Tips」を同サイトおよび筆者の了解を得たうえで、本フォーラムに移行したものです。記事はBuild Insiderで公開した状態のまま移行しているため、用語統一などの基準が@ITの通常の記事とは異なる場合があります。
【対応バージョン】
Angular 2/4対応。v2時点で執筆し、v4時点で内容を確認・検証しました。
Angularでは、データバインドを利用したテキストの埋め込みにも、セキュリティ上の考慮がなされています。例えば以下のようなコードを見てみましょう。以下は、タグ(HTML)を含んだ文字列をビューにバインドする例です。
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: '<p>{{msg}}</p>'
})
export class AppComponent {
msg : string = `<script>window.alert("危険!");</script>
<p>こんにちは、世界</p>'
<a href="#" onclick="alert('xxx')">危険なリンク</a>
<button>おっす</button>
<font color="Red">こんにちは、世界</font>`;
}
HTML文字列はきちんとエスケープ処理されて、単なる文字列として表示されるわけです。これはもちろん正しい挙動なのですが、時として、動的に生成された文字列をHTMLとして反映させたい、ということもあるでしょう。
そのような場合には、Property Bindingを利用して、innerHTMLプロパティに文字列をバインドします。
Property BindingによるHTML文書のバインド
以下は、先ほどのコードをProperty Bindingで書き換えたものです。
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: '<p [innerHTML]="msg"></p>'
})
……後略……
Property Bindingの構文については、別稿「TIPS:要素オブジェクトのプロパティに値をバインドするには?」も併せて参照してください。Property Bindingでは、あくまでバインドの対象は(属性ではなく)プロパティなので、innerHTMLプロパティに対しても値をバインドできるわけです。
ただし、この場合も全てのHTML文字列を無条件にバインドしているわけではありません。この例であれば、文字列に含まれる<script>要素やアンカータグのonclick属性、<button>要素などが除去(サニタイズ)されていることが確認できます。Angularでは、最低限、危険と思われる要素/属性を排除することで、意図せぬセキュリティホールの発生を防いでいるわけです。
HTML文字列が安全であることを保障する
もっとも、すでに別のライブラリで文字列を処理済みである、または、アプリとして内容を把握しており、「安全な」HTMLであることが保証できる場合には、<button>/<script>などの要素が勝手に除去されてしまうのは望ましくない場合があります。
そのような場合には、文字列にあらかじめ信頼済みマークを付与しておくことで、Angularによるサニタイズを回避できます。これには、Angular標準で提供されているDomSanitizerクラスのbypassSecurityTrustHtmlメソッドを利用します。
例えば冒頭の例であれば、以下のようにコンポーネントを書き換えます。
import { Component } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'app-root',
template: '<p [innerHTML]="safeMsg"></p>'
})
export class AppComponent {
safeMsg : SafeHtml;
msg : string = `<script>window.alert("危険!");</script>
<p>こんにちは、世界</p>'
<a href="#" onclick="alert('xxx')">危険なリンク</a>
<button>おっす</button>
<font color="Red">こんにちは、世界</font>`;
constructor(private sanitizer: DomSanitizer) {
// msgプロパティの内容に信頼済みマークを付与
this.safeMsg = sanitizer.bypassSecurityTrustHtml(this.msg);
}
}
bypassSecurityTrustHtmlメソッドによって、msgプロパティは信頼済み(trust)であることがマークされました。戻り値(safeMsgプロパティ)の型はSafeHtmlです。
これでProperty BindingはsafeMsgプロパティの内容をそのままページに反映させます。ただし、bypassSecurityTrustHtmlメソッドは、文字列の内容を検証しているわけではなく、ただ信頼済みであることをマーキングしているだけです。bypassSecurityTrustHtmlメソッドを利用する場合には、その文字列が安全であることを開発者自身が保証できなければなりません(さもなければ、そのままセキュリティホールになります!)。
処理対象:テンプレート構文(Template Syntax) カテゴリ:基本
処理対象:Property Binding(プロパティバインディング) カテゴリ:テンプレート構文(Template Syntax)
API:DomSanitizer カテゴリ:@angular > platform-browser > CLASS(クラス)
API:SafeHtml カテゴリ:@angular > platform-browser > INTERFACE(インターフェース)
Copyright© Digital Advantage Corp. All Rights Reserved.