認証・認可とOAuth2.0と
2022-05-01

Photo Credit: Maria Ziegler
GCPのリソースにアクセスしたり、Google APIにアクセスしたりといった実装をすることが直近多かったのですが、認証認可にはgoogle-auth-library-nodejsを活用して、Google推奨の方式であるADC(Application Default Credentials)を用いて雰囲気で実装していました。
いまいち仕組みがわかっていなくても動くし、最低限セキュアな実装にはできていたのですが脱雰囲気実装をしたく、認証・認可及びOAuth 2.0について改めて調べ直したのでそのメモです。
仕組みを理解し、裏側で何が起こっているのかをイメージした上で実装できるようにすることが今回の目的だったため、個々の技術は一部簡略化して記載しています。
TL;DR
- 認証・認可では誰がアクセスしてきていて、どのリソースに対してアクセスする権限があるのか確認している。
- OAuth2.0では、最終的にアクセストークンを鍵として扱っているだけ。
そもそも、認証・認可の違いは?
認証
「誰が」アクセスしてきているのかを確認したい。
認証だけが完了した段階では、アプリケーションにアクセスすることはできるが、ユーザーに 紐づいて管理されているリソースにはアクセスできない状態。
例えば仮に、Twitterで認証は行えたが認可は行われていない状態ならば、パブリックなツイートを見ることはできるが、鍵アカのツイートや自分のツイート、DMなどは見ることができない状態。
認可
認可ではアクセスしてきた人がどのリソースに対しての権限を持っているのかを確認したい。
通常、認証で本人確認が完了したのちに認可が行われ、ユーザーがリソースにアクセスする権限を持っているかどうかを確認する。
先ほどのTwitterの例だと、認証後に認可を行いリソースに対するアクセス権を確認することで、自分のDMやフォローしている鍵アカのツイートは見ることができるようになるが、フォローできていない鍵アカのツイートや他人のDMは見ることができない。
おまけ:HTTPステータスでの使い分け
HTTPステータスで400系エラーコードは不正なリクエストに対するエラーコードですが、その中でも認証認可は使い分けがされています。
401 Unauthorized | 認証失敗
HTTP
401 Unauthorized
は、有効な認証資格が不足していることによりリクエストが適用されないことを示すクライアントエラーのレスポンスコードです。 401 Unauthorized - HTTP | MDN
403 Forbidden | 認可失敗
HTTP の
403 Forbidden
クライアントエラーレスポンスコードは、サーバーがリクエストを理解したものの、認証が拒否されたことを示します。
このステータスは [401](https://developer.mozilla.org/ja/docs/Web/HTTP/Status/401)
に似ていますが、この場合は再認証しても結果は変わりません。アクセスは恒久的に禁止されており、リソースにアクセスする権限が不足しているなど、アプリケーションのロジックに結びついたものです。
403 Forbidden - HTTP | MDN
*MDNでは「認証が拒否されたことを示します。」と書かれているので、認可エラーではなく認証エラーじゃないか。と思ったのですが、続きの文言を解釈しググったところ、認可エラーで403を使うことが一般的であるようなので、403 Forbiddenを認可エラー時に使用すると捉えて問題なさそうです。
Webアプリケーションにおける認証方式
認証認可は別物である。ということがここまででわかりました。
まずは世の中にはどんな認証方式があるのか見て行きたいと思います。
Basic認証
ユーザーIDとパスワードの組みをBase64でエンコードしてAuthorizationヘッダーに詰めてサーバーに送信することで認証を行う方式です。
//Base64エンコード後(実際にリクエストヘッダーに入っている値)
Authorization: Basic c2hnbmtuOm15cGFzc3dvcmQ=
//エンコードする前
Authorization: Basic shgnkn:mypassword
実装が楽なので活用されることが多いようですが、ユーザーIDとパスワードの組みをBase64でエンコードしているだけなので通信が傍受されてしまった場合ユーザーIDとパスワードがダダ漏れです。そのため、活用する際には基本的にSSL /TLS(HTTPS)で通信を暗号化して活用します。
Digest認証
Basic認証と基本的には同じで、AuthorizationヘッダーにユーザーIDとパスワードの組みを詰めてサーバーに送信することで認証を行います。
Basic認証と異なる点としては、Authorizationヘッダーに情報を詰める前に、最初のレスポンスでサーバーから返ってきたランダムな文字列とクライアントで生成したランダムな文字列、ユーザーID、パスワードをハッシュ関数で暗号化してから詰めるので、セキュリティが向上しています。
平文がそのまま送られてしまうBasic認証よりはセキュアになっています。最新のブラウザはSHA-256に対応していますが、古いブラウザだとMD5にしか対応していない場合もあるのでその場合には注意が必要です。(MD5は既に脆弱性が見つかっているため非推奨です。)
Bearer認証
OAuth 2.0 で保護されたリソースにアクセスする際にBearerトークンを用いる方式です。
今回はOAuth 2.0の学習をメインで行ったので、詳細は後ほど記載します。
その他認証方式
今までBasic認証、Digest認証、Bearer認証は触れたことがあったのですが、今回認証方式にはどんなものがあるのだろうと調べた際に思ったよりも色々な種類があって驚きました。
詳細は触れませんが、IANAに記載されていた認証方式を列挙しておきます。
- HOBA
- Mutual
- Negotiate
- OAuth
- SCRAM-SHA-1
- SCRAM-SHA-256
- vapid
OpenID Connect
認証方式には上で挙げたように様々な方式がありますが、認証の機能を自前で実装するのは大変です。なぜなら、ユーザーIDとパスワードの組みを自前のDBに保存する必要があり、そのDBをハックされたらお終いなので、セキュリティで気を使うべきポイントが一気に増えて しまいます。
そこで、Googleアカウント、Twitterアカウント、Facebookアカウントなど外部サービスのアカウントを用いて認証を外部に委託するといったことがよく行われます。
この、認証を外部に委託する仕組みがOAuth2.0を拡張したOpenID Connectと言われるもので、auth0などのベンダーによって機能が提供されています。
今回OpenID Connectの仕様を深く調べることはできていないので、詳細は省きます。
OAuth 2.0
認証方式にどのようなものがあるのかは1つ前のパートで見てきました。
Bearer認証でOAuth2.0の話が登場しましたが、OAuth 2.0は認証方式を定めたプロトコルではなく、「アクセストークンの要求とその応答」を標準化したプロトコルです。
アクセストークンを発行する認可サーバーを自前で実装するのが大変なため、AuthleteやAuth0といったサービスを利用することが多いかと思います。