oauth-implementation
OAuth 2.0 および OAuth 2.1 の認証フローを、PKCEを含むセキュリティベストプラクティスに従って実装する際のガイドラインを提供します。OAuth対応機能の設計・実装時にトリガーされ、安全な認証基盤の構築をサポートします。
description の原文を見る
Guidelines for implementing OAuth 2.0 and OAuth 2.1 authentication flows with security best practices and PKCE
SKILL.md 本文
OAuth 実装
OAuth 2.0 と OAuth 2.1 実装の専門家です。OAuth 認証フローを実装する際は、以下のガイドラインに従ってください。
コア原則
- 常に OAuth 2.1 パターンを使用してください (PKCE は必須、implicit flow は禁止)
- すべての OAuth 通信に HTTPS を使用
- CSRF 保護のための適切な state 管理を実装
- スコープについて最小権限の原則に従う
- すべてのトークンはサーバー側で検証
OAuth 2.1 主要要件
OAuth 2.1 はベストプラクティスをまとめ、セキュアでないパターンを廃止しています:
- PKCE は認可コードフローを使用するすべてのクライアントで必須
- Implicit grant は廃止
- Resource Owner Password Credentials grant は廃止
- Redirect URI は完全一致で検証する必要があります
- Refresh token は送信者制約またはローテーションを使用する必要があります
Authorization Code Flow with PKCE
ステップ 1: PKCE パラメータの生成
// 暗号的に安全なコード検証機を生成
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64URLEncode(array);
}
// 検証機からコードチャレンジを作成
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64URLEncode(new Uint8Array(digest));
}
function base64URLEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
ステップ 2: 認可リクエスト
async function initiateOAuthFlow() {
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
const state = generateSecureRandomString();
// 後で検証するために保存
sessionStorage.setItem('oauth_code_verifier', codeVerifier);
sessionStorage.setItem('oauth_state', state);
const params = new URLSearchParams({
response_type: 'code',
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
scope: 'openid profile email',
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});
window.location.href = `${AUTHORIZATION_ENDPOINT}?${params}`;
}
ステップ 3: コールバック処理とトークン交換
async function handleCallback() {
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
const error = params.get('error');
// エラーをチェック
if (error) {
throw new Error(`OAuth error: ${error} - ${params.get('error_description')}`);
}
// CSRF を防ぐために state を検証
const storedState = sessionStorage.getItem('oauth_state');
if (state !== storedState) {
throw new Error('Invalid state parameter - possible CSRF attack');
}
// コード検証機を取得
const codeVerifier = sessionStorage.getItem('oauth_code_verifier');
// コードをトークンに交換
const response = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
code_verifier: codeVerifier,
}),
});
if (!response.ok) {
throw new Error('Token exchange failed');
}
const tokens = await response.json();
// クリーンアップ
sessionStorage.removeItem('oauth_code_verifier');
sessionStorage.removeItem('oauth_state');
return tokens;
}
サーバー側の実装
Confidential Client トークン交換
// Node.js/Express の例
app.post('/oauth/callback', async (req, res) => {
const { code, state } = req.body;
// state を検証
if (state !== req.session.oauthState) {
return res.status(400).json({ error: 'Invalid state' });
}
try {
const tokenResponse = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
// Confidential clients のクライアント認証
Authorization: `Basic ${Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: REDIRECT_URI,
code_verifier: req.session.codeVerifier,
}),
});
const tokens = await tokenResponse.json();
// トークンをサーバー側で安全に保存
req.session.accessToken = tokens.access_token;
req.session.refreshToken = tokens.refresh_token;
res.redirect('/dashboard');
} catch (error) {
res.status(500).json({ error: 'Token exchange failed' });
}
});
トークンセキュリティベストプラクティス
Access Token の検証
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: `${ISSUER}/.well-known/jwks.json`,
cache: true,
cacheMaxAge: 600000, // 10 分
});
function getSigningKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
const signingKey = key.getPublicKey();
callback(null, signingKey);
});
}
async function validateToken(token) {
return new Promise((resolve, reject) => {
jwt.verify(
token,
getSigningKey,
{
audience: EXPECTED_AUDIENCE,
issuer: EXPECTED_ISSUER,
algorithms: ['RS256'], // 許可されたアルゴリズムをホワイトリスト化
},
(err, decoded) => {
if (err) reject(err);
else resolve(decoded);
}
);
});
}
Refresh Token ローテーション
async function refreshAccessToken(refreshToken) {
const response = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: CLIENT_ID,
}),
});
if (!response.ok) {
// Refresh token が期限切れまたは取り消されている可能性があります
throw new Error('Refresh token invalid');
}
const tokens = await response.json();
// ローテーションが有効な場合、新しい refresh token が返されます
// 新しい refresh token を保存し、古いものを無効化
return tokens;
}
セキュリティ要件
Redirect URI の検証
// サーバー側: redirect URI をホワイトリストに対して検証
const ALLOWED_REDIRECT_URIS = [
'https://myapp.com/callback',
'https://myapp.com/oauth/callback',
];
function validateRedirectUri(uri) {
// 完全一致 - ワイルドカードなし
return ALLOWED_REDIRECT_URIS.includes(uri);
}
スコープ管理
// 必要最小限のスコープをリクエスト
const SCOPES = {
basic: 'openid profile email',
readOnly: 'openid profile email read:data',
fullAccess: 'openid profile email read:data write:data',
};
// サーバー側でスコープを検証
function validateScopes(requestedScopes, allowedScopes) {
const requested = requestedScopes.split(' ');
const allowed = allowedScopes.split(' ');
return requested.every(scope => allowed.includes(scope));
}
防止すべき一般的な脆弱性
1. 認可コードインジェクション
常に PKCE を使用してください - code_verifier は元のリクエスター のみがコードを交換できることを保証します。
2. CSRF 攻撃
// 常に state パラメータを使用して検証
const state = crypto.randomBytes(32).toString('hex');
// セッションに保存してコールバックで検証
3. オープンリダイレクト
// ユーザー入力から redirect URI を構築しないでください
// 常にホワイトリストされた URI を使用
const redirectUri = ALLOWED_REDIRECT_URIS[0]; // しないこと: req.query.redirect_uri
4. トークンリーク
// トークンをログに記録しないでください
console.log('User authenticated'); // 良い
console.log(`Token: ${accessToken}`); // 絶対にしないこと
// トークンを URL に含めないでください
// 代わりに Authorization ヘッダーを使用
fetch('/api/resource', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
トークンストレージの推奨事項
ブラウザアプリケーション
// オプション 1: メモリ (最もセキュアだが、リフレッシュで失われる)
let accessToken = null;
// オプション 2: HttpOnly cookie (バックエンドが必要)
// 適切なフラグで サーバーが設定
// Secure, HttpOnly, SameSite=Strict
// オプション 3: sessionStorage (タブを閉じるとクリア)
sessionStorage.setItem('access_token', token);
// 機密トークンには localStorage を避けてください
// XSS 攻撃に脆弱
サーバーアプリケーション
// トークンを暗号化してセッションまたはデータベースに保存
const encryptedToken = encrypt(accessToken, SESSION_ENCRYPTION_KEY);
req.session.encryptedAccessToken = encryptedToken;
エラーハンドリング
const OAUTH_ERRORS = {
invalid_request: 'リクエストが必須パラメータを失っています',
unauthorized_client: 'クライアントは認可されていません',
access_denied: 'ユーザーがリクエストを拒否しました',
unsupported_response_type: 'レスポンスタイプはサポートされていません',
invalid_scope: 'リクエストされたスコープは無効です',
server_error: '認可サーバーがエラーに遭遇しました',
temporarily_unavailable: 'サーバーは一時的に利用できません',
};
function handleOAuthError(error, errorDescription) {
const message = OAUTH_ERRORS[error] || 'Unknown error';
console.error(`OAuth Error: ${message}. Details: ${errorDescription}`);
// ユーザーフレンドリーなエラーメッセージを表示
}
テストチェックリスト
- PKCE フローが正しく機能する
- State パラメータが検証される
- 無効な state が拒否される
- トークン有効期限が処理される
- Refresh token ローテーションが機能する
- 無効なトークンが拒否される
- スコープが適切に強制される
- Redirect URI が検証される
- エラーケースが適切に処理される
ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- mindrally
- リポジトリ
- mindrally/skills
- ライセンス
- Apache-2.0
- 最終更新
- 不明
Source: https://github.com/mindrally/skills / ライセンス: Apache-2.0
関連スキル
secure-code-guardian
認証・認可の実装、ユーザー入力の保護、OWASP Top 10の脆弱性対策が必要な場合に使用します。bcrypt/argon2によるパスワードハッシング、パラメータ化ステートメントによるSQLインジェクション対策、CORS/CSPヘッダーの設定、Zodによる入力検証、JWTトークンの構築などのカスタムセキュリティ実装に対応します。認証、認可、入力検証、暗号化、OWASP Top 10対策、セッション管理、セキュリティ強化全般で活用できます。ただし、構築済みのOAuth/SSO統合や単独のセキュリティ監査が必要な場合は、より特化したスキルの検討をお勧めします。
claude-authenticity
APIエンドポイントが本物のClaudeによって支えられているか(ラッパーやプロキシ、偽装ではないか)を、claude-verifyプロジェクトを模した9つの重み付きルールベースチェックで検証できます。また、Claudeの正体を上書きしているプロバイダーから注入されたシステムプロンプトも抽出します。完全に自己完結しており、httpx以外の追加パッケージは不要です。Claude APIキーまたはエンドポイントを検証したい場合、サードパーティのClaudeサービスが本物か確認したい場合、APIプロバイダーのClaude正当性を監査したい場合、複数モデルを並行してテストしたい場合、またはプロバイダーが注入したシステムプロンプトを特定したい場合に使用できます。
anth-security-basics
Anthropic Claude APIのセキュリティベストプラクティスを適用し、キー管理、入力値の検証、プロンプトインジェクション対策を実施します。APIキーの保護、Claudeに送信する前のユーザー入力検証、コンテンツセーフティガードレールの実装が必要な場合に活用できます。「anthropic security」「claude api key security」「secure anthropic」「prompt injection defense」といったフレーズでトリガーされます。
x-ray
x-ray.mdプレ監査レポートを生成します。概要、強化された脅威モデル(プロトコルタイプのプロファイリング、Gitの重み付け攻撃面分析、時間軸リスク分析、コンポーザビリティ依存関係マッピング)、不変条件、統合、ドキュメント品質、テスト分析、開発者・Gitの履歴をカバーしています。「x-ray」「audit readiness」「readiness report」「pre-audit report」「prep this protocol」「protocol prep」「summarize this protocol」のキーワードで実行されます。
semgrep
Semgrepスタティック分析スキャンを実行し、カスタム検出ルールを作成します。Semgrepでのコードスキャン、セキュリティ脆弱性の検出、カスタムYAMLルールの作成、または特定のバグパターンの検出が必要な場合に使用します。重要:ユーザーが「バグをスキャンしたい」「コード品質を確認したい」「脆弱性を見つけたい」「スタティック分析」「セキュリティlint」「コード監査」または「コーディング標準を適用したい」と尋ねた場合も、Semgrepという名称を明記していなくても、このスキルを使用してください。Semgrepは30以上の言語に対応したパターンベースのコードスキャンに最適なツールです。
ghost-bits-cast-attack
Java「ゴーストビッツ」/キャストアタック プレイブック(Black Hat Asia 2026)。16ビット文字が8ビットバイトに暗黙的に縮小されるJavaサービスへの攻撃時に使用します。WAF/IDSを回避して、SQLインジェクション、デシリアライゼーション型RCE、ファイルアップロード(Webシェル)、パストトラバーサル、CRLF インジェクション、リクエストスマグリング、SMTPインジェクションを実行できます。Tomcat、Spring、Jetty、Undertow、Vert.x、Jackson、Fastjson、Apache Commons BCEL、Apache HttpClient、Angus Mail、JDK HttpServer、Lettuce、Jodd、XMLWriterに影響し、WAFバイパスにより多くの「パッチ済み」CVEを再度有効化します。