セキュアなトークンへのJWT適用について

f:id:ritou:20210815061808p:plain

おはようございます、ritouです。

こういう記事を読みました。

lab.astamuse.co.jp

Webアプリケーションでセキュリティ対策のためにランダム文字列な文字列する場面が多々あります。例えば

  • CSRF対策のトーク
  • OAuthやOpenIDに使用するnonce, state
  • メールの到達確認用URLのトーク
  • パスワードをhashする際に使用するsalt

これらの値は単に衝突しなければOKというものではありません。十分なセキュリティ強度を確保するためには推測不可能なランダム値を使う必要があります。

私は持病を持っているため、ついつい「あー、これJWTなら...」と考えてしまいます。 今回は、こういうセキュアなトークンの方にJWTを使ったらどうなるかというお話をざっくり書きます。

ちなみに、乱数生成の話に異論を唱えるわけではありません。 今回の要件にある

  • 衝突しない
  • 推測不可能

ざっくり言ってしまえば前者はデータ設計、後者はセキュリティ面からの要件と言えるでしょう。

  • 重複はしないんだけど生成される文字列の順番が決まってる文字列生成関数
  • オートインクリメントな数値

とかを(プライマリー)キーにしてデータを管理しており、それを外部に露出して推測されて困るような場合、記事にあるような乱数文字列をふったりマッピングをもったりする必要が出てくるかもしれません。

そこで、JWTを使うとどうでしょうか?以前、似たような話を書きました。

ritou.hatenablog.com

この記事では "推測困難な文字列としてJWTが使えるんじゃないか?" という主張をしていますので興味ある方はどうぞ。 私の記事はいわゆるJWS(JSON Web Signature)を使うお話が多いのですが、キーの値をPayloadに含めることを考えます。

  • JWTのPayloadはデコードしたら読める。ので、 データのキー自体は確認できるし推測できても良い場合 には使える。
    • ログイン中のデータ操作などはACLちゃんと確認しよう
  • 署名がついているので、キーを変えて他人のリソースに...ってのは困難

という点から、

記事にあるような乱数文字列をふったりマッピングをもったりする必要が出てくるかもしれません。

これをしなくても実用に耐えうるものとなるでしょう。

もちろん、良いことばかりとは言いません。

  • 鍵の管理が必要 : パスワードの処理とかには向かないかもしれません
  • 署名生成/検証処理が必要 : たくさんの処理をするものであれば無視できない部分でしょう

という話は出てきます。 それでもJWTを適用するメリットを享受するために、"文字列自体にメタデータを保持できる" 点を挙げておきます。

例えばセキュアなトークンにこんなメタデータを追加していったらどうなるでしょう。

  1. 単純な文字列 "だけ" をキーにしている場合 : "1234567890qwerty"
  2. 用途を含む "prefix" などがついている場合 : "sid_1234567890qwerty"
  3. 生成日時、有効期限などがついている場合 : "sid_1234567890qwerty_1628974596"

こういう値を追加することによって、データストアを参照する "前" にある程度弾くなどの処理ができそうです。 いろんな用途でセキュアなトークンを利用したい場合、キーとなる値 + 様々なメタデータを含むことで無駄なデータストアの参照を減らしたり、別の用途に文字列を利用も検知、拒否できます。

JWTの場合、署名により改竄されていないことを検証できるので、あとは

  • Header : 用途をこのレベルで分けてしまい、署名検証前にチェックする
  • Payload : 発行日時や有効期限などを持ち、データアクセス前にチェックする

と言った部分を工夫することで、必要最低限のデータアクセスを追求できるのではないでしょうか。

いかがでしたでしょうか?

JWTは "データを保持する際のキーとなる乱数文字列" をメタデータとともに保持することで、キーの種類に関わらず改竄不可能な値として扱えて無駄を省いたハンドリングが可能なので、セキュアなトークンとして利用するのに便利な仕組みじゃないですか? というお話でした。

ではまた。