「フロントエンド開発のためのセキュリティ入門」(スライド)を見る
リスクその1:暗号化の失敗
通信データの暗号化によって対策する。
HTTPにはいくつかのセキュリティ上の弱点がある
まず、暗号化されていない平文で送られるので盗聴されうる
通信相手の手がかりがURLしかないので、なりすましに弱い
通信途中で攻撃者による改ざんがあっても検知できない
よわっ
そこでHTTP通信の前に暗号化通信を確立したやり方がHTTPS
暗号化通信の確立の大まかな流れは
- 暗号アルゴリズムなどの決定
- サーバー認証
- 鍵交換
- 共通鍵を使った暗号通信の開始&ハンドシェイクの改ざんチェック
ハンドシェイクってよく見るけど雰囲気でしかわかってない
通信データの暗号化にはTLSというプロトコルを用いる。公開鍵を使って暗号化した共通鍵を使って文書を暗号化・復号する。
通信相手の検証には照明局が発行した電子証明書を用いる。
ブラウザにはもともとルート証明書と呼ばれる、信頼できる証明書が組み込まれているらしい。
このため、Webブラウザなど公開鍵暗号を利用するソフトウェアの開発者は、世界的に有力な大手の商用認証局や政府機関の認証局など、信頼するに足ると思う機関が自ら発行する証明書を事前に入手して組み込んでおき、証明書を上位にたどっていった結果その機関の証明書に行き当たったら正しく検証されたと判断するようにしている。
この信用の連鎖により安全に公開鍵暗号を運用する社会的な基盤のことをPKI(Public Key Infrastructure/公開鍵基盤)と呼び、その最上位に位置する機関をルート認証局(ルートCA)、その発行する自らのデジタル証明書をルート証明書という。(出典)
昨今、HTTPSはほぼ必須。HTTPSでしか使えないAPIもある。
HTTPS化するためにすること
- Mixed Contentの修正
- HTTP→HTTPSのリダイレクト設定
- HSTSの設定
Mixed Contentの修正
HTMLがHTTPSで配信されていても、サブリソースのJSがHTTPで配信されてしまっているなど、HTTPで配信されているリソースとHTTPSで配信されているリソースが混在する状態があり得る。
サブリソースでもHTTPで配信されていたらそこから潜り込まれる危険性がある
HTTP→HTTPSのリダイレクト設定
もろもろの都合でHTTPがやめられないときもある。そういうときのために、サーバー側でHTTPのリクエストをすべてHTTPSへリダイレクトする設定を行う。
HSTSの設定
HTTP Strict Transport Securityはブラウザから送信されるリクエストをすべてHTTPSに強制できる機能。
Strict-Transport-Securityヘッダを受け取ったブラウザは、次回以降のリクエストをHTTPS通信で行うようになる。
Strict-Transport-Security:includeSubdomainsでサブドメインにも適用可
HTST Preloadリストとやらにホスト名が登録されていれば、自動的にHTTPSにしてくれるらしい
リスクその2:アクセス制御の不備
CORSを使ったアクセス制御により対策する
ブラウザには外部のアクセスを制限するOrigin(スキーム名+ホスト名+ポート番号)という境界線がある
Originが同一であることをSame Origin、異なることをCross Originと表現する
ブラウザは一部のデータのやり取りをSame Originにだけ許可している。ざっくりいうとこれがSame Origin Policy。これによりCross Originへの種々のリクエストが制御される。
Cross Origin Resource Sharingを設定すればfetchなどを使ったデータのやり取りが可能になる。
クロスオリジンのサーバーからのレスポンス自体はいつも返ってくるらしい。CORSヘッダを見てブラウザがデータの取り出し許可を出すかどうか決める。
CORSではリクエスト自体は防げないので、データの更新や削除など副作用のあるリクエストを対策するには工夫が必要。
それがプリフライトリクエスト。事前に「このメソッド問題ないですか?」と聞きに行く。
リスクその3:インジェクション
XSSの対策をする
XSSにもいろいろ種類があるらしい
反射型XSS
悪意のあるリンクを踏むとレスポンスによって反応的に被害を受ける?
蓄積型XSS
サーバーへ送信された不正スクリプトをDBなどに保存してしまうことで半永続的な被害を受ける
DoM-based XSS これが本題っぽい
DOM操作が脆弱性の原因となるもので、サーバーを介さずに攻撃が成立するため検知が難しい。
Dev toolのおかげでフロントエンドの脆弱性は見つけやすい。
文字列のエスケープ処理を挟むことで悪意のあるスクリプトを実行してしまうことは大抵防げる。
Reactなどの有名ライブラリ・フレームワークはそこらへんのことをすでにやってくれている。
ただしdangerouslySetInnerHTMLのように脆弱なAPIが一部存在する。
<a href='javascript:void(0)' >Help me!</a>
みたいに、javasript:
スキームのURLがあるとそこからスクリプトを実行できてしまう。https:
などから始まるURLに絞り込むべき。
Linkコンポーネントの実装例
const Link = ( props ) => {
const protocol = new URL(props.href).protocol
const safeUrl = /^https:/.test(protocol) ? props.href : ""
return <a href={safeUrl}>{props.children}</a>
}
DOMPurifyライブラリやSanitizer APIなどでサニタイズができるらしい
またContent Security Policyとやらで許可されていないリソースの読み込みをブロックすることができるらしい。
フロントエンドだけでも設定できるが、運用途中からの導入は難しい。
リスクその4:古くなったコンポーネント
npm audit fixやDependabotで対策する