はじめに
ブログを書く画面は、太字にしたり見出しを付けたり画像を挿入したりできる、いわゆるリッチエディタが便利です。ただ、この「自由に書ける」という性質は、そのままだとセキュリティ上の弱点にもなります。
この記事では、書き心地を損なわずに危険なコードの混入を防いだ方法を紹介します。専門用語が少し出てきますが、考え方はシンプルです。
なぜエディタが危険になり得るのか
XSSという攻撃
リッチエディタで書いた内容は、最終的にHTMLとしてサイトに表示されます。もし記事の中に悪意あるプログラム(スクリプト)が紛れ込んでいたら、それを見た人のブラウザで勝手に実行されてしまいます。
これがXSS(クロスサイト・スクリプティング)と呼ばれる攻撃です。ログイン情報を盗んだり、偽の入力画面を表示したりできてしまいます。
管理者しか書かないから安全、ではない
「管理者しか記事を書けないのだから危険なコードは入らない」と考えがちですが、管理者のアカウントが乗っ取られた場合や、外部からコピーした文章に不正なコードが紛れていた場合には通用しません。書き手を信頼するのではなく、内容を検査する仕組みが必要です。
「許可するものを決める」方式にする
危険なタグを見つけて除去する方式(ブラックリスト)だと、新しい攻撃手法が出るたびに漏れが生まれます。そこで使ってよいものだけを列挙し、それ以外はすべて捨てる方式(ホワイトリスト)を採用しました。
記事本文で許可したのは、段落・改行・太字・斜体・下線・打ち消し線・引用・区切り線・見出し・箇条書き・リンク・画像だけです。それ以外のタグは、記事に書かれていても表示時には消えます。
二重にサニタイズする
保存時と表示時、両方で検査する
検査(サニタイズ)は、記事を保存するときと、記事を画面に表示するときの2回行っています。
ここでの制限は書き心地のためのもので、安全性の保証ではない
許可リストにないタグ・属性をすべて除去してからデータベースへ
データベースの内容を信用せず、表示直前に再度検査
「保存時に検査したのだから表示時は不要では」と思うかもしれません。しかし、保存後にデータベースが直接書き換えられた場合や、過去に別のルールで保存されたデータが残っていた場合を考えると、表示時の検査が最後の砦になります。
エディタ側での制限は、あくまで書き心地のためのものと位置づけています。画面での制限をセキュリティの根拠にしない、という考え方です。
リンクと画像は特に慎重に
リンクのURLには、通常のWebページを指すもの、メール、電話番号のみを許可し、それ以外の形式は除去します。特殊な形式のURLには、スクリプトを埋め込めるものがあるためです。
画像は、暗号化された通信のURLのみを許可しました。データを直接URLに埋め込む形式も、悪用されうるため許可していません。
また、外部サイトへのリンクには自動的に安全属性を付与し、画像には自動的に遅延読み込みの指定を追加します。書き手が意識しなくても、正しい形に整えられる仕組みです。
スタイル指定は文字揃えだけ
文字の配置(左寄せ・中央・右寄せ)は記事の表現として必要なので許可しましたが、それ以外のスタイル指定はすべて禁止しています。
要素の位置を自由に指定できてしまうと、画面上に透明なボタンを重ねるといった手口(クリックジャッキング)が可能になるためです。デザインの自由度は下がりますが、記事本文にそこまでの自由は必要ありません。
便利な機能も安全に作る
お店カードは識別子だけを埋め込む
ブログでお店を紹介するとき、エディタから検索して選ぶと、記事の中にはカード形式の要素が挿入されます。ただし、この要素に埋め込まれるのはGoogleのお店識別子(Place ID)だけです。
表示のたびに最新の店名や評価を取得するので情報が古くならず、記事本文には不正なコードを埋め込む余地もありません。
さらに検査の段階では、「お店カード用の要素で、かつ識別子が正しい形式のもの」以外は、中身ごと削除するルールにしました。似せた要素を作っても通りません。
機能追加のたびに検査ルールも見直す
新しい表現を追加するときは、必ず「その要素だけを、正しい形式のときだけ許可する」というルールをセットで追加します。機能だけ足して検査を忘れると、そこが穴になります。
HTMLを直接編集できる逃げ道も用意する
リッチエディタでは表現しきれない場面のために、HTMLを直接編集するモードも用意しました。ただし、ここで書かれた内容も保存時・表示時のサニタイズを通ります。
自由度を上げつつ、検査は必ず通る。この構造なら、逃げ道を用意しても安全性は変わりません。
まとめ
エディタのセキュリティで押さえたのは、次の3点です。
- 許可リスト方式 — 使ってよいタグだけを列挙し、それ以外は無条件で捨てる
- 保存時と表示時の二重検査 — データベースの内容も信用しない
- 画面での制限を安全の根拠にしない — エディタの制限は書き心地のため、と割り切る