これはSOUSEI Technology アドベントカレンダー3日目の記事です。
記事の内容は会社の業務とは一切関係ありません。
前置き
セキュリティ的に良いとは言えないですが、まだ開発中で非公開だし、クローラやボットの巡回で画面が表示されたりするのを避けられれば良いやくらいのお手軽な手法として、今でもBasic認証は度々使われることがあると思います。
そんな他のアクセス制御かかかった環境にAPIを追加するようなケースでのお話なので、開発用の環境を想定しています。
結果的にRFCを無視しますので、所謂production(実運用している)環境でやるようなことではありませんよー、と前置きさせていただきます。
nginxのBasic認証の設定については、他に良い記事がたくさんありますので省略しています。
ざっくりまとめ
結論から言うと、ミドルウェア(nginx)の設定を変えるのが難しい(できない?)ので、Bearerトークンを送るHTTP headerを変える。アプリケーションで、そのheaderからトークンを取り出してあげる。です。
実験した環境
webサーバ: nginx
アプリケーション: php-fpm7.3/7.4
フレームワーク: Laravel6/7/8
順番に解説
HTTP header
Basic認証のRFC
https://tools.ietf.org/html/rfc2617#section-2
# Basic
Authorization: Basic xxxxx
BearerトークンのRFC
https://tools.ietf.org/html/rfc6750#section-2.1
# Bearer
Authorization: Bearer xxxxx
ということで、どちらも通常はHTTP headerはAuthorization
を使うことになっています。
このままではどちらかしか使えませんので、片方に場所を譲ってもらいます。
どちらのheaderを変えるか?
-> Bearerトークンのheaderを変えます。headerの取り扱いの制御が可能なアプリケーションで解決しましょう。(※)
例えば以下のように、Basic認証は標準のまま、Bearerトークンはカスタマイズしたheaderで送信するようにしてみます。
RFCを無視することになりますが、非公開の、自前のバックエンドに、自前で用意したクライアントからリクエストさせる分には特に問題にはならないでしょう。
# Bearer
Authorization: Basic xxxxx
Auth-Token: Bearer xxxxx
※ いろいろ調べてみた+知人のインフラエンジニアさんにも聞いてみたのですが、nginxでBasic認証を扱うモジュールではAuthorization header以外で待ち受けるような設定がなさそうでした。情報お持ちの方は是非ご教示ください。
アプリケーション
サンプルとしてPHP Laravel(ver.6/7/8で確認)を使用しますが、HTTP headerの取り扱いに関しては他のプラットフォームでも大きくはかわらないでしょう。
まず、headerを変更せずに扱う場合どうしているのかを確認してみます。
Laravelでは通常リクエストをIlluminate\Http\Request
(あるいはその派生)で扱いますが、このクラスはBearerトークンを取り出すためのメソッドが用意されています。
// リクエストオブジェクトにアクセスできれば何でも良いです
Auth::viaRequest('bearer', function ($request) {
$bearer = $request->bearerToken();
/** ここで何某かのトークン検証処理 */
});
メソッドは以下のように、規定のheaderからトークン部分を取り出してくるだけのシンプルな実装になっています。
public function bearerToken()
{
$header = $this->header('Authorization', '');
if (Str::startsWith($header, 'Bearer ')) {
return Str::substr($header, 7);
}
}
さて、これでやるべきことが見えました。
上記の動作を、変更したheaderが対象になるように自前で用意してあげれば良いだけです。
// リクエストオブジェクトにアクセスできれば何でも良いです
// 例外処理は省略しています
Auth::viaRequest('bearer', function ($request) {
$bearer = null;
$header = $this->header('Auth-Token', '');
if (Str::startsWith($header, 'Bearer ')) {
$bearer = Str::substr($header, 7);
}
/** ここで何某かのトークン検証処理 */
});