POST のトークン
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
API やフォームで POST するときに特にトークンとか用意してないことも多いのですが セキュリティをちゃんと考えるならあったほうが良さそうです
どう実装するのがいいのかなと考えてみました
どう実装するのがいいのかなと考えてみました
トークンの意味
まずトークンが必要な理由ですが 本人が普通に POST する分には特に要らないものだと思います悪い人の攻撃を防ぐセキュリティ的なことが主な目的です
一応ワンタイムトークンにしておけば 二重 POST を防ぐとかもできますけど
普通に考えれば攻撃しようにしても 他人のログイン情報や Cookie を盗めないと何もできません
しかし ウェブの場合は自分のサイトへアクセスしてきたユーザに外部のサイトへアクセスさせることができます
そういったアクセスでも Cookie は送られます
そのユーザのブラウザからすると サイト A へのリクエストなので サイト A が作った Cookie を送るのは自然です
Cookie が送られていればログインしたユーザとしてのアクセスなので
https://service100/account/delete
みたいな URL にアクセスさせれば もしそのユーザが service100 というサイトにログインしていた場合にアカウント削除させることができてしまいます
他にもブログだと本人が書いた覚えのない記事を投稿させたり 銀行だと勝手にお金を振り込ませたりできてしまいます
ログインしないサービスでも殺害予告や迷惑行為を知らない間に他人にやらせたりできてしまいます
こういうのを防ぐ目的でトークンが使われます
Cookie の場合
ログインが必要なサービスの場合は ログインしているユーザのリクエストである必要があります基本的には Cookie で判断するので Cookie が送られなければ未ログインとみなされ POST は無効です
Cookie に SameSite 属性をつければ 外部からのリクエストの場合に Cookie を送らないように制御できます
ログイン時に発行する Cookie にこの属性をつければトークンはなくてもこれだけで済むはずです
またログインしない場合でも とりあえず Cookie を発行しておいて POST リクエストでは Cookie が送られていることをチェックすれば外部からのリクエストでないことは保証できるはずです
fetch で headers に Cookie を指定しても送ることはできないので とりあえずの Cookie は全員同じ中身で 「1」 だけでも大丈夫そうです
そういう意味では便利な属性のはずなのですが Chrome がデフォルト値を変更したり None は https 必須にしたりしたので困ることの方が増えてむしろやっかい機能の印象の方が強かったりしますけどね
トークンの使い方
SameSite のことは一旦忘れてトークンを使う場合についてですCookie だと勝手に送られるので 本人が意図したリクエストでしか送られないものを用意することで 意図しないリクエストによる操作を防ぎます
トークンは Cookie 中のセッションに持たせて それと同じトークンを POST リクエストで送信します
この 2 つが一致すれば本人が意図したリクエストのはずです
外部からのリクエストではトークンが含まれないので不正とみなすことができます
ログインするものなら Cookie ではなく データベースのユーザ情報に持たせることもできます
基本的にトークンは外部に漏れることはないはずなので使い回しでも問題ないはずです
送信の仕方は フォームの場合は <input type="hidden"> を用意してサーバ側で埋め込んでしまうのがかんたんです
SPA など API でリクエストすることになるならフォーム同様 body に含めることもできますが header を使えるので header で送るのもありです
Cookie の必要性
Cookie だと自動で送られるので それとは別にトークンを手動で送ることになったのですが 手動でトークンを送るならそれ自体を Cookie の代わりにすれば良いように思いましたSPA のサイトだとサーバとの通信は API 呼び出しのみになるので すべてのリクエストの header にトークンをセットできます
このトークンを Cookie のように扱いセッションを管理してログイン判断します
実際に Cookie とは別のトークンで認証処理をする仕組みは存在します
この場合は POST のトークンと同じで手動でセットするものなので 外部からのリクエストでは送信されずログインしてるとはみなされません
Cookie の代わりにこの仕組みを使えば POST のトークンというものを意識しなくて済みますし 良い方法なのかもしれないです
手動送信するトークンでセッションを管理する場合 その情報をブラウザに保存する必要があります
そうなると localStorage などを使うことになりますが これが良くないと言ってる人もみかけます
JavaScript から操作できるのでリスクがあるとは言えますが 前提として攻撃用のスクリプトが自分のサイトで動く必要があります
XSS 対策してないとか信頼できないライブラリを埋め込むのが原因ですし そうなってる時点ですでに手遅れだと思います
そんな状況でトークンだけ守ったところでたいした意味はないと思います
トークンを盗めなくてもサービス利用者の権限で任意のリクエストをサーバへ送れます
Cookie を使って SameSite や httpOnly の制限をかけていたとしても そのサイト内からの正常なリクエスト扱いなので防げません
またそのレスポンスを悪い人たちが管理してる外部サーバへ送信もできます
JavaScript を実行できる以上 画面を書き換えて偽物の画面を出してユーザに入力を促すこともできます
「セッションが切れたのでパスワードを再入力してください」という画面を出せばユーザが認証情報を入れてくれるのでそれを外部サイトに送ることもできます
なので localStorage に保存することは問題あるとは思えません
zip とパスワードを別メールで送るくらいのほとんど効果が見込めないセキュリティ対策だと思います
それ以前の攻撃用スクリプトが自分のサイトで動くようなことがないように気をつけるべきです
そもそも いろいろなサイトで使われていて フレームワークの機能としてもサポートされているくらいですしね
まとめ
[SameSite]外部からのリクエストで Cookie を送信しなくできる
ログインが必須なページならこれで不正な POST を防げる
ログインが不要なページでも Cookie を作っておけば不正な POST を防げる
Cookie の属性をつけるだけなのでお手軽
[トークン]
Cookie とは別に手動でトークンを送る
外部からのリクエストでは正しいトークンを送れないので不正な POST を防げる
[トークンを Cookie 代わりにも使う]
ログインが必要なときに ログインの Cookie とトークンを送るのはムダが多い
トップフレームやリソースの読み込みに Cookie が不要で API リクエストしかないならトークンを Cookie 代わりに使える
外部からのリクエストでは正しいトークンを送れないので 不正な POST を防げる
ログインのためのものになるので POST 用のトークンは不要になる