Angular TIPS

Angular TIPS

ビューにHTML文書をバインドするには?(Property Binding)

2017年2月13日 (2017/04/14 更新)

プロパティバインディングでHTMLタグを含む文字列にバインドした場合、デフォルトでサニタイズされる挙動を確認。逆に意図的にサニタイズさせない方法を説明する。

  • このエントリーをはてなブックマークに追加

【対応バージョン】 Angular 2/4対応。v2時点で執筆し、v4時点で内容を確認・検証しました。

 Angularでは、データバインドを利用したテキストの埋め込みにも、セキュリティ上の考慮がなされています。例えば以下のようなコードを見てみましょう。以下は、タグ(HTML)を含んだ文字列をビューにバインドする例です。

TypeScript
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  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文字列をビューにバインドするためのコード(app/app.component.ts)
HTML文字列はただの文字列として表示される

 HTML文字列はきちんとエスケープ処理されて、単なる文字列として表示されるわけです。これはもちろん正しい挙動なのですが、時として、動的に生成された文字列をHTMLとして反映させたい、ということもあるでしょう。

 そのような場合には、Property Bindingを利用して、innerHTMLプロパティに文字列をバインドします。

Property BindingによるHTML文書のバインド

 以下は、先ほどのコードをProperty Bindingで書き換えたものです。

TypeScript
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: '<p [innerHTML]="msg"></p>'
})
……後略……
Property BindingでHTML文書をバインドするコード(app/app.component.ts)
HTML文字列をHTMLとして解釈して表示(開発者ツール)

 Property Bindingの構文については、別稿「TIPS:要素オブジェクトのプロパティに値をバインドするには?」も併せて参照してください。Property Bindingでは、あくまでバインドの対象は(属性ではなく)プロパティなので、innerHTMLプロパティに対しても値をバインドできるわけです。

 ただし、この場合も全てのHTML文字列を無条件にバインドしているわけではありません。この例であれば、文字列に含まれる<script>要素やアンカータグのonclick属性、<button>要素などが除去(サニタイズ)されていることが確認できます。Angularでは、最低限、危険と思われる要素/属性を排除することで、意図せぬセキュリティホールの発生を防いでいるわけです。

HTML文字列が安全であることを保障する

 もっとも、すでに別のライブラリで文字列を処理済みである、または、アプリとして内容を把握しており、「安全な」HTMLであることが保証できる場合には、<button><script>などの要素が勝手に除去されてしまうのは望ましくない場合があります。

 そのような場合には、文字列にあらかじめ信頼済みマークを付与しておくことで、Angularによるサニタイズを回避できます。これには、Angular標準で提供されているDomSanitizerクラスのbypassSecurityTrustHtmlメソッドを利用します。

 例えば冒頭の例であれば、以下のようにコンポーネントを書き換えます。

TypeScript
import { Component } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

@Component({
  selector: 'my-app',
  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);
  }
}
HTML文字列に信頼済みマークを付与するコード(app/app.component.ts)
スクリプトや<button>要素なども維持されている(開発者ツール)

 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(インターフェース)

※以下では、本稿の前後を合わせて5回分(第7回~第11回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

7. ビューの中で「安全に」プロパティ/メソッドにアクセスするには?(「?.」演算子)

プロパティやメソッドを呼び出す際にレシーバーオブジェクトがnullでないことを確認した上で呼び出せる「?.」演算子の基本的な使い方を解説する。

8. 要素オブジェクトのプロパティに値をバインドするには?(Property Binding)

要素のプロパティに値をバインドするProperty Bindingについて、「[プロパティ名] = "式"」「bind-プロパティ名 = "式"」「{{プロパティ名}}」という3つのバインディング構文を説明する。

9. 【現在、表示中】≫ ビューにHTML文書をバインドするには?(Property Binding)

プロパティバインディングでHTMLタグを含む文字列にバインドした場合、デフォルトでサニタイズされる挙動を確認。逆に意図的にサニタイズさせない方法を説明する。

10. 要素の属性に値をバインドするには?(Attribute Binding)

HTML要素の属性に対応するプロパティではなく、属性そのものにバインディングする方法を説明する。

11. JavaScriptオブジェクトをJSON形式に変換するには?(json)

JavaScriptのJSON.stringifyメソッドと同じように、AngularでJavaScriptのオブジェクトをJSON形式に変換できるjsonパイプの基本的な使い方を説明する。

サイトからのお知らせ

Twitterでつぶやこう!