公開APIの認証を、④で扱った秘密の保管から、資格情報の発行・権限・要求数制限・失効までつなげて考えます。

認証とセッション

 認証とセッション⑨

 公開されたWeb APIに、何を見せてアクセスするのか

C#で作ったバックエンド、Windowsの業務アプリ、Flutterで作ったスマホアプリ。

Web APIの便利なところは、異なる環境で動くアプリから、同じ機能を呼び出せることです。HTTPで要求を送り、JSONで結果を受け取る。そのやり取りができれば、呼び出す側の言語やOSをそろえる必要はありません。

バックエンド・スマホ・PC・ブラウザーから同じWeb APIを利用する概念図
言語や端末が違っても、HTTPを通じて共通の機能を呼び出せます。

APIをAzure App Serviceなどに配置すれば、社外の端末からも利用できます。しかし、インターネットへ公開された入口には、正規のアプリ以外からも要求が届きます。

自分が作ったアプリから呼べるということは、第三者が作ったプログラムからも呼び出せる可能性がある、ということです。

そこで今回は、⑥のCookie認証と比較しながら、アクセストークンを取得し、Bearer方式でAPIへ送る流れを追います。そのうえで、秘密の保管と公開された認証窓口の運用を考えます。

APIのURLを知っているだけでは、利用者の証明にならない

公開Web APIで認証情報と操作の権限を確認し、不足する要求を拒否する概念図
APIへ届くことと、その機能の利用を許可されることは別です。

Web APIのURLは、機能を呼び出すための宛先です。宛先を知っていることと、その機能を利用してよいことは別です。要求に「正規のアプリです」と書いてあっても、それだけで信用するわけにはいきません。

確認すること役割
誰から、どのアプリからの要求なのか認証
その呼び出し元に、この操作を許可してよいか認可

たとえば、ある顧客の端末として認証できても、別の顧客のデータを取得させてよいわけではありません。入口で身元を確認した後も、操作やデータごとに権限を確認します。

⑥のCookie認証から、Bearer認証へ

⑥では、ログインに成功するとサーバーが認証Cookieを発行しました。その後は、ブラウザーがCookieの送信条件に従って、次の要求へ認証Cookieを添えます。

今回取り上げるBearer方式では、呼び出すアプリがアクセストークンを取得し、HTTPのAuthorizationヘッダーへ設定して送ります。SDKや認証ライブラリが、この設定を担当する場合もあります。

比較する点⑥のCookie認証今回のBearer方式
要求に添える場所CookieヘッダーAuthorizationヘッダー
誰が添えるかブラウザーが送信条件に従って添えるブラウザーや、Cookieを管理するHTTPクライアントアプリや認証ライブラリ
確認するもの認証Cookieの有効性と、それに基づく権限アクセストークンの有効性と、それに基づく権限
ブラウザーなしで使えるか使える使える

Cookie認証は、ブラウザー専用ではありません。

ブラウザーを持たない送信プログラムでも、ログイン時にサーバーから認証Cookieを受け取り、以後の要求へ添えれば、Cookie認証を利用できます。C#のHttpClientでは、CookieContainerを使ってCookieの保持と送信を任せられます。

⑥ではブラウザーがこの役割を担いましたが、送信プログラムのHTTPクライアントも同じ役割を担えます。ただし、ブラウザーに保存されているCookieが、別のプログラムへ自動的に引き継がれるわけではありません。

一方、ブラウザーからBearer方式のAPIを呼ぶこともできます。CookieとBearerを選ぶ基準は、ブラウザーの有無ではなく、認証基盤との連携や資格情報の管理、APIの設計です。 今回は、その代表的な選択肢としてBearer方式を扱います。

トークンを取得してから、業務APIを呼ぶ

最初の認証と、その後のAPI呼び出しを分けると、役割が見えやすくなります。

順序アプリとサーバーの動き
① トークンを取得するユーザーのログインや、バックエンドの資格情報による認証を経て、認証サーバーからアクセストークンを受け取る
② 業務APIへ要求するアプリがAuthorizationヘッダーにアクセストークンを付ける
③ 検証・認可するAPIがトークンの有効性を検証し、その操作やデータへの権限を確認する
④ 結果を返す許可した処理を実行し、JSONなどで結果を返す

バックエンドがトークン取得に使うシークレットと、業務APIへ送るアクセストークンは、役割の違う資格情報です。認証サーバーと業務APIは役割として分けて説明していますが、必ず別の物理サーバーで動かすという意味ではありません。

Authorization: Bearer ― 実際に送るのはここ

たとえば、作業一覧を名前順で取得する要求は、次のようになります。URL・トークンは説明用で、HTTP/1.1の一部を抜粋した例です。通信にはHTTPSを使います。

GET /api/works?sort=name HTTP/1.1
Host: api.example.com
Authorization: Bearer ACCESS_TOKEN
Accept: application/json

Bearerの後ろに半角スペースを置き、取得したアクセストークンを続けます。ACCESS_TOKENという固定文字列を送るわけではありません。Acceptは、受け取りたい応答形式を示すヘッダーです。

登録処理などでJSONを送る場合も、業務データと認証情報を分けられます。次は本文を含む要求の抜粋です。

POST /api/works HTTP/1.1
Host: api.example.com
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{"name":"資料作成"}

JSONの本文には処理したいデータ、AuthorizationヘッダーにはAPIへのアクセスに使うトークンを載せています。JSONを送れること自体が認証になるわけではありません。

用語意味
アクセストークンAPIへのアクセスに使う資格情報
Bearerトークンを持つ者が、それを提示して利用する方式
JWTトークンの形式の一つ。Bearerで送るトークンが必ずJWTとは限らない

Bearer方式では、トークンの提示に加えて、その要求ごとに別の秘密鍵の所持を証明することは通常求めません。そのため、盗まれたトークンが有効なら、攻撃者にも悪用され得ます。HTTPS、保管先の保護、有効期限、権限の限定が大切になります。

JWTであっても、内容を読み取れたことは、正当性を検証したことにはなりません。詳細な構造と検証は次回扱います。

無人のアプリは、何を提示するのか

人が使うサービスなら、ログイン画面で本人確認を行えます。しかし、夜間に自動実行するバックエンドでは、そのたびに人がスマホを操作する構成にはできません。こうした場合には、アプリ自身の資格情報を使います。

代表例が、OAuthの「Client Credentials」です。バックエンドアプリは、登録されたシークレットや証明書を使って認証し、アクセストークンを取得します。

  • アプリが認証サーバーに、自分の資格情報を使って認証を求める。
  • 認証サーバーが確認し、有効期限と権限を持つアクセストークンを発行する。
  • アプリがアクセストークンを付けてWeb APIを呼ぶ。
  • APIがトークンと権限を確認し、許可された処理を行う。

シークレットは、人が覚えられる短い合言葉である必要はありません。機械が生成した、十分に長くランダムな値を使えます。証明書を使う場合も、通常は秘密鍵そのものを相手へ渡すのではなく、秘密鍵を持っていることを署名によって示します。

そして、そのシークレットや秘密鍵の保管場所で、④の話が再び登場します。APIを呼ぶための秘密を、コードへ直接書かずにどう保持するか。認証方式を決めることと、資格情報を安全に保管することは、セットで考える必要があります。

通信にはHTTPSを使い、資格情報やアクセストークンをURLへ入れないようにします。JWTを使うAPIでは、署名、有効期限、発行者、対象APIなどを検証したうえで権限を確認します。

PC・スマホアプリでも同じ方法を使える?

HTTPで同じAPIを呼べても、資格情報の持ち方まで同じとは限りません。管理下のバックエンドと、利用者へ配布するアプリでは、秘密を保護できる条件が異なります。

呼び出す側認証を考える際の前提
管理下のバックエンド実行環境へのアクセスを制限し、アプリの資格情報を管理する
利用者のPC・スマホアプリ配布したアプリ内の共通シークレットを、秘密のまま保持できるとは考えない
ブラウザー上のJavaScript配信したコードに、秘密として守るべき値を埋め込まない

人が使うPC・スマホアプリでは、ユーザーのログインとAuthorization Code+PKCEなどを組み合わせ、アクセストークンを取得する構成が使われます。

DPAPIなどによる保存時の保護は役立ちますが、それだけで、配布したアプリの中身を利用者から完全に隠せるようになるわけではありません。④で扱った保存の保護と、今回の呼び出し元の証明は、役割を分けて考える必要があります。

期限切れと権限不足では、対応が違う

代表的な応答意味呼び出す側で考えること
401 Unauthorized必要な認証情報がない、トークンが期限切れ・無効など認証状態や期限を確認し、必要に応じてトークンを再取得する
403 Forbidden認証できても、必要な権限が足りないなど権限・対象データ・設定を確認する。再取得だけで解決するとは限らない

APIによっては資源の存在を隠すために404を返すなど、応答方針が異なる場合もあります。ここではBearer認証での代表的な区別を示しています。

有効期限が来ても、すべての方式でリフレッシュトークンを使うわけではありません。Client Credentialsでは通常、アプリの資格情報を使って新しいアクセストークンを取得します。ユーザーがログインする方式では、発行条件に応じてリフレッシュトークンを使ったり、再ログインが必要になったりします。

APIを呼ぶたびにトークンを発行するのではなく、有効な間は再利用し、期限に応じて更新します。401が続くときに無制限の再取得を繰り返さず、失敗の原因と再試行間隔を管理します。

④で扱った「金庫の鍵」の続き

OSやクラウドで資格情報を保管し、認証して取得したアクセストークンでAPIを利用する概念図
④の保管の考え方から、資格情報による認証とAPIの利用へ進みます。④のHMAC鍵をそのままAPI認証へ流用する意味ではありません。

「認証とセッション④」では、「秘密鍵を保管する金庫の鍵は、どこに置く?」という問題を扱いました。

秘密鍵を暗号化して保存しても、その暗号化に使う鍵をソースコードへ書いてしまえば、鍵の置き場所が一段移っただけです。そこで④では、最後の鍵の管理をOSやクラウドの仕組みに任せる考え方を説明しました。WindowsのDPAPIや、Azure Key VaultとマネージドIDが、その具体例です。

今回は、その先です。保管した秘密を使って、別のサーバーに自分の身元をどう示すのでしょうか。

秘密の役割使用する場面
保存した情報を保護する鍵データの暗号化・復号
アプリの身元を示す資格情報認証サーバーへの認証
トークンの正当性を保証する鍵トークンの署名・検証

いずれも保護が必要ですが、用途や持つ人は異なります。④の「どこに保管するか」という考え方を引き継ぎながら、今回は「誰が持ち、どこへ提示し、何を許可されるか」を追っていきます。

図の「④で守った秘密」は、④の保管方法を引き継ぐという意味です。暗号化用・認証用・署名用の鍵を、同じ値で使い回すことを勧めるものではありません。

マネージドIDなら「金庫を開けるための秘密」はどうなる?

AzureのマネージドIDを利用できる構成では、アプリの身元をAzure側で管理し、トークン取得に必要な資格情報の管理もプラットフォームへ任せられます。

④で登場した「マネージドIDを使ってKey Vaultへアクセスする」という構成も、その一例です。Key Vaultを開けるための固定シークレットを、アプリのコードへ追加する必要をなくせます。

ただし、アクセスを許可する設定は必要です。どのマネージドIDに、どの秘密や機能の利用を許可するかを管理します。秘密の保管と更新を任せても、権限を決める責任は残ります。

「ログイン画面をネットに晒すな」とは、何の話か

ルーターやNASなどでは、「管理画面をインターネットに公開しない」という話を聞きます。こうした画面を使うのは、限られた管理者です。世界中から接続できる必要がなければ、まず接続可能な範囲を狭めます。

CISAも、米連邦民間行政機関を対象とする指令で、対象となる管理インターフェースを公開インターネットから除去するか、別のアクセス制御によって保護するよう求めています。

一方、各地の顧客端末が利用するAPIでは、外部から接続できること自体が必要です。その場合は、公開する範囲を必要な機能に絞ったうえで、外から届く要求のうち、どれを受け付けるかを設計します。顧客向けAPIを公開することと、管理機能まで公開することは分けて考えます。

長い秘密があれば、総当たりされても大丈夫?

十分にランダムで長い秘密なら、総当たりで正解を見つけることを現実的でなくできます。ただし、それで公開APIの問題がすべて解決するわけではありません。

たとえば、認証を試すたびにDBへ接続し、計算量の大きい検証処理を実行するとします。攻撃者は認証に成功しなくても、大量の要求によって処理を繰り返させられるかもしれません。

  • 不正な相手を認証しないこと。
  • 不正な要求で、正規の利用まで妨げられないようにすること。

そのため、認証結果だけでなく、要求の頻度も制御します。IP単位だけに頼らず、アカウントやAPIキー単位などの制限も検討します。

顧客端末を締め出さずに、要求を制限したい

「連続で何回か失敗したら、一定時間ロックする」。分かりやすい対策ですが、無人端末では悩ましいところです。資格情報の更新が行き違ったときなどに、正規の端末まで利用できなくなる可能性があります。

さらに、攻撃者が故意に認証を失敗させ、正規利用者をロックすることも考えられます。OWASPも、このようなサービス妨害への注意を求めています。そこで、「認証失敗」と「異常な要求の集中」を分けて考えます。

対策目的
短時間の要求数に上限を設ける大量要求による負荷を抑える
端末側で再試行の間隔を延ばす障害中の要求集中を避ける
再試行のタイミングを端末ごとにずらす多数の端末が一斉に再接続することを避ける
制限を一定時間で自動解除する一時的な制限から復帰できるようにする
失敗の継続を記録・通知する設定不整合や攻撃を調査できるようにする

端末側の再試行制御だけでは、攻撃者の要求は止められません。サーバー側の制限も必要です。また、資格情報そのものが無効なら、待つだけでは復旧しません。安全に更新・再発行する手順を用意します。

目指すのは、異常な要求を抑えながら、正規の顧客端末が復旧できる設計です。

金庫へ入れる前に、誰へ鍵を渡すのか

④では、秘密を安全に保管する方法を考えました。しかし、安全な金庫へ保管していても、最初から誤った相手に資格情報を発行していたら、利用者を限定できません。

  • 登録:利用を許可する相手を、どう確認するか。
  • 発行:資格情報を、正しい相手へどう届けるか。
  • 保管:端末やサーバー上で、どう守るか。
  • 利用:何を提示し、どの操作まで許可するか。
  • 更新・失効:期限、漏えい、端末交換、契約終了にどう対応するか。

登録や資格情報の復旧も、認証と同じように守る必要がある入口です。

「金庫の鍵をどこに置くか」は、秘密を守るための問題でした。公開APIでは、そこからさらに、「誰に資格情報を渡し、何に使わせ、いつ無効にするか」まで考えます。

Web APIの幅広さを支えているのはHTTPによる共通の呼び出し方です。そして、その便利な入口を安心して利用するためには、認証・権限・資格情報の管理・障害からの復旧を、ひと続きの仕組みとして設計する必要があります。

次回

次回は、Web APIが受け取ったアクセストークンを、どのように確認するのかを取り上げます。

JWTを例に、内容を読み取ることと、正当なトークンとして検証することの違いを整理します。署名・有効期限・発行者・対象APIを確かめたうえで、権限の確認へ進む流れを見ていきます。

参考資料

仕様:RFC 6750 — OAuth 2.0 Bearer Token Usage/RFC 6749 §4.4.3 — Client Credentialsのトークン応答