日曜大工で作っているウェブアプリケーションにログイン機能をつけた。AWSに大変お世話になった。
入門書を読んだ後、認証をいざ自分でやるとなった場合、恐る恐る以下のようなことをするのだろうなあと思っていた。
- ユーザーのuser/passにsaltつけてたくさんハッシュしてデータベースに保存
- ブラウザはuser/passをサーバーに送る
- サーバーは受け取ったuser/passを保存してあるものと比較して認証
しかしながらAmazon Cognitoのおかげで、以下のような形になった。自身のアプリケーションがuser/passをどんな形でも保存せず、in transitでも関わる必要がなく、大変ありがたい。
- ユーザーはブラウザでuser/passを入れる
- ブラウザはAmazon Cognitoを相手に認証を試み、成功したらtokenがもらえる
- ブラウザはAmazon Cognitoからもらったtokenを認証成功の証としてサーバーに送る
- サーバーはtokenが有効かをチェックし、有効ならブラウザとやり取りを開始する
結果、守るべきはtokenとなる。寿命が短く、また、他のアプリケーションでの再利用ができないので、パスワードを守るよりずっとよい。
というわけでブラウザからAWSにアクセスするのでJavaScriptのAWS SDKを使う。道中、以下のような学びがあった。
Secure Remote Password Link to heading
ブラウザがAmazon Cognito相手に認証を試みる時、どうやらこのコネクションはinsecureで問題ないらしい。おお。SRPというプロトコルなんだそうだ。以下読むと、公開鍵暗号に似ている。
The Secure Remote Password Protocol
恐る恐る読み始めたけど、細かいanalysis気にしないならそんなに難しくない。
JSON Web Token Link to heading
JWT、たまに見た気がする。これだったのかな。
Amazon CognitoはJWTという形でtokenをくれる。認証されたユーザーの情報の後に、有効期限や、Amazon Cognitoが認証しましたよという署名がついている。ああ、デジタル署名とはこのように使われるのだなあと感じることができる。
署名のチェックに必要となるAmazon Cognitoからの公開鍵は、(e,n)
で得られる。署名チェックにはPEMフォーマットが必要なので(libraryによるだろうけど)ちょっと面倒だが、ああ、公開鍵はそもそもは(e,n)
なんだなあと感じることができる。(c.f. Using Tokens with User Pools)
RFCは当然長いが、必要に応じてつまみ読みするだけだから何ら恐れることはない。
その他 Link to heading
AWSのドキュメントはやはり親切ではないが、まあこんなもんかなとも思うようになった。AWS全般のSDKがあるが、そちらではなく、amazon-cognito-identity-jsを見つけてしまえば特に困ることはない。最初AWS SDKで作り始めたが、SRPの公開鍵にあたるものを自分で作らなきゃいけなくなり、困った。
JWTのverificationはtoken自身に静的に行われるため、こういう使い方をしている限りはtokenの強制的なrevokeができない。一時間の寿命のうちに盗まれたら、それに気付けても悪用を防げない。大事なオペレーションの前にはtokenのrefreshを要求するのだろうが、できることはそれくらい?世の中のアプリケーションではこんな感じでJWTは広く使われているんだろうか。
ししまい。