DAT (Distributed Access Token)
1. 概要
同時接続ユーザー数の増加にともないセッション (Session) の数も増え、セッションサーバーに過大な負荷が発生します。
DAT は、このようなセッションサーバーの負荷問題を解決し、サーバー間で状態を共有しない (Stateless) 効率的な認証を実現するために考案されたトークン仕様です。
DAT はピリオド (.) で区切られた 5 つの固定フィールドから成る文字列です。JSON パースなしに区切り文字の位置だけで各フィールドを切り出すことができ、有効期限と暗号化領域が仕様そのものに含まれています。
2. ワイヤーフォーマット
expire . cid . plain . secure . signature| フィールド | 型 | エンコーディング | 備考 |
|---|---|---|---|
有効期限 | uint64 | 10 進文字列 | Unixtime (秒) |
CID | uint64 | 16 進文字列 | 証明書 ID |
平文データ | Binary | Base64Url (パディングなし) | 公開データ |
暗号化データ | Binary | Base64Url (パディングなし) | 暗号化データ |
署名 | Binary | Base64Url (パディングなし) | 署名 |
2.1. フィールド別詳細仕様
有効期限 : uint64 (Unix Time)
- トークンの有効期限を秒 (Seconds) 単位の 64 ビット符号なし整数で表します。
- 純粋な 10 進数字のみを許可します。符号・空白・区切り文字が含まれる場合は形式エラーです。
CID : Hex (uint64)
- トークン検証に使用する証明書 ID (Certificate ID) です。
- 純粋な 16 進数字のみを許可し、
0xプレフィックスは使用しません。
平文データ : Base64Url (Binary)
- クライアントに公開するデータを格納します。文字列だけでなくバイナリデータもサポートしており、クライアント側でデコードして確認できます。
- 暗号化されません。 機密性のある値を入れてはいけません。
暗号化データ : Base64Url (Binary)
- クライアントに非公開とするデータを格納します。証明書の暗号化アルゴリズムで暗号化されているため、証明書を持たないクライアントは内容を復号できません。
- 内部構造は
IV(96bit) + 暗号文であり、IV は暗号化のたびに新しく生成されます。
署名 : Base64Url (Binary)
- トークンの改ざんを検証するための署名データです。先行するフィールドを証明書の署名アルゴリズムで署名して生成します。
- 署名検証に失敗したトークンは、どのフィールドも信頼してはいけません。
3. 正規ルール (Canonical Rules)
複数の言語で実装されたクライアントが同じトークンを同じように解釈するためには、以下のルールが実装ごとに食い違ってはなりません。リファレンス実装は Rust (dat-rust) であり、その他の実装はすべてこのルールに合わせられています。
3.1. 数値フィールドのパース
expire と cid は厳格に解釈します。以下の入力はすべて形式エラーとして拒否されます。
| 入力例 | 結果 | 理由 |
|---|---|---|
100 | 通過 | 純粋な 10 進 |
007 | 通過 | 先行ゼロは許可 |
+100 | 拒否 | 符号は使用不可 |
-1 | 拒否 | 符号は使用不可 |
" 100 " | 拒否 | 空白は不可 |
1_0 | 拒否 | 区切り文字は不可 |
0x10 | 拒否 | プレフィックスは不可 |
zzzz | 拒否 | 数字ではない |
"" | 拒否 | 空文字列 |
18446744073709551616 | 拒否 | uint64 の範囲を超過 |
なぜ厳格でなければならないのか
寛容なパーサーは -1 を uint64 の最大値に折り返して事実上失効しないトークンを作り出したり、数字でない値を黙って 0 に置き換えたりします。実装ごとに寛容さが異なると、同じトークンが一方では通過し他方では拒否され、相互運用が壊れます。
3.2. 有効期限の判定
DAT トークンと証明書では有効期限の境界が異なります。 混同しないでください。
| 対象 | 有効条件 | 有効期限ちょうど (expire == now) |
|---|---|---|
| DAT トークン | expire > now | 失効として拒否 |
| 証明書 | expire >= now | まだ有効 |
トークンは有効期限に達した瞬間ただちに無効となり、証明書はその時刻まで有効です。境界で発行されたトークンを検証できるようにするため、証明書はトークンより 1 ティック長く生きている必要があるからです。
3.3. 空の secure ペイロード
暗号化するデータがない場合、secure は空文字列です。
encrypt(空の入力)→ 空の出力 (IV も GCM タグも付きません)decrypt(空の入力)→ 空の出力- 空でないのに IV の長さ (12 バイト) 以下である場合は復号エラーです。
1893456000.1a.SGVsbG8..T3RoZXItc2lnbmF0dXJl
↑ secure の位置が空である正常なトークン4. 発行と検証
4.1. 発行手順
- マネージャーが保有する証明書のうち、発行可能な (issuable) 証明書を選びます。
expire = now + dat_ttl_secondsを計算します。plainを Base64Url でエンコードし、secureは暗号化したうえで Base64Url でエンコードします。expire.cid.plain.secureの文字列に署名し、最後のフィールドとして付け加えます。
4.2. 検証手順
- ピリオド (
.) で 5 つのフィールドに分割します。フィールド数が異なる場合は形式エラーです。 expireを確認します。失効したトークンは署名検証より前に拒否されます。cidで証明書を探します。見つからなければ検証できません。expire.cid.plain.secureの区間に対して署名を検証します。- 検証に成功した後で初めて
secureを復号します。
署名検証前の値を信頼しないでください
一部の実装は、署名を確認せずにフィールドを取り出す API (parse without verify 系) を提供しています。この値は完全に攻撃者が操作できる値であり、ログ出力やデバッグ用途にのみ使用してください。
5. JWT との比較
DAT と JWT (JSON Web Token) は、ピリオド (.) で区切られたトークン構造と署名による検証方式を共有していますが、内部設計において次のような重要な違いがあります。
5.1. 構造的な差異の比較
JWT 構造
header body signature Base64Url (JSON String) Base64Url (JSON String) Base64Url (Binary) DAT 構造
有効期限 CID 平文データ 暗号化データ 署名 Unixtime (uint64) Hex (uint64) Base64Url (Binary) Base64Url (Encrypt Binary) Base64Url (Binary)
5.2. 主要な相違点
- Binary ベースの軽量化: JWT は Header と Body を JSON 文字列の形で扱いますが、DAT はバイナリ (Binary) データを直接扱うことでデータサイズを最適化し、パース効率を高めています。
- セキュリティの内在化 (
暗号化データフィールド): JWT は基本的にペイロード (Payload) が平文で露出しており、暗号化が必要な場合は JWE のような別途の仕様を適用する必要があります。一方 DAT は、暗号化データフィールドによってトークン自体が暗号化機能をサポートします。 - 有効期限の制約の強制: JWT では
exp(Claims) フィールドが任意項目ですが、DAT は有効期限フィールドがトークン構造上で強制されているため、有効期限の検証が必ず実行されます。 - アルゴリズム交渉なし: JWT はヘッダーの
alg値をトークン自身が持ち歩くため、アルゴリズム混同攻撃の攻撃面が生じます。DAT はアルゴリズムを証明書が決定し、トークンにはアルゴリズム情報が含まれません。