FAPI 1.0 Advanced Profile
FAPI 1.0 Advanced Profile は、決済や高額取引など高リスクな金融 API 向けのセキュリティプロファイルです。
目次
第1部: 概要編
Advanced Profile とは?
Advanced Profile は、Baseline Profile の要件に加えて、より強力なセキュリティ対策を要求するプロファイルです。
FAPI 1.0 Advanced の追加要件:
Baseline:
✓ PKCE
✓ state
✓ nonce
✓ 機密クライアント推奨
Advanced(追加):
+ private_key_jwt または mTLS 必須
+ Request Object 必須
+ s_hash / c_hash 検証
+ JARM または Hybrid Flow + ID Token 検証
+ Sender-Constrained Access Tokens
なぜ Advanced Profile が必要なのか?
背景と歴史
2016年頃、オープンバンキング規制が本格化する中で、読み取り専用API(Baseline)だけでなく、**書き込みAPI(決済、送金等)**にも対応する必要が出てきました。しかし、Baselineレベルのセキュリティでは以下のリスクに対処できませんでした:
| リスク | Baseline の限界 | Advanced の解決策 |
|---|---|---|
| 認可リクエストの改ざん | URL パラメータは改ざん可能 | Request Object(署名付きJWT)で保護 |
| 認可レスポンスの改ざん | code と state は保護されない | JARM で JWT として保護 |
| トークン盗難 | Bearer トークンは誰でも使用可能 | mTLS でトークンをクライアントにバインド |
| Mix-Up 攻撃 | 複数ASの環境で混乱の可能性 | iss パラメータと ID Token で検証 |
FAPI 1.0 Advanced は 2019年に Final 仕様として承認され、現在、世界中の決済APIで採用されています。
脅威モデル
FAPI Advanced が対処する主な脅威:
-
認可リクエスト改ざん攻撃
攻撃シナリオ:
1. クライアントが認可リクエストを送信
/authorize?client_id=client1&scope=payment&amount=100
2. 攻撃者が中間でパラメータを改ざん
/authorize?client_id=client1&scope=payment&amount=10000
3. ユーザーが改ざんされた内容で認可
4. 攻撃者が10000の送金を実行
対策: Request Object(署名付きJWT)
- すべてのパラメータをJWTに含めて署名
- ASがJWTの署名を検証
- 改ざんは検出される -
認可レスポンス改ざん攻撃
攻撃シナリオ:
1. ASが認可コードを発行
/callback?code=CODE1&state=STATE1
2. 攻撃者がcodeを差し替え
/callback?code=CODE2&state=STATE1
3. クライアントが攻撃者のcodeでトークン取得
4. 攻撃者のアカウントにアクセス
対策: JARM または Hybrid Flow
- 認可レスポンスをJWTで保護
- クライアントがJWTの署名を検証
- 改ざんは検出される -
トークン盗難攻撃
攻撃シナリオ:
1. 攻撃者がアクセストークンを盗難
(ネットワーク盗聴、ログ漏洩等)
2. 攻撃者が盗んだトークンでAPIにアクセス
3. 成功(Bearer トークンは誰でも使用可能)
対策: mTLS Certificate-Bound Tokens
- アクセストークンをクライアント証明書にバインド
- APIアクセス時に同じ証明書が必要
- 盗んだトークンだけでは使用不可 -
Mix-Up 攻撃
攻撃シナリオ:
1. クライアントが複数のASに対応
2. 攻撃者が悪意あるASを用意
3. ユーザーがAS1を選択
4. 攻撃者がAS2のレスポンスに差し替え
5. クライアントがAS2のcodeをAS1に送信
6. 攻撃成功
対策: iss パラメータ + ID Token検証
- レスポンスにissパラメータを含める
- クライアントがASを検証
- Mix-Upは検出される
用途
| 用途 | プロファイル | 理由 |
|---|---|---|
| 残高照会 | Baseline | 読み取り専用、情報漏洩のリスクのみ |
| 取引履歴 | Baseline | 読み取り専用、情報漏洩のリスクのみ |
| 送金・決済 | Advanced | 書き込み、金銭的損失のリスク |
| 口座開設 | Advanced | 個人情報の変更、なりすましリスク |
| 個人情報変更 | Advanced | 重要情報の変更、なりすましリスク |
| 定期支払い設定 | Advanced | 継続的な金銭的影響 |
実際のユースケース
ユースケース1: オープンバンキング決済(Advanced 適用)
シナリオ:
- ユーザーがECサイトで商品を購入
- ECサイトが銀行APIで決済を実行
- 金額: 50,000円
- リスクレベル: 高(金銭的損失のリスク)
Advanced が必要な理由:
1. Request Object
- 金額、送金先を署名付きJWTで保護
- 攻撃者が金額を改ざんできない
2. JARM
- 認可レスポンスを改ざんから保護
- 攻撃者がcodeを差し替えできない
3. mTLS
- アクセストークンをクライアントにバインド
- トークンを盗まれても使用不可
4. private_key_jwt
- クライアント認証を強化
- なりすましを防止
ユースケース2: 投資取引(Advanced 適用)
シナリオ:
- ユーザーが投資アプリで株式を購入
- アプリが証券会社APIで取引を実行
- リスクレベル: 極めて高(金銭的損失 + 法的問題)
Advanced のセキュリティ対策:
1. Request Object
- 銘柄、株数、価格を改ざん不可に
- 取引内容の完全性を保証
2. mTLS
- トークンバインディングで盗難対策
- 二次利用を防止
3. 短い有効期限
- アクセストークン: 5分
- 攻撃の時間窓を最小化
4. トランザクション単位の認可
- 1取引ごとに認可を取得
- 不正取引を防止
FAPI Advanced の規制対応
世界各国の規制
| 地域/国 | 規制名 | FAPI 採用 | 主な要件 |
|---|---|---|---|
| 欧州 | PSD2 | ✅ | 強い顧客認証(SCA)、API公開義務 |
| 英国 | Open Banking Standard | ✅ | FAPI Advanced 必須(決済) |
| オーストラリア | Consumer Data Right (CDR) | ✅ | FAPI Advanced 準拠 |
| ブラジル | Open Banking Brasil | ✅ | FAPI Advanced 必須 |
| 日本 | オープンAPI規制 | 推奨 | 金融機関のAPI公開推奨 |
| シンガポール | MAS API Standards | 推奨 | FAPI準拠を推奨 |
PSD2 との関係
PSD2(決済サービス指令第2版)の要件:
1. 強い顧客認証(SCA)
- FAPI Advanced: 署名付きRequest Object
- 認証内容の完全性を保証
2. API公開義務
- FAPI Advanced: 標準化されたセキュリティプロファイル
- 異なる銀行間での互換性
3. 取引の非否認性
- FAPI Advanced: mTLS + private_key_jwt
- クライアントの確実な識別
4. データ保護
- FAPI Advanced: トークンバインディング
- トークン盗難時の被害最小化
第2部: 詳細編
Request Object(必須)
Request Object は、認可リクエストのパラメータを署名付き JWT で送信する仕組みです。
Request Object の構造
| クレーム | 必須 | 説明 |
|---|---|---|
iss | ✅ | 発行者(client_id) |
aud | ✅ | 対象者(AS の URL) |
response_type | ✅ | レスポンスタイプ |
client_id | ✅ | クライアント ID |
redirect_uri | ✅ | リダイレクト URI |
scope | ✅ | スコープ |
state | ✅ | CSRF 対策 |
nonce | ✅ | リプレイ防止 |
exp | ✅ | 有効期限(通常5分) |
iat | ✅ | 発行時刻 |
nbf | 推奨 | 有効開始時刻 |
jti | 推奨 | JWT ID(リプレイ防止) |
claims | △ | 要求するクレーム |
Request Object の生成例
送信方法
方法 1: request パラメータ
GET /authorize?
client_id=s6BhdRkqt3
&request=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9...
注意: Request Object が大きい場合、URL が長くなりすぎる可能性があります。
方法 2: request_uri パラメータ(PAR 推奨)
Pushed Authorization Requests (PAR) を使用します。
POST /par HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
request=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9...
&client_id=s6BhdRkqt3
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGciOiJQUzI1NiIs...
レスポンス:
HTTP/1.1 201 Created
Content-Type: application/json
{
"request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c",
"expires_in": 90
}
認可リクエスト:
GET /authorize?
client_id=s6BhdRkqt3
&request_uri=urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c
PAR のメリット:
- URL の長さ制限を回避
- リクエストの機密性(直接ブラウザに渡らない)
- クライアント認証が可能
- リプレイ攻撃防止(短い有効期限)
Request Object の検証(AS側)
認可レスポンスの保護
Advanced Profile では、認可レスポンスを保護する必要があります。
方法 1: JARM(JWT Secured Authorization Response Mode)
認可レスポンスが JWT として返される:
GET https://client.example.com/callback?
response=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJhdWQiOiJzNkJoZFJrcXQzIiwiZXhwIjoxNzA0MTUzNjAwLCJjb2RlIjoiU3BseGxPQmVaUVFZYllTNld4U2JJQSIsInN0YXRlIjoiYWYwaWZqc2xka2oifQ.
signature
JWT のペイロード:
{
"iss": "https://auth.example.com",
"aud": "s6BhdRkqt3",
"exp": 1704153600,
"code": "SplxlOBeZQQYbYS6WxSbIA",
"state": "af0ifjsldkj"
}
JARM の response_mode:
| モード | 説明 | フラグメント/クエリ |
|---|---|---|
query.jwt | JWT をクエリパラメータで返す | クエリ |
fragment.jwt | JWT をフラグメントで返す | フラグメント |
form_post.jwt | JWT をPOSTで返す | POST |
jwt | デフォルト(response_type による) | 自動 |
JARM レスポンスの検証(クライアント側)
方法 2: Hybrid Flow + ID Token 検証
response_type=code id_token を使用:
GET https://client.example.com/callback#
code=SplxlOBeZQQYbYS6WxSbIA
&id_token=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9...
&state=af0ifjsldkj
ID Token で code を検証:
{
"iss": "https://auth.example.com",
"sub": "user-123",
"aud": "s6BhdRkqt3",
"c_hash": "LDktKdoQak3Pk0cnXxCltA", ← code のハッシュ
"s_hash": "abc123...", ← state のハッシュ
"nonce": "n-0S6_WzA2Mj",
"exp": 1704153600
}
c_hash の計算:
1. code を ASCII オクテットとして取得
2. SHA-256 でハッシ ュ
3. 左半分(128 ビット)を Base64URL エンコード
c_hash と s_hash の検証実装
Sender-Constrained Access Tokens
アクセストークンをクライアントにバインドすることが必須です。
mTLS Certificate-Bound Tokens
フロー:
1. クライアントが mTLS 接続でトークンリクエスト
- クライアント証明書を提示
2. AS がアクセストークンを発行
- トークンに証明書のハッシュを含める
3. クライアントが mTLS 接続で API にアクセス
- 同じクライアント証明書を提示
4. API がトークンと証明書を検証
- トークンの cnf.x5t#S256 と証明書のハッシュを比較
- 一致すればアクセス許可
アクセストークン(JWT):
証明書のハッシュ計算:
リソースサーバーでの検証:
クライアント認証(必須)
Advanced Profile では、以下のクライアント認証方式のみ許可されます。
許可される認証方式:
1. private_key_jwt
- 非対称鍵 JWT で認証
- 秘密鍵はクライアントのみが保 持
2. tls_client_auth
- CA が発行した証明書で認証
- 証明書のサブジェクト DN で識別
3. self_signed_tls_client_auth
- 自己署名証明書で認証
- 事前に公開鍵を登録
禁止される認証方式:
❌ client_secret_basic
❌ client_secret_post
❌ client_secret_jwt
❌ none
private_key_jwt の詳細実装
mTLS クライアント認証の実装
署名アルゴリズム
Advanced Profile で許可されるアルゴリズム:
ID トークン:
✅ PS256, PS384, PS512
✅ ES256, ES384, ES512
❌ RS256, RS384, RS512(禁止)
Request Object:
✅ PS256, PS384, PS512
✅ ES256, ES384, ES512
クライアントアサーション:
✅ PS256, PS384, PS512
✅ ES256, ES384, ES512
JARM:
✅ PS256, PS384, PS512
✅ ES256, ES384, ES512
なぜ RS256 が禁止されるか?
| リスク | 説明 |
|---|---|
| Bleichenbacher 攻撃 | PKCS#1 v1.5 パディングの脆弱性 |
| セキュリティマージンの低下 | PSS パディングの方が安全 |
| 業界標準 | 金融業界では PSS が推奨 |
iss パラメータ(Mix-Up 攻撃対策)
iss パラメータの目的:
- Mix-Up攻撃の防止
- 複数ASに対応するクライアントの保護
認可レスポンス(issパラメータ付き):
GET /callback?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
&iss=https://auth.example.com
クライアントの検証:
1. state を検証(CSRF防止)
2. iss を検証(Mix-Up防止)
- 期待するASのissと一致するか
3. トークンリクエストを送信
実装例: