OIDC の ID トークンの sub は DB に平文保存してよいかの一考察
よく見かける「 Google でログイン」みたいな外部 IdP(Identity Provider) を利用した認証でよく使われるのが OpenIDConnect(OIDC) の ID トークンだ。
特に payload の sub(Subject) に「その IdP 内でのユーザー固有 ID 」が入っている。
通常「 Google でログイン」を実装する場合、この sub の値をユーザーと紐づけて RDB 等に保存することになる。
で、ふと思った。いままでなにげに平文で保存していたけど、これで大丈夫? と
ということで平文保存でいいのか考察してみる。
なんで平文保存に疑問を持ったのか?
これはシンプルでパスワードとの比較。
パスワードを DB に保存する場合の定石は bcrypt や Argon2 などのパスワードハッシュ関数を通してパスワードハッシュという形で保存する。
さて ID トークンにおける sub 。これはどう保存する? これがはじまり。
結論
いきなり結論を書くと 平文保存で構わない
まずはパスワードの保存方法について
まずパスワードをパスワードハッシュとしてなぜ保存するかを考えてみる。
真っ先に思いつくのは DB データ流出時への対策。
もし、パスワードを平文保存していたら流出した瞬間にアウト。
また、流出してしまった場合、パスワードを見つけられるまでの時間を稼ぐため salt やストレッチングを行う必要があり、パスワード専用の一方向ハッシュ関数(bcrypt, Argon2 etc)が生まれた。
それでは sub が流出したら?
パスワードの時と同じく DB データ流出が起きてしまったと考える。
平文保存だと sub の値はすぐに分かる。
これはまずいだろうか?
結論から言うと「まずくない」と考える。
なぜまずくないのか?
まずは ID トークンまで話を遡る。
ID トークンは IdP 側の認証プロセス(例えば Google アカウントでのログイン)を経て、正当な署名付き ID トークンが発行され、それをバックエンドで検証しない限りログインは成立しない。
つまり流出した sub の値を使って ID トークンで「なりすます」ことは不可能である。ちゃんと実装されていれば。
また、 ID トークンには資格情報(パスワードとかアクセストークンとかね)は含まれない。 OIDC ではアクセストークン、リフレッシュトークンは別途発行されるものであり ID トークンには含まれない。
つまりどういうことか。
ID トークン、及び sub の値がわかっても API へのアクセスは出来ない。ちゃんと実装されていたら。
なので結論としては 平文保存 で問題ない。
注意事項
OIDC や OAuth2.0 を実際に使ったことある人は分かる通り、仕様と現実のギャップが存在する。
ID トークンの payload の sub ではないクレームにユーザー固有 ID が入っていたり、 OAuth2.0 のはずなのに認証情報まで含まれていたり。
このような事情があるので、必ず IdP から発行された ID トークンの payload の確認を行う必要がある。
法的な注意点
個人情報保護法では sub は個人を特定しうる情報(PII)に含まれるとも解釈できるので、メールアドレス等と同様、適切なアクセス制御下で管理する必要がある。