Webアプリケーションのセキュリティで気をつけること

社内で使われている問い合わせシステム(チャットボットと有人チャット)を開発しています。
私の会社では外に公開するシステムは基本的に脆弱性診断を受けてリリースする必要があります。
全システムで基本的に対応すべき項目に今回のリリースするシステムの事情を交えた対応方法と、
脆弱性診断で指摘もらった項目を失敗談交えて晒そうと思います。

# 基本的なこと

[Webアプリケーションのおすすめセキュリティ設定](https://qiita.com/fukushi_yoshikazu/items/1a746fc8c8663be18bf9)が参考になります。
上記の記事から今回のリリースしたシステムの事情に合わせて補足的に解説します。

## X-Frame-Optionsヘッダ（XFO）

ブラウザがiframeなどをページ内部に表示することを許可するかの設定です。
基本的に `X-Frame-Options DENY` ヘッダをつければ問題ありません。

ただ、今回は別のサイトに埋め込み可能なサービスであるという要件でした。(別のシステム内にiframeで埋め込んだり、ライブラリを提供して別システム内で利用できるようにしています)
許可するには `X-Frame-Options ALLOW-FROM uri` ヘッダがありますが、

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Frame-Options

> これは廃止されたディレクティブであり、最近のブラウザーでは動作しません。使用しないでください。
> Content-Security-Policy ヘッダーには frame-ancestors ディレクティブがあり、代わりにこれを使用することができます。

廃止されているので `Content-Security-Policy frame-ancestors uri` ヘッダを利用して特定のサイトを許可してあげます。
以下はnginxの設定例です。(複数ドメインある場合はスペース開けて指定できます)

```conf
add_header Content-Security-Policy "frame-ancestors https://example.com/";
```


## キャッシュ設定

メリットデメリットあるので確認しながら適切な値を設定してください。 https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control

今回は以下はキャッシュしないnginxの設定例です。

```conf
add_header Cache-Control "no-store";
add_header Pragma "no-cache";
add_header Expires "-1";
```



# プロジェクト固有の問題

ここからはプロジェクト固有の問題で指摘もらった項目を当時考えていた事と共に紹介します。


## アプリケーションによるDoS攻撃の可能性

### 経緯

オペレータとユーザーがチャットするページでは、無限にメッセージ数増えるわけではなく、だいたい30メッセージくらいでやりとりが終わる。
基本的に一般的なチャットは全件メッセージ表示ではなく、スクロールに連動したロードが行われるようになっているが、このチャットはメッセージが多くはないし、作るのにまぁまぁ時間かかるので後回しにして全件ロードで後回しにしよう。
問題起きてからの対応でいいだろう。

### 問題

オペレータとユーザーとのチャットから大量のメッセージや添付ファイルを送信する事で、そのチャンネルの表示時に大量のAPI通信が発生している。
ページを開くだけで負荷をかけられ、DoSに脆弱な可能性がある。
悪意のあるユーザーに添付ファイルを大量に送信された場合に、サービスがダウンする可能性がある。

### 対応

面倒くさがらず起こりうるなら起こるのでちゃんと対応するべきだった。
スクロールに連動した数十件づつロードをするように変更し、ファイルは見えた時にロードするように変更しました。

## 管理者/開発者が利用するAPIでアプリケーションに不都合をもたらす事ができる問題

### 経緯

チャットを埋め込まれる側の連携サービスを登録する管理画面があるが、
運用的にほぼ開発者しか使わず、開発者は仕様把握しているのでデータに不都合になるような操作はしないし、無意識に細かな検証をしていなかった。

### 問題

- 他連携サービスが設定しているJWT ISSと同じ値を設定する事で署名確認を妨害する事が可能な問題
- JWTの署名に使うSecretを更新後に発行済みのトークンを使用することが可能な問題
- 連携サービス削除後に発行済みのトークンを使用することが可能な問題
- 削除済みのサービスのトークンを発行することが可能な問題
- 単純なSecretでも登録可能な問題

### 対応

いずれも開発者しか使わないから無意識に検証できていなかった箇所だった。
どのAPIだろうが誰がどう使っても不都合が起こらないようにバリデーションする必要がある。


## セッション情報漏れ

### 経緯

私の会社には独自のSSO基盤があり、今回開発したシステムもチャットの埋め込み先であるサービスもそのSSO基盤に乗っかっています。
その場合、埋め込み先でSSO認証が走り、埋め込まれたチャットも(認可は一度だけで良いものの)認可画面が表示されてしまうので多少ユーザビリティが悪くなってしまいます。
また、分析要件で誰がどういう質問をしたか追う必要があるのと、利用元からクライアント側でID(メールアドレスなどの)を送ってもらうような改ざん可能なユーザー特定方法は避けようと考えていて(物騒な話ですが他人名義で爆破予告とかあるとたまったもんじゃないので)
URLにその個人の権限のみの範囲で期限付きでAPIを利用可能なトークンを乗せてもらい認証していました。
その個人の権限のみの範囲なので、そのユーザーが漏らそうと思って漏らす以外漏洩しないので問題ないだろうと考えていました。

### 問題

`個人を特定可能` なセッション情報がURLに含まれていてリスクが高い

### 対応

ユーザーはトークンのあるページだという認識はしないので、その個人の権限のみの範囲でも関係なく、何かしらで漏れても責任を取れません。
リスクを軽減させるためURLに乗せず、 `postMessageを利用したトークンの受け渡しに変更` しました。

`個人を特定可能`というのがミソで、S3のPreSignedURLにもトークンが含まれますが、個人を特定するものではないのでOKのようです。

次回やるとすれば2重SSOを許容するか、利用元からjsでIDを埋め込んでもらう簡易トラッキングにするかもしれません。


## ファイル形式を制限しないアップロード

### 経緯

ヒアリングで漏れる要件(プリンタドライバの受け渡しなど)が十分にありえるのと、制限するとファイルの受け渡しに別のツールを利用されてユーザービリティ悪くなるので、チャットでやりとりできる `ファイル形式は絞らない` 仕様にしていました。
拡張子やContentTypeも適切に指定/保存しているし、XSS対策でhtmlは表示しないプレビュー不可能なファイルは表示しないなど十分に配慮できているだろうと考えていました。
また、 `実行形式のexeを勝手に実行するなどはブラウザの責任` で、 `exeをダウンロード後、実行する/しないはユーザーの責任` だろう、ContentTypeを適切に指定しているので `ファイルの種別に応じてブラウザが適切に処理してくれる` だろう、Content-Dispositionを適切にブラウザに教えてるので `開くがダウンロードするかなどにブラウザは従ってくれる` だろう、 `実際別のチャットツールではなんでもアップロードできる` し問題ないだろうと考えていました。

### 問題1

- 画像以外のファイルをアップロードすることが可能
  - 例えば、HTMLファイルをアップロードされた場合、 `フィッシングサイトなどを作成することも可能かも` しれない
  - そのHTMLファイルの中にXSSが含まれている場合、 `セッション情報などの機微な情報を奪われるかも` しれない
  - 実行形式のファイルもアップロード可能だがマルウェアが `どのような形で実行されるかは環境次第`
  - 上記も一例で、 `すべてのファイル/パターンを網羅するのは現実的ではない`

### 対応1

たしかにすべて環境次第で、網羅するのは現実的ではないのでホワイトリストで制限する事にしました。

フロント側での選択可能な `ファイルタイプ` の制限と、バックエンド側での `拡張子チェック`、 `Content-Typeチェック`、 `マジックバイトチェック` によるバイトレベルでのチェック (ファイルの先頭バイトからファイル種別を特定する)とそれらで判定した全てが同一のファイル種別がチェックするようにしました。
またその対応時にcsvはどう作ってもtext/plainだったりするので除外したり、良いか悪いかは置いといてpngをjpgにリネームしたファイルは通せないのでどうするかなどいといろ検証箇所がありました。

ただ、逃げ道確保の為に `zip` は残していました。

### 問題2

zipの場合はサーバー側で展開して中身に同様のチェックをする必要がある

### 対応2

対応としてはzipだけ特別な処理を組まなければならず、zip in zipにも再起的に処理しないといけなくて複雑で、
掃除も必要だし自分の知らないメモリ関連の脆弱性とか不具合多そうでした。
幸いにも元の要件は画像/動画のアップロードなので、zipの対応は見送りました。



# まとめ

今回私はこういう対応をしましたが、プロジェクトの目的や、ユーザビリティや開発速度など重要視するもので対応は変わるかと思います。
すべての会社/プロジェクトで絶対に対応するべきというわけではなく、どういうリスクがあるのか認識して優先順位はつけた方がいいでしょう。(許容する事も一つの判断)

情報セキュリティの3要素は、以下3つの用語を英語表記したときの頭文字を取って「CIA」と呼ばれています。

- 機密性（confidentiality） 情報に対するアクセス権限を徹底して保護・管理すること
- 完全性（integrity） データを最新かつ正しい状態で維持すること
- 可用性（availability）システムを障害（機器やパーツの故障・災害・アクシデントなど）で停止させることなく稼働し続けること

誰かのアカウントで何かをできてしまうなどだけではなく、管理者のみが実行するAPIでのデータ不整合や、アプリケーションDosでの障害も含めたセキュリティについていろいろ考えるきっかけになり、いろいろ勉強になりました！