0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Hike軽速化:パスキー編】新言語「Hike」でPasskey/WebAuthnをフルWasm・ステートレス化。サイズ1/5・セッションI/Oゼロ(0ms)・秒間83万回検証を達成した話

0
Last updated at Posted at 2026-10-22

皆様こんにちは。初見の方は、初めまして。普段はマネジメントや別事業にしつつ、インフラからアプリまで何でもこなす現場のIT何でも屋です。

新言語「Hike」の圧倒的なフットプリント削減とネイティブ実行性能を活かして、Webフロントエンドの重量級処理を「軽量化」かつ「高速化」する【Hike軽速化(けいそくか)】シリーズ。

前回のバーコード編(全19規格の統合と高速画像補正)に続くシリーズ第4弾となる本記事のテーマは、次世代認証の標準規格である 「パスキー(Passkey / WebAuthn / FIDO2)」 です。


パスキー(WebAuthn)導入に立ちはだかる現場の「3大障壁」

パスワードレスで強固なセキュリティを実現できるパスキーですが、実際のプロダクション環境やエッジ環境(Cloudflare Workers / Vercel Edge)へ導入しようとすると、以下の3つの壁にぶつかります。

  1. クライアント側のバンドル肥大化とパース処理のオーバーヘッド
  • 有名ライブラリを組み込むと、依存関係を含めて15〜25 KBほどバンドルサイズが増加し、バイナリの即時解析やBase64URL変換の処理オーバーヘッドでメインスレッドをじわじわと削る。
  1. サーバー側の重厚なCBORデコード負荷
  • 汎用CBORライブラリが裏で生成する動的オブジェクトの山が、JavaScriptのガベージコレクション(GC)を暴走させ、レイテンシのスパイクを引き起こす。
  1. セッションI/Oへの依存(ステートフル・チャレンジの宿命)
  • 認証ごとにサーバーが発行した challenge をRedisやDBへいちいち一時保存するため、1回の認証で必ず1〜2回のネットワークI/O(5〜30ms)が発生。エッジ環境の利点を完全に殺すだけでなく、複数タブでのセッション衝突(認証エラー)という問題を生む。

「完全ステートレス(0ms I/O)かつ極小フットプリントで動くDrop-in互換のPasskeyスイートを作ればすべて解決するのでは?」と考え開発したのが、「HIKE Universal Passkey Suite」です。


成果ハイライトと定量的性能比較

  • WASMサイズ:
     約15〜25 KBからわずか4.2 KBへ極小化(約1/4〜1/6)。
  • Base64URL変換速度
     Node.js標準Buffer / JS比で約2.9〜5.0倍高速。
  • ゼロI/Oステートレス認証:
     HMAC-SHA256自己検証型チャレンジにより、Redis / DBセッションアクセスが完全に0ms。
  • サーバー側スループット (ゼロI/Oステートレス検証・単体演算):
    • Node.js / Edge: 秒間 82,000 req
    • Go: 秒間 650,000 〜 830,000 req(単体演算 / 1.8 µs/op)
    • PHP環境: ゼロI/Oステートレス検証の単体演算で秒間 545,000 req超(処理時間: 1.83 µs/op)

(※いずれもセッションI/OゼロによるCPU演算ベースの圧倒的な超高速化を達成)
※これらはサーバー側での純粋なステートレス署名検証(HMAC-SHA256)単体の演算性能であり、DB等のI/Oを挟まないためこの速度が出ます


アーキテクチャの核心:0ms I/Oを実現する「自己完結型ステートレス・チャレンジ」

先ほど挙げたセッションI/Oへの依存(3大障壁の第3の壁)を完全に粉砕するのが、このステートレス・チャレンジ方式です。
HIKE Passkeyでは、チャレンジ自体にユーザー識別子・有効期限タイムスタンプ(TTL)・HMAC-SHA256署名をコンパクトに埋め込む設計を採用しています。

┌─────────────────────────────────────────────────────────────────┐
│                      Stateless Challenge                        │
├───────────┬──────────────┬──────────────┬───────────────────────┤
│ Version   │ Timestamp    │ User ID Hash │ HMAC-SHA256 Signature │
│ (1 byte)  │ (8 bytes)    │ (8-16 bytes) │ (32 bytes)            │
└───────────┴──────────────┴──────────────┴───────────────────────┘

サーバーはリクエスト内のチャレンジから署名を再計算して検証するだけで済むため、DBやRedisへ問い合わせることなく正当性・有効期限・ユーザー一致を0msで判定できます。もちろん、改ざんや別ユーザーによるリプレイ攻撃は暗号学的に100%遮断されます。さらに、乱数(Nonce)を含めることで、同一ユーザーが複数タブを開いた際の認証衝突も起きません。

💡 セッションI/Oゼロが生み出す「実運用上の2大メリット」

このステートレス検証アーキテクチャは、単にベンチマークが速いだけでなく、実際のプロダクション運用において強力な武器になります。

  1. DB / クラウドI/Oの劇的削減
    認証のたびに発生していたRedisやデータベースへの書き込み・読み込み(セッションI/O)が完全になくなるため、クラウドインフラのI/OコストやDBの負荷を極限まで抑えられます。
  2. ログイン特化のL7 DDoS攻撃に対する強靭な耐性
    CGI環境やサーバーレス、エッジワーカーを問わず、悪意ある大量のフェイクリクエスト(不正なチャレンジ)が送られてきた場合でも、バックエンドのデータベースやセッションストアに問い合わせる前に、軽量なCPU演算(1.8 µs)だけで瞬時に弾き捨てることができます。これにより、DBのコネクション枯渇やサーバーダウンを構造的にシャットアウトします。

あらゆる環境を網羅するマルチモジュール構成と完全互換

本スイートはGo言語専用ではありません。フロントエンドからサーバーレス、Node.jsバックエンドまで、Web開発で使われる主要なすべての環境に対応するDrop-in互換モジュールを提供しています。

  1. ブラウザ・フロントエンド向け(@simplewebauthn/browser 完全互換)

既存の関数をそのまま利用可能で、わずか4.2 KBの極小WASMで動作。React, Next.js, Vue, iOS/Android等の各種WebViewでも環境を選びません。

  1. Node.js サーバー向け(@simplewebauthn/server 完全互換)

Node.js (v18〜)、Bun、Denoに対応。既存の検証関数と完全互換を保ちつつ、引数一つで「従来のDB方式」と「ゼロI/Oのステートレス方式」を切り替え可能です。

  1. エッジ・サーバーレス向け(Pure WebCrypto準拠)

Cloudflare WorkersやVercel Edge、AWS Lambda等向けに、Node.js固有APIに依存しないWeb標準APIのみで構築されたエッジモジュールを完備。

  1. 他言語バックエンド(Python, Rust, PHP, Java 等)との連携

フロント側のHIKEクライアントはW3C標準規格のJSONを入出力するため、「フロントはHIKE(4.2KB)、バックエンドはPythonやRust」といった異種言語間での混在導入もシームレスに行えます。


【開発の裏話】「普通に入れ替えたら動かない!?」Go特有の型問題とDrop-in互換への工夫

この記事では各言語での「100% Drop-in互換」と謳っていますが、実はGo言語バックエンドの開発途中で大きな壁にぶつかりました。

JSやNode.js環境ではスムーズに置き換えが進んだものの、Goの実装をHIKEモジュールに「普通にインポートして入れ替えた」ところ、最初は全く動かず、認証エラーやパニックが連発したのです。

原因は「Goライブラリの型」と「W3C標準規格の型」のギャップ

ログを追っていくと、原因はWebAuthn標準仕様と、Go特有の型の違い(Type Mismatch)にありました。W3C標準(JSON)では、各種バイナリデータは「Base64URLエンコードされた文字列」として扱うのが基本です。しかし、既存のGoライブラリではそれらを厳密なバイトスライス([]byte)として定義したり、独自のインターフェース型を要求したりと、強い型付けで表現されていました。W3C標準に厳格に準拠して作ったHIKEエンジンに、Go独自の構造体がそのまま渡ってきたため、型の不一致で弾かれていたのです。

ここで利用者に「HIKEを使う場合は、Goの構造体を書き換えてください」とお願いしては、Drop-in互換の理念が崩れます。

そこで、HIKEのGoモジュール内部に 「型の自動変換を行う透過的アダプター層」 を実装しました。利用者が既存の構造体をそのまま投げ込んでも、内部アダプターが差異を吸収し、W3C標準の型へゼロオーバーヘッドで変換してから処理エンジンに渡す仕組みです。この泥臭い型合わせを経たことで、利用者は既存のGoコードを1行も変えることなく 、インポート先をHIKEに変えるだけで秒間83万回検証の恩恵を受けられるようになりました。


あらゆる要件に応える「ハイブリッド運用」:ステートレスとDB方式の自由な切り替え

W3Cの規格上、チャレンジをサーバー側でどう管理するかは実装側の裁量です。HIKE Passkey Suiteでは、要件に応じて関数に渡す引数を変えるだけで、柔軟なハイブリッド構成を実現できます。

  1. ステートレス方式:通常ログイン(高速・スケール重視)
    エッジ(Cloudflare Workers等)やサーバーレスでの認証にはステートレス方式を利用し、DB接続遅延を排除して0msで検証を完結させます。

  2. DB/Redis方式:決済・機密操作(厳密な使い捨て重視)
    同期型パスキー(SignCountが0固定)などに対し、決済直前などの「極めて厳密な1回限りの使い捨て」が求められる場合は、従来のRedis方式を利用して認証直後にチャレンジを即時破棄(DEL)します。

  3. ハイブリッド方式:L7 DDoS・不正アクセス対策(セキュリティ重視)
    1番のステートレス検証をすべてのリクエストの第1段階として必ず通すことで、悪意ある大量のフェイクリクエストをバックエンド到達前に瞬時に弾き捨てます。これにより、2番のDB/Redisを守る強靭なL7 DDoS防壁として機能します。


制限環境でも確実に動作する「純JS / WebCrypto 4重フェールセーフ」

厳格なセキュリティポリシー(CSP)等でWASMがブロックされた場合でもシステムを止めないよう、多重フォールバック機構を内装しています。

  • ブラウザ側: WASMの初期化に失敗した場合、純JavaScript(btoa / atob)による変換へ自動フォールバックします。
  • エッジ・サーバー側: WASMなしでもPure WebCrypto APIのみで100%動作する軽量モジュールを完備しています。

サーバーサイド・ラッパーシリーズ(各言語でのハイブリッド運用とDrop-in互換)

フロントエンド側のWASM最適化だけでなく、バックエンド側でも既存の構成を壊さずに導入できるよう、主要なサーバー環境に向けた 「Drop-in互換ラッパーシリーズ」 を完備しています。

要件に合わせて、引数一つで「0msステートレス検証」と「従来のDB/Redis方式」を自由に切り替え可能です。

※ 補足: ここで紹介するステートレス運用では、チャレンジの検証(セッション確認)自体を暗号学的署名により自己完結させるため、セッションDBやRedisへのI/Oアクセスが完全に0msになります(※認証成功後の公開鍵・カウンターの取得等には通常のDBアクセスが行われます)。

1. Node.js ラッパー (@simplewebauthn/server 互換)

  • 概要: 既存の @simplewebauthn/server と完全互換。useStatelessChallenge を有効にするだけで、DBアクセスを完全にゼロにできます。
  実装例
const { verifyAuthenticationResponse } = require('./passkey/server/node');

// 【パターンA: ステートレス方式(超高速・ゼロI/O)】
const resultStateless = await verifyAuthenticationResponse({
  response: req.body,
  useStatelessChallenge: true, // ここをtrueにし、秘密鍵を渡すだけ
  expectedUserID: user.id,
  statelessSecretKey: process.env.STATELESS_SECRET,
  expectedOrigin: '[https://example.com](https://example.com)',
  expectedRPID: 'example.com',
  credential: { publicKey: user.publicKeyDER, counter: user.signCount }
});

// 【パターンB: 従来のDB/Redis方式(厳密な即時破棄)】
const resultStateful = await verifyAuthenticationResponse({
  response: req.body,
  expectedChallenge: session.challenge, // Redisから取得した文字列を渡す
  expectedOrigin: '[https://example.com](https://example.com)',
  expectedRPID: 'example.com',
  credential: { publicKey: user.publicKey, counter: user.counter }
});

2. Go ラッパー (github.com/go-webauthn/webauthn 互換)

  • 概要: 従来のDB保存方式に加え、ステートレス専用の関数(Statelessプレフィックス)を搭載し、Go特有の型とのギャップを吸収します。
  実装例
import "hikepasskey"

// 1. チャレンジ生成(Redis / DB への保存不要 0ms)
creationOptions, err := wa.BeginStatelessRegistration(user)

// 2. 登録完了の検証(セッション DB への問い合わせ不要 0ms)
cred, err := wa.FinishStatelessRegistration(user, reqBody)

3. エッジ・TypeScript ラッパー (Cloudflare / Vercel Edge)

  • 概要: WebCrypto APIを利用し、完全型安全(TypeScript)で動作するゼロI/O認証ゲートウェイ用のラッパーです。
  実装例
import { HikePasskeyEdge } from './hike-passkey';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const body = (await request.json()) as any;
    
    // DBへのアクセス完全ゼロでチャレンジをステートレス検証 (0ms)
    const check = await HikePasskeyEdge.verifyChallenge(body.response.clientDataJSON.challenge, {
      userID: body.username,
      secretKey: env.PASSKEY_SECRET
    });

    if (!check.valid) {
      return Response.json({ error: check.error }, { status: 401 });
    }
    return Response.json({ status: 'ok' });
  }
};

4. PHP ラッパー (レンタルサーバー / CGI 環境向け)

  • 概要: リクエストごとにプロセスが起動するPHP環境において、MySQL等のDB接続オーバーヘッドを完全に解消するラッパーです。
  実装例
require __DIR__ . '/Stateless.php';
$config = require __DIR__ . '/config.php';

$clientChallenge =$requestPayload['response']['clientData']['challenge'] ?? '';

// DBへの問い合わせを行わず、0msで自己検証
$check = HikePasskeyStateless::verifyChallenge(
    $clientChallenge,$_SESSION['user_id'] ?? null, 
    $config['stateless']['secret_key']
);

if (!$check['valid']) {
    die("認証エラー: " . $check['error']);
}

おわりに

パスキーは非常に優れている反面、「重いライブラリ」「セッションDBの負荷」「エッジでの制約」が課題になりがちでした。

Hike言語による極小WASMと、要件に合わせて自在に切り替えられるハイブリッドなアーキテクチャを組み合わせることで、一切の無駄を削ぎ落とした超高速・超軽量なパスキー基盤を実現できました。

シリーズ一覧

本シリーズの過去記事や今後の続編一覧は、以下のストックリストで一括管理しています。ぜひご活用ください!
👉 Hikeで軽速化シリーズ(Qiitaストック)

謝辞

本プロジェクト(Hike Passkey Suite)の実現にあたり、多大なるインスピレーションと恩恵をいただいた皆様に深く感謝申し上げます。

  • WebAuthn / Passkey コミュニティおよび各エコシステムの皆様: @simplewebauthn/server や go-webauthn をはじめ、偉大な先達が築き上げてくれた標準仕様と堅牢なライブラリ群のおかげで、ゼロI/O・フルWasm版の設計・検証が可能になりました。心より感謝いたします。

本記事の構成案の壁打ちや技術検証のサポートには生成AI(Gemini)を活用しました。AIの強力なアシストを受けながら、スムーズに検証から執筆までをやり切ることができました。

軽量なWasm環境やエッジな開発、AIを活用した開発に興味がある方は、ぜひ Hike を触ってみてください!


※ 本実装のAPIインターフェースは、既存の @simplewebauthn/browser(Matthew Svalbasen氏)や @simplewebauthn/server 等の仕様を参考に、実務におけるシームレスなDrop-in互換を担保しています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?