원클릭으로
spec-security
セキュリティ・脆弱性対策の開発・テストを行う際に使用。OAuth/OIDC攻撃対策、認証識別子切り替え攻撃、Session Fixation、マルチテナント分離、セキュリティテスト実装時に役立つ。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
セキュリティ・脆弱性対策の開発・テストを行う際に使用。OAuth/OIDC攻撃対策、認証識別子切り替え攻撃、Session Fixation、マルチテナント分離、セキュリティテスト実装時に役立つ。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
UserInfoエンドポイント(UserInfo Endpoint)機能の開発・修正を行う際に使用。UserInfo claims、scopeフィルタリング、verified_claims実装時に役立つ。
外部API認証ユースケースの設定ガイド。外部API連携(認証委譲、リスク判定、OTP等)の interaction 設計、identity_match_field、MFA 2段階目、previous_interaction のヒアリングと設定JSONを提供。
ユースケース別セットアップのエントリポイント。ユーザーにユースケースを選択してもらい、対応するスキル(use-case-login, use-case-mfa等)にルーティングする。共通ワークフロー、前提条件、組み合わせパターンの概要を提供。
認証機能(Authentication Policy, MFA)の開発・修正を行う際に使用。認証ポリシー、パスワード、OTP、FIDO2、条件付き認証実装時に役立つ。
外部サービス連携(External Service Integration)機能の開発・修正を行う際に使用。HTTP Request Executor, MappingRule, OAuth/HMAC認証実装時に役立つ。
FIDO2/WebAuthn/パスキー関連の実装・設定・ドキュメント・テストを扱う時に使用。パスキー登録、認証、管理、アテステーション検証に関する作業で自動的に呼び出される。
| name | spec-security |
| description | セキュリティ・脆弱性対策の開発・テストを行う際に使用。OAuth/OIDC攻撃対策、認証識別子切り替え攻撃、Session Fixation、マルチテナント分離、セキュリティテスト実装時に役立つ。 |
e2e/src/tests/security/README.md - セキュリティテスト詳細documentation/docs/content_11_learning/06-security/ - セキュリティ学習リソース| 脆弱性 | CWE | 重大度 | 対策 |
|---|---|---|---|
| 認証識別子切り替え攻撃 | CWE-287 | Critical | 1st factor: DB検索優先化 / 2nd factor: 認証済みユーザーへバインド |
| Redirect URI切り替え攻撃 | CWE-601 | Critical | 完全一致検証 |
| Session Fixation | CWE-384 | High | 認証後Session再生成 |
| 認可コード再利用 | CWE-294 | High | 使用後即時削除 |
| マルチテナント分離違反 | CWE-284 | Critical | Tenant第一引数パターン |
| SSRF | CWE-918 | High | プライベートIP/メタデータブロック |
| パスワードブルートフォース | CWE-307 | High | Redisカウンター(INCR+TTL) |
| Null Byte Injection | CWE-626 | High | MaliciousRequestRejectFilter(Servlet Filter) |
| Oversized Request Body | CWE-400 | Medium | MaliciousRequestRejectFilter(10MB制限)+ nginx + AWS API Gateway |
1. 被害者のメールアドレスAで認証開始
2. チャレンジ送信後、攻撃者メールアドレスBに変更
3. Bの検証コードで認証
4. 【脆弱】: メールアドレスAとしてログイン ❌
5. 【正常】: メールアドレスBとしてログイン ✅
EmailAuthenticationInteractor.java:257-306:
private User resolveUser(
Tenant tenant,
AuthenticationTransaction transaction,
String email,
String providerId,
UserQueryRepository userQueryRepository) {
// === 1st factor user identification ===
// 1. Database search FIRST (Issue #800 fix)
User existingUser = userQueryRepository.findByEmail(tenant, email, providerId);
if (existingUser.exists()) {
log.debug("User found in database. email={}, sub={}", email, existingUser.sub());
return existingUser;
}
// 2. Reuse transaction user if same identity (Challenge resend scenario)
if (transaction.hasUser()) {
User transactionUser = transaction.user();
if (email.equals(transactionUser.email())) {
return transactionUser; // Same identity -> reuse
}
// Different identity -> create new user (identifier switching)
}
// 3. New user creation...
}
e2e/src/tests/security/identifier_switching_attack.test.js
1st factor の DB検索優先化に加え、password を 2nd factor(requires_user: true)に使う場合は、検証対象を入力 username ではなくセッションの認証済みユーザーに固定する。これがないと、1段目を被害者として通過後、2段目に別アカウントの正しい credential を提示して被害者としてログインできてしまう(バインド欠落 = identifier switching の別経路)。
PasswordAuthenticationInteractor(2nd factor):
requires_user && hasUser: execution request の username / provider_id を transaction.user() の値に上書きしてから executor を呼ぶ → パスワードは本人の hash に対して照合されるrequires_user && !hasUser: executor 実行前に拒否(user_not_found)EmailAuthenticationChallengeInteractor#resolveEmail(2nd factor で request 入力を無視)と同じ作法。
セキュリティテスト:
e2e/src/tests/usecase/mfa/mfa-22-password-second-factor-user-binding.test.js
| 攻撃パターン | 説明 | 対策 |
|---|---|---|
| Token Endpoint不一致 | 認可時と異なるredirect_uriでトークン取得 | 完全一致検証 |
| 未登録URI | 登録されていないredirect_uriを使用 | 登録URI必須 |
| 部分一致攻撃 | example.com/callback.evil.com | 厳密一致(substring禁止) |
| URIエンコーディング | %2e%2e/ でパストラバーサル | 正規化後検証 |
// redirect_uri検証(RFC 6749 Section 4.1.3)
// "values MUST be identical"
// NG: 部分一致
if (registeredUri.startsWith(requestUri)) // ❌
// OK: 完全一致
if (registeredUri.equals(requestUri)) // ✅
e2e/src/tests/security/redirect_uri_switching_attack.test.js
テストケース(21件):
1. 攻撃者がSession IDを取得
2. 被害者に固定Session IDでアクセスさせる
3. 被害者が認証完了
4. 【脆弱】: 攻撃者が同じSession IDで被害者としてアクセス ❌
5. 【正常】: 認証後にSession ID再生成 ✅
認証成功後にセッションを再生成し、旧セッションIDを無効化。
e2e/src/tests/security/session_fixation_password_auth.test.js
"The authorization server MUST ensure that authorization codes cannot be used more than once."
AuthorizationCodeGrantService.java:131-134, 202:
// 存在確認
if (!authorizationCodeGrant.exists()) {
throw new TokenBadRequestException("invalid_grant", "not found authorization code.");
}
// ... トークン発行処理 ...
// 使用後は即時削除(再利用防止)
authorizationCodeGrantRepository.delete(tenant, authorizationCodeGrant);
// ✅ 全Repository操作でTenant第一引数
public interface UserRepository {
User find(Tenant tenant, UserId userId);
void register(Tenant tenant, User user);
}
// ❌ Tenant指定なし(テナント間データ漏洩リスク)
public interface UserRepository {
User find(UserId userId); // 危険
}
-- RLSポリシーでテナント分離を強制
CREATE POLICY tenant_isolation ON users
USING (tenant_id = current_setting('app.current_tenant')::uuid);
e2e/src/tests/security/multi_tenant_isolation.test.js
PrivateIpRange.java:
| 種別 | IPレンジ | 説明 |
|---|---|---|
| IPv4 Loopback | 127.0.0.0/8 | ループバック |
| RFC1918 Private | 10.0.0.0/8 | プライベート(Class A) |
| RFC1918 Private | 172.16.0.0/12 | プライベート(Class B) |
| RFC1918 Private | 192.168.0.0/16 | プライベート(Class C) |
| Cloud Metadata | 169.254.169.254/32 | AWS/GCP/Azure メタデータ |
| Link-Local IPv4 | 169.254.0.0/16 | リンクローカル |
| CGNAT | 100.64.0.0/10 | Carrier-Grade NAT (RFC6598) |
| Documentation | 192.0.2.0/24 | TEST-NET-1 |
| Documentation | 198.51.100.0/24 | TEST-NET-2 |
| Documentation | 203.0.113.0/24 | TEST-NET-3 |
| Broadcast | 255.255.255.255/32 | ブロードキャスト |
| Current Network | 0.0.0.0/8 | 現在のネットワーク |
| IPv6 Loopback | ::1/128 | IPv6ループバック |
| IPv6 Link-Local | fe80::/10 | IPv6リンクローカル |
| IPv6 ULA | fc00::/7 | IPv6ユニークローカル |
| IPv4-mapped IPv6 | ::ffff:0:0/96 | IPv4マップドIPv6 |
SsrfProtectionValidator.java:261-274:
private void validateIpAddress(String host, InetAddress address) {
String ipString = address.getHostAddress();
for (PrivateIpRange range : blockedRanges) {
if (range.contains(address)) {
throw new SsrfProtectionException(
String.format(
"Blocked: Host '%s' resolves to private/reserved IP %s (%s)",
host, ipString, range.description()),
host,
ipString,
range);
}
}
}
詳細は /ops-system-config スキル参照。
1. 攻撃者がCIBA/OAuth/管理APIのパラメータにUTF-8 0x00(null文字)を含めて送信
2. 【脆弱】: PostgreSQLが "invalid byte sequence for encoding UTF8: 0x00" で500エラー ❌
3. 【正常】: Servlet Filterで事前検出し400 Bad Requestを返却 ✅
MaliciousRequestRejectFilter.java — Servlet Filterで全APIを一括防御:
getParameterNames() / getParameterValues() でチェック@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 5)
public class MaliciousRequestRejectFilter extends OncePerRequestFilter {
private static final int MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
// query params, form body, JSON body全てをチェック
// Null byte検出時: {"error":"invalid_request","error_description":"Invalid parameter value"}
// サイズ超過時: {"error":"invalid_request","error_description":"Request body too large"}
}
| レイヤー | 制限 | レスポンス形式 |
|---|---|---|
| AWS API Gateway(本番) | 10MB | API Gatewayエラー |
| nginx(ローカル) | client_max_body_size 20m | HTML 413 |
| MaliciousRequestRejectFilter | 10MB(MAX_BODY_SIZE) | JSON 413 |
e2e/src/tests/spec/malicious_request_reject_filter.test.js
テストケース(5件):
e2e/src/tests/security/invalid_user_status_authorization.test.js
cd e2e
npm test -- security/
# 認証識別子切り替え
npm test -- security/identifier_switching_attack.test.js
# Redirect URI攻撃
npm test -- security/redirect_uri_switching_attack.test.js
# Session Fixation
npm test -- security/session_fixation_password_auth.test.js
# マルチテナント分離
npm test -- security/multi_tenant_isolation.test.js
# MaliciousRequestRejectFilter (Null Byte Injection + Payload Size)
npm test -- --testPathPattern="malicious_request_reject_filter"
describe("Security: [攻撃名]", () => {
it("Should prevent [攻撃シナリオ]", async () => {
// 1. 攻撃準備
const victim = createVictimUser();
const attacker = createAttackerUser();
// 2. 攻撃実行
const result = await executeAttack(victim, attacker);
// 3. 脆弱性検証
if (result.authenticatedAs === victim) {
fail("CRITICAL: Attack succeeded - vulnerability exists");
}
// 4. 正常動作確認
expect(result.authenticatedAs).toBe(attacker);
});
});
| OWASP | 対策 |
|---|---|
| A01 Broken Access Control | Tenant分離、認可検証 |
| A02 Cryptographic Failures | JWT署名検証、TLS必須 |
| A03 Injection | パラメータバインディング、Null Byte Rejection |
| A04 Insecure Design | 認証フロー設計レビュー |
| A05 Security Misconfiguration | SSRF保護、デフォルト拒否 |
| A07 Identification Failures | 識別子切り替え対策 |
| スキル | 用途 |
|---|---|
/ops-system-config | SSRF保護、Trusted Proxies |
/spec-security-event | セキュリティイベント通知 |
/spec-session | セッション管理 |
/spec-authentication | 認証実装 |
/test-e2e | テスト実行方法 |