コペンハーゲン・ブック: Webアプリケーション認証実装ガイド
(thecopenhagenbook.com)- Webアプリケーションで 認証実装 を設計する際に参考にできる基本ガイドであり、オンラインの認証資料の空白を埋めることを目的としている
- 無料・オープンソース の資料であり、コミュニティが保守する形で提供されている
- 一部の内容は 意見が反映 されていたり不完全だったりする可能性があるため、単独の基準として使うより補助資料として活用するのが適切
- 認証セキュリティの参考資料である OWASP Cheat Sheet Series とあわせて使うことを推奨
- 提案や懸念事項は新しい Issue を作成して伝えることができ、文書改善に参加できる
文書の目的と性格
- The Copenhagen Book は、Webアプリケーションで 認証(auth) を実装する際に参考にできる一般的なガイドラインを提供する
- 運営形態は次のとおり
- 無料で提供される
- オープンソースである
- コミュニティが保守する
- 内容はときに意見が反映されていたり、完全ではなかったりすることがある
あわせて参考にする資料と参加方法
- 認証実装時には OWASP Cheat Sheet Series とあわせて参照することを推奨
- 提案や懸念事項があれば、新しい Issue を作成できる
1件のコメント
Hacker News のコメント
勘違いでなければ、この記事は TypeScript 向けの人気認証ライブラリ Lucia の作者が書いたものらしい
最近、Lucia ライブラリはもはや認証実装における人間工学的な方法ではないと判断し、ライブラリを廃止して文章によるガイドシリーズに置き換えると発表した
初期のガイドのプレビューは読みやすく、The Copenhagen Book ともよく合っていた
https://github.com/lucia-auth/lucia
https://github.com/lucia-auth/lucia/discussions/1707
https://lucia-next.pages.dev/
複雑さが爆発しそうな兆候を見て、ライブラリが本人にとってももはや有用ではないことを認めたうえで、ありがちな ライブラリ肥大化 の標準的な道筋から謙虚に降りた点は珍しい
実際、99% の人に必要なのはログイン/ログアウトだけで、これはライブラリとして提供されると非常に便利だ
Web 8.0 のパスキーを WASM 楕円曲線ソケットで提供しなければならないなら、自作するか Auth0 を使えばいいだろうが、CRUD 料理アプリを作っている人に OAuth 仕様と落とし穴リストを投げて認証を自作しろと言うのはおかしい
その人は配管を再発明するより実際のアイデアに集中すべきで、多くの人が結局誤って実装し、事実上認証がない状態になりかねない
この部分は間違っている: 「メールアドレスは大文字小文字を区別しない」
https://thecopenhagenbook.com/email-verification
メール標準によれば メールアドレスは大文字小文字を区別する
送信過程でメールアドレスを小文字化すると、別の人にメールが届く可能性があり、認証では危険だ
多くの有名メールプロバイダーは大文字小文字を区別しないことを選んでいるが、一般的な認証を扱うサイトなら一般的なケースを推奨すべきだ
https://stackoverflow.com/questions/9807909/are-email-addres...
付け加えると、ある文字の反対の大文字・小文字が何なのかは常に明確とは限らず、時間とともに変わることもある。たとえばドイツ語の大文字 ß は 2008 年に Unicode に追加されたので、一般的なプログラミングでは可能なら大文字小文字の区別を避けるほうがよい
標準は一つのことを言っているが実装は違う動きをしており、この場合、標準の文言だけに従うと現実世界で問題が起きる
こうすればメールは最初に入力された大文字小文字のアドレスに送り、ログインは大文字小文字に関係なく可能になる
欠点は、大文字小文字だけが異なるメールアドレスを持つ 2 人のユーザーを別々に置けないことだが、その程度は問題ないと思う
メールアドレスは大文字小文字を区別しなかったが、メールから作られた ユーザー名は大文字小文字を区別 していたため、大文字を含むメールアドレスでアカウントを作ると、その大文字小文字どおりに入力しないとログインできず、大文字小文字を無視したメールアドレスではログインできなかった
その次には言語と文化の違いもなくせば、何十億行ものコードが消えるはずだから、リポジトリがもう縮んでいく音が聞こえる
たとえば、あるユーザー項目には JohnDoe@example.com、別の項目には johndoe@example.com が入っていて、明らかに同一人物というケースだ
しかも別々のシステムから入ってきた値だったので、さらに厄介だった
大文字小文字を区別しないメールアドレスのリスク行列のほうが、大文字小文字を区別するメールアドレスよりはるかにましだと思う。つまり、すべてのメールアドレスを小文字に変換するほうが正しく、The Copenhagen Book もこの点では正しい
とても良い。セキュリティ資料の 90% は、セキュリティ専門家でなければ到底理解できないように作られているように感じて、いつも不満だった。特に 暗号学の資料 がそうだ
だが、ここのほぼすべてのページは明確で、簡潔で、要点を突いており、すぐ適用できるので気に入っている
ただし楕円曲線のページは、他の暗号学資料と同じくらい理解しにくかった
動作原理そのものが数学なので、本質的に不透明だと思う
有限体や楕円曲線、実質的には整数群を理解してから、多くの暗号学を把握できるようになり、たいていは何らかの形で 離散対数問題 だった
似たテーマの代替ドキュメントもあるとよい。たとえば OWASP Cheatsheet のようなものだが、より実用的な観点のドキュメントのこと
敬意は払うが、この文書には少し懐疑的
名前がかなり大げさだ。まるでコペンハーゲンの大学研究者たちが書いたかのように文書を呼ぶのは、見事なマーケティング上の小技だ
Lucia が比較的人気のあるライブラリなのは確かだが、だからといってベストプラクティスを推進しているとか、著者をこの重要分野の権威と見るべきだという意味ではない
Lucia の設計で気に入らない部分もある。ユーザートークンがほぼ期限切れになるとき、新しいセキュリティトークンを作るのではなく、既存トークンの寿命を延長するよう提案している
トークンが事実上永遠に生き続け、悪用され続け得るので、非常に安全でない挙動に見えるし、制限されたトークン寿命というセキュリティのベストプラクティスに反している
Lucia と “Copenhagen Book” はどちらもこの方式を推奨している: https://thecopenhagenbook.com/sessions#session-lifetime
これはよくある方式で、通常はローリングセッションと呼ばれる
トークン自体はそもそも不変であるべきなので寿命を変更できるべきではなく、そのためトークンの更新は元のトークンを変えるのではなく、新しいトークンを渡す方式になる
1つ目の場合、攻撃者はトークンを盗んで永遠に使う。2つ目の場合でも、攻撃者がトークンを盗んだあと、期限切れ直前に新しいトークンを受け取れば、ユーザーになりすまし続けられる
ユーザーが締め出されれば何かに気づくかもしれないが可能性は低く、良いユーザー体験のためにはどうせ猶予期間が必要になる。そうしないと並列リクエストで正当なユーザーにも問題が起きる
2つ目のトークン、つまりリフレッシュトークンを使うことはできるが、リスクをそのトークンへ移すだけだ。今度はリフレッシュトークンが盗まれて永遠に悪用されることを心配しなければならない
リフレッシュトークンは、リクエストごとにデータベースを叩かなくてよい点で有用だ。通常、短命のセッショントークンは署名付き JWT のようにデータベースなしで検証できる
ただし盗難時に無効化できず、有効期限までは有効なので、被害を抑えるには期限を短くする必要がある
一方、リフレッシュトークンではデータベースを参照する。2つ目のトークンそのものがセキュリティを加えるのではなく、データベース参照がセキュリティを加える。データベースから削除して無効化できるからだ
Lucia は少なくとも例を見る限り常にデータベースを参照するので、いつでもトークンを無効化できる
リスクを減らすには、ユーザーがアクティブなセッションを見て終了できるようにすべきだ。可能なら時刻、場所、デバイス情報も一緒に表示するとよい。例: “昨日午前12時にコペンハーゲンの iPhone からログイン”
新しい場所やデバイスからログインしたらユーザーに通知することもできる
結局、長期間続くセッションを完全に安全に実装する方法はない
良い指摘だと思うが、自分が専門家だとは言えない
OAuth で誰もが見落としていて、あまり表に出てこないことが2つある
そのうち1つを誰かが指摘してくれてうれしい。トークンを使うときは、トランザクションを分散ロックメカニズムとして必ず使うべきだ
リフレッシュトークンではこの重要性が2倍、4倍になる。同じトークンを2回以上使うと、そのユーザーはログアウトされる
システムが1台で動いていようが N 台で動いていようが関係ない。リフレッシュトークン付きのリクエストが2つ以上同時に進行していると、これは非常によく起きるのだが、ユーザーをログアウトさせ、しばしば 500 で終わる
リフレッシュトークンは使い捨てだ
開発者と認証フレームワークが見落としているもう1つは “state” パラメータだ
最近の認証が全体として進んでいる方向を見ると、曖昧さによるセキュリティではなく、曖昧さによる不安定性に近づいているように思える
アプリケーション状態まで絡むと、認証に合わせてアプリケーションロジックを調整しなければならず、誰かがリフレッシュトークンを盗んだかどうかをアプリケーションが確認しなければならない
トランザクションだけでは解決しない。たとえばタブを2つ素早く開くと、元のリフレッシュトークンでサーバーを2回叩くことになる
その文書ではリフレッシュトークンのローテーションを好ましいとしているが、クラスター環境での明らかな難しさにも触れている: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.2....
複数のリクエストは常に同時に発生する。たとえばブラウザが複数タブで起動したり、スマートフォンアプリが起動時に複数のリクエストをまとめて送ったりすることはよくある
もっと多くのウェブサイトに「ログアウトするまでセッションを期限切れにしないでほしい。リスクは理解している」という選択肢を用意してほしい
最近の「remember me」ボタンは何の効果もない
セッション期限切れのせいで一日が何度も中断されるのにはうんざり。GitHubは特に嫌いで、週1回ほど使うだけなのでセッションがいつも期限切れになっており、二要素認証まで強制されるため、毎回スマホを取り出して数字を入力しなければならない
ユーザー体験もひどいが、根拠はないにせよ、こうして何度もログインさせるとユーザーが疲れて注意力が落ちると思う
あるサイトに毎週何度もログインしていると、60回目くらいのログインではフィッシングサイトが紛れ込みやすくなる。逆に、ほとんどログインすることのないアカウントが突然パスワードを要求してきたら、不自然に感じて警戒心が高まる
結局、企業は人によってリスク許容度やセキュリティ態勢が違うことを学び、選択肢を提供すべき
付け加えると、GitHubの頻繁なセッション期限切れと二要素認証があまりに鬱陶しかったので、Giteaに移行して期限切れをオフにしてしまった。移行理由の90%はそれだった
自分のネットワークからしかアクセスできないので、むしろセキュリティは良くなったと感じている。柔軟性のないセキュリティモデルのせいで企業が顧客を失うことは十分あり得る
ざっくり言うと、30日以内に使われていればセッションをさらに30日延長する方式
https://thecopenhagenbook.com/sessions#session-lifetime
さらに悪いのは、セッションが1週間より1〜2分ほど長く続くように見えるため、今週は先週の作業を始めた直後に追い出されることになる点
数週間前、定例会議の直前にメモを取ろうとしてログインしたのだが、その後数週連続でその会議の途中にログインを強制された
そうした「remember me」ボタンを無効化し得る設定は非常に多い
個人的にはGitHubを含め、ウェブサイトのログイン維持で問題が起きたことはない
最近SRPプロトコルを知ったが、もっと広く使われたり言及されたりしていないのが驚き
比較的シンプルなプロトコルでゼロ知識証明を行い、サーバーとクライアント間のセッショントークンまで一度に作れる
https://en.wikipedia.org/wiki/Secure_Remote_Password_protoco...
この資料は気に入った。多くのセキュリティ助言は難解で、ときにはばかげているようにも感じる。まるで何もするなと助言する弁護士のようだ
このガイドは驚くほど簡潔で、追いやすく、理解しやすく、率直な助言があるのが良い
反対意見や注意点があるかコメントを見続けるつもりだが、自分の認証プロジェクトと照らし合わせて検討することになりそう
ひとつ望むなら、JWTセクションがあればよかった。著者の意見が「使うな」なら、そう言うだけでもよい
セキュリティ担当チームについてよく批判する点のひとつは、人々がやりたいことを効率的にできるツールや方法を先回りして提供するよりも、してはいけないことを伝えるのに多くの時間を費やしていること
メールはより安全なシステムと見なせるので、即時に、あるいはどこからでもアクセスできるとは限らない
ここでいう「auth」は認証(authn)を指すのか、認可(authz)を指すのか気になる
見たところ認証を指しているようだが、明確にしてくれるとよい
少し関連した短い愚痴だが、内蔵ブラウザがウェブを台無しにしている
内蔵ブラウザはソーシャルOAuthの利用を不可能にする。場合によっては文字どおり不可能で、別の場合には事実上不可能
InstagramでリンクをタップするとデフォルトでInstagramブラウザで開くが、そのリンクに「Sign in with Google」があると、GoogleがInstagramのような「安全でないブラウザ」をブロックするため動作しない
「Sign in with Facebook」すら問題が起きる。MetaはInstagramとFacebookの両方を所有しているのに。Facebookの内蔵ブラウザでも似た問題がある
Slackのような場所でリンクをタップした後にメッセージに返信し、またブラウザに戻ってそのページがあることを期待したのに、Slackが自分のブラウザ内に飲み込んでしまってどこにもない、という場合はほぼ常に役に立たない
幸い、今確認したところSlackでは内蔵ブラウザをオフにする方法がある