ブラウザの認証情報保存に使える新しい仕様についてのアイデア 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対応が難しいな…