そもそもエスケープとは何なのか?

(Last Updated On: 2018年8月16日)

まずエスケープ処理について全て書こう、ということでPHP Securityカテゴリで様々なエスケープ処理について書いてきました。しかし、「エスケープ処理とは何か?」を解説していなかったので解説します。

エスケープ処理は文字列処理の基本中の基本です。

「エスケープは要らない、知る必要もない」という意見を稀に聞きますが、プログラムに於ける文字列処理とその重要性を理解していないからでしょう。全ての開発者はエスケープ処理の必要性を理解し、確実かつ適切にエスケープできなければなりません。

エスケープ処理の定義

エスケープ処理には複数の役割があります。エスケープ処理とは「エスケープ文字によって、それに続く文字に別の意味を持たせる処理」です。

通常、以下の3つの役割があります。

  • 意味を持つ文字列にエンコードする(文字列に意味を持たせる)
  • キーボードから入力できない文字を表現する
  • 望まない解釈となる文字の意味を打ち消す

一番目の「意味を持つ文字列にエンコードする」を聞きなれない方も居ると思います。これはターミナルなどで文字に色を付けたりする処理での利用方法です。コマンドラインアプリケーションでは、エスケープシークエンスを利用し、文字列に意味を持たせ文字や背景に色を付ける処理がよく行われています。

二番目の「キーボードから入力できない文字を表現する」はWebアプリでも見られます。例えば、文字列中のヌル文字は多くの言語で”\0″と標記します。

三番目の「望まない解釈となる文字の意味を打ち消す」がセキュリティ的に最も意味のある処理です。例えば、シングルクォートで囲まれた文字列にシングルクォートを書くには

'\''

とエスケープ文字の\でエスケープし、特殊な意味(この例の場合、文字列の終端文字の ‘ )を打ち消しています。(注:このエスケープが使えない場合もあることに注意。OSコマンドエスケープXPathエスケープ、標準SQLの場合は’(シングルクオート)は’(シングルクオート)でエスケープします)

英語ではこの処理を”Character Quoting”と言う場合があります。英文マニュアルなどでエスケープの事をQuoteと書いてある場合がありますが、日本語ではエスケープとした方が解りやすいと思います。

エスケープとエンコーディング

そもそもエスケープには、エスケープ文字+1文字で標記するダイグラフ(Digraph)とエスケープ文字+2文字で標記するトリグラフ(Trigraph)がありました。現在ではプログラミング言語中の8進数への変換(エスケープ文字+数字3文字)やHTMLで特殊な意味を持つ文字をHTMLエンティティなどへの変換(&を&)、URLエンコーディング(’ ‘を’%20’)もエスケープ処理の一種だと考えられています。

エンコーディングもセキュリティ処理となるエスケープの一種と考えられていますが、エスケープとエンコーディングでは多少意味合いが異なります。セキュリティ処理で利用するエスケープは特別な意味を持つ文字の意味を打ち消すために変換を行います。エンコーディングは特定のルールに基づいてデータを逆変換可能な形で変換します。

例えば、HTMLでは全ての文字は数値参照エンティティ(例: – 半角スペース)に変換できます。JavaScriptもUnicodeエスケープ記法を用いて全ての文字をUnicodeエスケープ(例:\u0020 – 半角スペース)表記にエンコードできます。

エンコードの一部はセキュリティ処理として役立つ場合があるので、エスケープ処理の一種と考えられるようになったと思われます。一般に全ての標記を別の形式に変換する処理がエンコーディング処理またはエンコーディング変換と呼ばれ、一部の標記のみを別の形式に変換する処理がエスケープ処理と呼ばれていると思います。

エンコーディング処理は必ずしもセキュリティ処理として役立つ訳ではありません。例えば、UTF-8文字エンコーディングをShift_JISエンコーディングに変換しても、セキュリティ処理として役立ちません。しかし、UTF-8文字エンコーディングをHTML文字参照エンティティに変換する処理、JavaScriptのUnicodeエスケープ記法に変換する処理は、セキュリティ対策用のエスケープ処理としても利用できます。

エンコーディングはセキュリティ対策用のエスケープとは異なりエンコーディングは必ずしもセキュリティ処理として役立たない、エンコーディングの方がエスケープより範囲が広い事が多いと覚えておけば良いでしょう。

  • エンコーディング ≠ エスケープ(エンコーディングはセキュリティ処理とならない場合がある)
  • エンコーディング >= エスケープ(エンコーディングは特定の文字の意味を変えるだけでなく、表現形式を変える)

OWASPではエスケープ(危険な文字の意味を打ち消す)よりエンコード(安全な表現形式に変換)する方法を推奨しています。例えば、Unicodeは全て文字参照に変換(エンコード)する、JavaScriptの文字列を変換する、などをセキュリティ対策として推奨しています。

エスケープ処理とセキュリティ

Webアプリではエスケープ処理とセキュリティは切っても切れない関係にあります。Webアプリが処理するデータのほとんどがテキストインターフェースを持ち、特別な意味を持つ文字があるからです。

参考:CERT Top 10 Secure Coding Practices  (セキュアコーディング10原則)

特別な意味を持つ文字が一文字でもある場合、ユーザー入力などをそのまま出力するとシステムが誤作動(インジェクション攻撃)する可能性があります。そもそもプログラムは正しい(妥当な)入力に対して正しく動作するように作られています。この為、不正なデータの受け入れは常にリスクが伴います。

攻撃者が悪意を持たせた入力を処理すると、システムに不正な処理を実行させる事(インジェクション攻撃)が可能になる事例は数えきれないくらい在ります。

セキュリティ対策的には、エスケープ処理は攻撃用入力データを送信(インジェクション)された場合でも「特別な意味を持つ文字の意味を打ち消し」、システムが誤作動しなくなるようにする為に行います。インジェクションができない様にエスケープ処理しても、出鱈目な攻撃用入力データがシステムに保存されることを防げない場合もあることに注意が必要です。(例えば、SQLiteのカラムは基本文字列型

Webアプリの脅威には様々な脅威がありますが、インジェクション攻撃がWebアプリにとって最大の脅威であることは、CVE等に登録される脆弱性情報からも明らかです。出鱈目な攻撃用入力データがシステムに保存されることを防げない場合があっても、エスケープ処理は様々なインジェクション攻撃の脅威に対するセキュリティ対策として重要な意味を持っています。

現在、Webシステムで問題となるインジェクション攻撃の大多数が”文字列”に対して”不正な処理を行う文字列”を”挿入”(インジェクション)することによって発生します。

XPath 1.0のように壊れた標準でなければ、エスケープ処理は定義されています。エスケープ処理のAPIは必ず用意されているモノではありません。エスケープ処理が定義されていてもAPIがないことが珍しくありません。

つまり「APIさえ使っていれば大丈夫」は成り立たないです。よくある勘違いに「APIがないから、このままで大丈夫だと思った」があります。一文字でも意味がある文字がある場合、エスケープかバリデーション、または命令とデータを分離するAPIの利用が必要です。

しかし、

  • 命令とデータを分離するAPI
  • エスケープ処理をするAPI

が存在しない場合も少くありません。例えば、SQLiteやMySQLには識別子のエスケープ方式は定義されていますが、識別子エスケープAPIは定義されていません。

APIが存在しても不完全であるケースも少くありません。特に3rdパーティーライブラリ/フレームワークの場合、エスケープ仕様に漏れがあるケースも少くありません。

このためセキュアなソフトウェアを構築するには適切なエスケープ処理方法を知ることが重要です。

エスケープ処理とAPI

エスケープ処理は手作業でも行えます。HTMLを記述する場合には&などと手作業で書くことも可能です。

しかし、通常はエスケープ処理を行うAPIが用意されている場合、そのAPIを利用します。PHPでHTMLエスケープを行う場合、HTMLエスケープを行うhtmlentities関数やhtmlspecialchar関数が用意されているのでこれらを使います。SQL文字リテラルをエスケープする場合、pg_escape_literal(PostgreSQL)やmysql_real_escape_string(MySQL)を利用します。
(注:エスケープで文字エンコーディングを考慮しなければならない場合、APIを使った方が簡単。参考:SET NAMESは禁止

エスケープ処理をバイパスするAPIもあります。

エスケープ処理が必要になる原因は命令とデータが混在する「テキストインターフェース」である事にあります。テキストの中に命令やパラメーターを書き込む為、不正な入力によってインジェクションが可能になります。
(注:インジェクションはテキストのみでなく、データ形式が原因で発生する場合もある。例:MongoDBインジェクション)

出力先の命令とパラメーターを分離できれば、パラメーターが誤って命令として解釈される事はありません。この場合、エスケープ処理を行わなくても安全に出力できます。

代表例はSQLのプリペアードクエリです。しかし、SQLのプリペアードクエリは命令とリテラルの分離は行えますが、識別子の分離や命令自体の記述を禁止できません。この点には注意が必要です。識別子が原因でSQLインジェクションに脆弱になってしまうケースは少くありません。

参考:SQLクエリと識別子エスケープの話

出力先のシステムが命令とパラメーターを分離する仕組みを提供してなくても、プログラムを作成して命令とパラメーターを分離することも可能です。ORM、HTMLヘルパーなどがその代表例です。(注:命令とパラメーターの分離を完全に行えるORM、HTMLヘルパーは知る限りではありません)

 

Web開発者はエスケープを知るべきなのか?

Web開発者はエスケープを知るべきなのか?答えはもちろんYESです。テキスト処理は数値演算と同じくプログラムの基本中の基本です。知らなくて良いはずがありません。

Webアプリはテキストインターフェースのシステム(HTML、CSS、JavaScript、JSON、SQL、LDAP、XMLなど)を利用して構築します。テキストインターフェースを持つシステム上でアプリケーション開発する以上、エスケープ処理を知らずに作ることは「どこで落とし穴に落ちる分からない」状況で作ることを意味します。汎用性があるエスケープの知識は絶対に必要な知識と言えると思います。

便利なAPIがあっても色々な制限があったりしますが、そもそもエスケープAPIさえ用意されていない場合もあります。基礎教育としてエスケープ処理は知っていれば、たとえエスケープAPIやエスケープを省略できるAPIが無かったとしても「出力先には意味がある文字がある=エスケープ処理無しでは危険な可能性がある」と認識できれば自ら安全な処理を確認する事ができます。

セキュアコーディングの第7原則は「全ての出力を無害化する」です。これにはエスケープが欠かせません。

参考:出力対策の3原則 + 1原則

 

まとめ

セキュリティ対策としてのエスケープ処理は

  • 基礎知識として必須である
  • 出力先のシステムが誤作動しないように行う
  • エンコーディング処理が利用できる場合がある
  • 命令とパラメーターの分離ができる場合は省略できる

「SQLインジェクション対策としてプレイスホルダだけ使っていれば良い」は120%誤りです。識別子のエスケープやLIKEクエリのエスケープ、DBによっては正規表現やバイナリデータのエスケープが必要です。確実な(安全な)テキスト処理にエスケープ処理とその知識は欠かせません。

エスケープ処理は出力先とそのコンテクストによって変わります。ブラウザなら

  • URL、本文、属性、CSS、JavaScriptのコンテクスト

に応じたエスケープが必要です。SQLデータベースなら

  • パラメーター、識別子、LIKEクエリ、正規表現、バイナリデータのコンテクスト

に応じたエスケープが必要です。参考:完全なSQLインジェクション対策

正しいエスケープ方法は出力先とコンテクストによって異なります。正しくエスケープすることが必要です。

エスケープをしなくて良いAPI(多くはパラメーターを分離するAPI)がある場合はそちらを利用する方が間違いが少なくなります。エスケープ処理と必要な箇所を理解した上でこれのAPIを使うと間違い(セキュリティ上の問題を作ること)が更に少くなります。

最後に、出力処理”だけ”のセキュリティ対策は論理的/構造的に間違いです。2017年版OWASP TOP 10ではこの構造的間違いを脆弱性だとしています。

「出力対策だけのセキュリティ設計」が誤りである理由

参考

投稿者: yohgaki