メインコンテンツまでスキップ

Software Design 202501

認証技術の最前線

ユーザー認証とは

ユーザー認証は以下の要素で構成される。

  • 当人認証
    • 識別
    • 検証
  • 身元確認

当人認証とは、登録済みのユーザーが本人であることを確認することである。これには、識別と検証の二つのプロセスがある。

識別は、ユーザーを特定することである。例えば、ユーザーが入力したメールアドレス等に基づいて、ユーザーを特定する。

検証は、本当にその人なのかを確認する作業のこと。例えば、パスワードを入力してもらい、それが登録済みの値を等しいかを確認する。

身元確認は、ユーザーの属性情報が正しいかの確認である。例えば、住所や電話番号などの情報が正しいかどうかを公的個人認証で確認するなど。

多要素認証の課題

  • 知識情報 / Something you know
  • 所持情報 / Something you have
  • 生体情報 / Something you are

多要素認証と言ったときには、パスワードともう一つ別の要素を認証に使う方法を指すことが多い。 パスワードを使うと必然的にフィッシングに対する耐性がなくなる。 パスワードマネージャーを使えばフィッシングへの耐性を得られるが、普及は中途半端である。

例えば、中継型フィッシング攻撃/Man-in-the-Middle という攻撃手法がある。 これは正規サイトと同じ見た目の偽サイトを用意してユーザーに ID/Pass/OTP などを入力させ、その情報をリアルタイムで正規サイトに中継し、ログイン成功後に生成されるセッション情報を盗む攻撃のこと。 ただのフィッシングと異なり、多要素認証すら突破されてしまう攻撃手法である。

パスワードレス

パスワードは知識情報による一要素認証と言える。 なお、所持情報だけを単体で利用する一要素認証もある。

  • マジックリンク
    • メール本文に URL が書いてあるので、フィッシングに強い
    • メールを受信できる環境が複数存在しうるので、所持情報の強度が弱い
  • SMS 認証
    • SIM カードという所持情報を利用するため所持情報の強度が強い
    • フィッシングに弱い

FIDO 認証

FIDO 認証はパスワードレス認証方式の一つ。サービス、デバイス、ブラウザという登場人物がいる。

  • サービス
    • FIDO 認証を利用する Web サービスのこと
    • 正式名称は Relying Party (RP)
  • デバイス
    • 認証に必要な情報を格納した、認証に利用する、ユーザーが所持するデバイス。
    • 正式名称は Authenticator
  • ブラウザ
    • サービスから FIDO 認証を要求され、デバイスとやり取りをする仲介役。
    • 正式名称は Client

FIDO 認証は公開鍵暗号方式のデジタル署名の仕組みをユーザー認証に利用している。

  • 初回
    • デバイスに秘密鍵(FIDO クレデンシャルという)を、サービスに公開鍵を登録する
    • 処理の流れはログイン時とほぼ同じ
    • 鍵はサービスごとに生成される
  • ログイン時
    • サービスが、チャレンジ(ランダムに生成された値)やドメインの情報をブラウザに送る
    • ブラウザはオリジンを検証したのちデバイスに認証を要求する
    • デバイスでローカル認証を行う
    • デバイスは秘密鍵を用いてチャレンジを含むデジタル署名を生成して返答する
    • サービスは公開鍵を用いてデジタル署名を検証する

秘密鍵に加え、知識情報 or 生体情報を使うため、二要素認証である。

パスワードに比べたときのメリットは以下の通り。

  • 覚えなくていい
  • フィッシングに強い
  • 秘密鍵はネットワークを流れない
  • サーバー側の公開鍵は流出しても無害
  • 秘密鍵が流出しても影響はそのサービスに限られる

パスキー認証の登場

FIDO 認証ではデバイスを無くしたときのダメージが大きすぎる。 この問題を解決するのがパスキー認証である。

FIDO 認証では秘密鍵をデバイス内に保存するが、パスキー認証では秘密鍵をパスワードマネージャーに保存する。

細かい話

  • アカウントリカバリーを考えると、複数のパスワードマネージャーに登録してもらうのが現状では良さそう
  • パスキーはデバイスだけでなくセキュリティキーにも保存できる
  • パスキー作成時にしかるべきオプション指定をすると、ログイン画面でいきなりアカウント選択 UI(Autofill UI)を出すことが可能
  • 複数ドメインで共通のパスキーを利用させたいときは、.well-known/webauthnに JSON を配置することで可能になる

ドメイン解体新書 / SSL 証明書発行時の認証

SSL 証明書の有効期間が短くなる予定。一説では 45 日という話も。 そうなると認証と発行を自動化することが必須だろう。 ACME(Automatic Certificate Management Environment) というらしい。

ACME を使うには DNS-01 認証か、HTTP-01 認証が必要。ワイルドカードを使うなら前者が必須。

HTTP-01 認証とは認証局(CA)が指定するトークンを Web サーバ上の特定の URL(http://<ドメイン>/.well-known/pki-validation/など)に設置することで、ドメインを管理していることを証明する。

DNS-01 認証では、指定されたトークンを DNS の TXT レコードとして設定することでドメイン所有権を証明する。

Web API テスト実践ガイド

Web API テスト

E2E テストの性質は、ユーザーの体験に近い、安心感がある、遅い、高コスト、短期的、不安定など。 ユニットテストの性質は、高速、安定、低コスト、長期的、ユーザーの体験から遠い、など。

Web API テストは E2E とユニットテストの中間的な性質を持つためバランスが良いうえ、 UI からはテストしづらい部分もテストできるという利点がある。

テストの形式とスコープ

テストの形式とスコープは必ずしも結合されているわけではない。

  • テスト形式:UI テスト、Web API テスト、VRT、などの別
  • テストスコープ:E2E スコープ、サービス単体(統合?)スコープ、ユニットスコープ、などの別

例えばテスト形式が Web API テストの場合、以下のようにスコープを選択できる。

サービス単体(API サーバーのみ)のスコープでやろうと思えば、Vitest とフレームワーク備え付けのテスト機能を活用して、一部の機能や関数単位でテストを行うことができる。 このパターンだと DB さえ Docker などで用意してしまえばデータの差し込みや改変をテスト内で自由に行えるため、テストがしやすい。 一方で、API 以外の部分を含んだ横断的網羅的なテストにはならない。

E2E スコープでやろうと思えば、全ての関連するインフラを立ち上げて、API リクエストによりテストを行うことになる。 網羅的でユーザー体験に近い一方、コストが高い。外形監視で代替することもある。

機能面のチェック項目

  • コントラクト
    • 入出力データの形式が、例えば OpenAPI の定義に従っているかなど
  • 一貫性・冪等性
    • 「冪等性を期待しない操作」が重複して行われた時に意図しない問題が起きないか
    • 「冪等性を期待する操作」を何度行っても問題がないか
  • API バージョン間の互換性
    • 古いエンドポイントに問題が起きないか
  • エラーハンドリング
    • 必要に応じて適切なエラーメッセージが返されるか

セキュリティ面のチェック項目

  • 認証
    • 許可なしやトークン切れでアクセスできないか
  • 認可
    • 機能レベルの認可不備、オブジェクトレベルの認可不備、オブジェクトプロパティレベルの認可不備がないか(参考)
  • 消費リソースの制限
    • リクエストごとの消費リソースが制限されているか(タイムアウト、ペイロードやファイルサイズ、命令の数、返却するレコード数、など)
    • リクエストの頻度や回数に制限がかかっているか
    • 対応例:API Gateway でレート制限する
  • データの漏洩や過剰露出
    • (エラー)レスポンスに機密情報が載っていないか
    • 対応例:実際にリクエストして検証したり、サーバー側のログを確認したりする
  • 入力データのバリデーション
    • SQL インジェクションや XSS などの攻撃を受けないか
    • 対応例:ライブラリによるサニタイズ、WAF
  • HTTP ヘッダ
    • CORS, cache, Sniffing 対策, HSTS, Frame Options あたりをちゃんと設定しているか(本文参照)

パフォーマンスのチェック項目

  • 高負荷時に性能が保てるか
  • 長期に渡って安定しているか
  • 必要に応じてスケールするか
  • 無駄なリソース消費がないか
  • API の限界値を超えた時にどうなるか

なるべく本番に近い環境に対して Postman などのツールを用いて負荷をかけ、レイテンシー、スループット、エラーレート、リソース使用状況などを計測する。

テストの書き方

機能テストの内容はドメインにより異なるが、HTTP ステータスごとにテストを書くところから出発するのは一つの手である。 200,201,400,401,403,404 など。

パフォーマンステストではクライアントは別マシンとし、十分な性能を担保すること。 そうしないと見かけ上テストが成功しているように見えたりするので。

ソフトウェア探検隊 / 単体テスト

テストケース爆発を抑える手法がいくつかある。

0 スイッチカバレッジは状態遷移テストの一種。 ある状態から別の状態への遷移を網羅的にテストする手法である。 スイッチとは条件分岐のことで、これをテストから取り除くことでテストケースを減らすことができる。

e.g. コード内に A->B、B->X、B->Y という遷移がある場合、A->B, B->X, B->Y の 3 ケースを個別にテストする。

Property-based testing では、テスト対象が常に満たすべき性質やルールを定めたうえで、 その特性を満たしているか確認するために、ランダムに多様な入力を生成してテストする手法である。

実践データベースリファクタリング / 厄介な時間枠

時間枠の扱いは複雑になりやすい。

  • N+1 の発生
  • インデックスが効かない
  • 条件分岐が複雑になる
  • 終了日がないなら null を入れるみたいな設計だと、都度 null への対処が必要になる

ベストプラクティスは存在しないが、まずは正規化をしっかりやっておくことが大事。

なお、PostgreSQL の範囲型は超強力。 タイムスタンプを扱うtsrange型がある。 それだけで PostgreSQL を使う理由になりうる。 ただしロックインとのトレードオフにはなる。