ブラウザの認証情報保存に使える新しい仕様についてのアイデア Authorization Storage(仮名)
実情
現在、Webアプリケーションによるxhr
やfetch
を使うときの認証情報の送信には2つの方法があります。
Cookie
とAuthorization
ヘッダーです。
現在はCookie
のほうがよく使われていると思います。
なぜ、Auth方式が使われていないかというと、トークンを保存するとき、ローカルストレージなどを使う必要が出てきますが、ローカルストレージは基本的にJavaScript
などからアクセスし放題で、XSS
など発生した場合、認証情報が外部に流出する可能性があるからです。
ではCookie
を使うのが万能かというとそうでもありません。
例えば、TweetDeck
のような一つの画面で複数のアカウントのデータをやり取りするようなアプリケーションを作るとき、セッション情報に複数のアカウントを紐付け、APIを叩くときにどのアカウントからの要求か、というのをいちいち指定しなければならなく、これはとてもスマートではありません。
実際にTweetDeck
の通信内容を解析してみましたが、そのような実装になっているようでした。
Auth方式を使えば、JavaScript
でトークンを指定できるので、実装がシンプルになりますが、セキュリティに欠陥があります。
実装
今回、この問題を解決するAuthorization Storage
というのを思いつきました。
Authorization Storage
はブラウザに搭載される保存領域です。
※以降、コードを書いていきますが、適当に思いついた実装なので、問題があると思います。
Authorization Storage
はLocal Storage
やSession Storage
のようにドメイン単位でキーバリュー形式で文字列の保存ができます。
authStorage.setItem('user1', 'Bearer ThiSisRanDoMeGeNerateDToken');
ただし、JavaScript
側からバリューの読み込みはできません。
代わりに、保存されているキーのリストは読み込むことができます。
console.log(authStorage.keys()); // ['user1']
削除することもできます。
authStorage.removeItem('user1'); // remove user1 token
では読み込めないデータをどのように使うか。
それは、xhr
やfetch
などを使うときに使用できます。
const url = 'https://example.com/api/1.0/articles';
const userId = 'user1';
await fetch(url, {authKey: userId});
上記のfetch
のリクエストは Authorization: Bearer ThiSisRanDoMeGeNerateDToken
を含んだリクエストとなります。
また、この機能を使ってRequest
が取得できる場合、Authorization
ヘッダーにアクセスできないようにする必要があります。
(そもそもfetch
やxhr
発行後にヘッダーは読み取れるんですかね? そのへん詳しくないのでわからない…)
終わりに
この仕組を実装すれば、セキュアかつ柔軟性のあるトークンの保存が可能になると思われます。
反論、問題点など自由にコメントお願いします。
長々と読んでいただきありがとうございました。
authStorage.setItem
はブラウザのAPIだとすると、セットするまではトークンを閲覧できる感じでしょうか?完全にクライアントから見えないhttp-only cookieと比べるとセットするまでの値の取り扱いは少し注意が必要そうですね。
保存されているキーのリストは読み込むことができます。
これ良いですね。http-only cookieだと「値が保存されているかどうか」が分からず困ることが多いので、このような操作ができるとありがたいところ…
そうですね。ただ、アプリのJavaScriptを読み込む必要がありますし、そこまでできるならもっと高度なハッキングも可能なので(アプリにもよりますが)。
Cookieよりは少し弱いですがそれでも十分に強いと思っています。
ありがとうございます。そうですよね、localStorageにトークンを入れるよりずっと安全だと思います。このあたりの仕様について自分で新しい仕様を考えるようなことが無かったので、とても良い機会になりました。
ご返信ありがとうございました。
WHATWGに提案してみました
CORS対応が難しいな…