mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-14 13:53:29 +08:00
Revert "feat(ecc): prune plugin 43→12 items, promote 7 rules to .claude/rules/ (#245)"
This reverts commit 1bd68ff534.
This commit is contained in:
494
docs/ja-JP/skills/security-review/SKILL.md
Normal file
494
docs/ja-JP/skills/security-review/SKILL.md
Normal file
@@ -0,0 +1,494 @@
|
||||
---
|
||||
name: security-review
|
||||
description: 認証の追加、ユーザー入力の処理、シークレットの操作、APIエンドポイントの作成、支払い/機密機能の実装時にこのスキルを使用します。包括的なセキュリティチェックリストとパターンを提供します。
|
||||
---
|
||||
|
||||
# セキュリティレビュースキル
|
||||
|
||||
このスキルは、すべてのコードがセキュリティのベストプラクティスに従い、潜在的な脆弱性を特定することを保証します。
|
||||
|
||||
## 有効化するタイミング
|
||||
|
||||
- 認証または認可の実装
|
||||
- ユーザー入力またはファイルアップロードの処理
|
||||
- 新しいAPIエンドポイントの作成
|
||||
- シークレットまたは資格情報の操作
|
||||
- 支払い機能の実装
|
||||
- 機密データの保存または送信
|
||||
- サードパーティAPIの統合
|
||||
|
||||
## セキュリティチェックリスト
|
||||
|
||||
### 1. シークレット管理
|
||||
|
||||
#### ❌ 絶対にしないこと
|
||||
```typescript
|
||||
const apiKey = "sk-proj-xxxxx" // ハードコードされたシークレット
|
||||
const dbPassword = "password123" // ソースコード内
|
||||
```
|
||||
|
||||
#### ✅ 常にすること
|
||||
```typescript
|
||||
const apiKey = process.env.OPENAI_API_KEY
|
||||
const dbUrl = process.env.DATABASE_URL
|
||||
|
||||
// シークレットが存在することを確認
|
||||
if (!apiKey) {
|
||||
throw new Error('OPENAI_API_KEY not configured')
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] ハードコードされたAPIキー、トークン、パスワードなし
|
||||
- [ ] すべてのシークレットを環境変数に
|
||||
- [ ] `.env.local`を.gitignoreに
|
||||
- [ ] git履歴にシークレットなし
|
||||
- [ ] 本番シークレットはホスティングプラットフォーム(Vercel、Railway)に
|
||||
|
||||
### 2. 入力検証
|
||||
|
||||
#### 常にユーザー入力を検証
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// 検証スキーマを定義
|
||||
const CreateUserSchema = z.object({
|
||||
email: z.string().email(),
|
||||
name: z.string().min(1).max(100),
|
||||
age: z.number().int().min(0).max(150)
|
||||
})
|
||||
|
||||
// 処理前に検証
|
||||
export async function createUser(input: unknown) {
|
||||
try {
|
||||
const validated = CreateUserSchema.parse(input)
|
||||
return await db.users.create(validated)
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return { success: false, errors: error.errors }
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ファイルアップロード検証
|
||||
```typescript
|
||||
function validateFileUpload(file: File) {
|
||||
// サイズチェック(最大5MB)
|
||||
const maxSize = 5 * 1024 * 1024
|
||||
if (file.size > maxSize) {
|
||||
throw new Error('File too large (max 5MB)')
|
||||
}
|
||||
|
||||
// タイプチェック
|
||||
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
throw new Error('Invalid file type')
|
||||
}
|
||||
|
||||
// 拡張子チェック
|
||||
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']
|
||||
const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0]
|
||||
if (!extension || !allowedExtensions.includes(extension)) {
|
||||
throw new Error('Invalid file extension')
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] すべてのユーザー入力をスキーマで検証
|
||||
- [ ] ファイルアップロードを制限(サイズ、タイプ、拡張子)
|
||||
- [ ] クエリでのユーザー入力の直接使用なし
|
||||
- [ ] ホワイトリスト検証(ブラックリストではなく)
|
||||
- [ ] エラーメッセージが機密情報を漏らさない
|
||||
|
||||
### 3. SQLインジェクション防止
|
||||
|
||||
#### ❌ 絶対にSQLを連結しない
|
||||
```typescript
|
||||
// 危険 - SQLインジェクションの脆弱性
|
||||
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
||||
await db.query(query)
|
||||
```
|
||||
|
||||
#### ✅ 常にパラメータ化されたクエリを使用
|
||||
```typescript
|
||||
// 安全 - パラメータ化されたクエリ
|
||||
const { data } = await supabase
|
||||
.from('users')
|
||||
.select('*')
|
||||
.eq('email', userEmail)
|
||||
|
||||
// または生のSQLで
|
||||
await db.query(
|
||||
'SELECT * FROM users WHERE email = $1',
|
||||
[userEmail]
|
||||
)
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] すべてのデータベースクエリがパラメータ化されたクエリを使用
|
||||
- [ ] SQLでの文字列連結なし
|
||||
- [ ] ORM/クエリビルダーを正しく使用
|
||||
- [ ] Supabaseクエリが適切にサニタイズされている
|
||||
|
||||
### 4. 認証と認可
|
||||
|
||||
#### JWTトークン処理
|
||||
```typescript
|
||||
// ❌ 誤り:localStorage(XSSに脆弱)
|
||||
localStorage.setItem('token', token)
|
||||
|
||||
// ✅ 正解:httpOnly Cookie
|
||||
res.setHeader('Set-Cookie',
|
||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||
```
|
||||
|
||||
#### 認可チェック
|
||||
```typescript
|
||||
export async function deleteUser(userId: string, requesterId: string) {
|
||||
// 常に最初に認可を確認
|
||||
const requester = await db.users.findUnique({
|
||||
where: { id: requesterId }
|
||||
})
|
||||
|
||||
if (requester.role !== 'admin') {
|
||||
return NextResponse.json(
|
||||
{ error: 'Unauthorized' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// 削除を続行
|
||||
await db.users.delete({ where: { id: userId } })
|
||||
}
|
||||
```
|
||||
|
||||
#### 行レベルセキュリティ (Supabase)
|
||||
```sql
|
||||
-- すべてのテーブルでRLSを有効化
|
||||
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- ユーザーは自分のデータのみを表示できる
|
||||
CREATE POLICY "Users view own data"
|
||||
ON users FOR SELECT
|
||||
USING (auth.uid() = id);
|
||||
|
||||
-- ユーザーは自分のデータのみを更新できる
|
||||
CREATE POLICY "Users update own data"
|
||||
ON users FOR UPDATE
|
||||
USING (auth.uid() = id);
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] トークンはhttpOnly Cookieに保存(localStorageではなく)
|
||||
- [ ] 機密操作前の認可チェック
|
||||
- [ ] SupabaseでRow Level Securityを有効化
|
||||
- [ ] ロールベースのアクセス制御を実装
|
||||
- [ ] セッション管理が安全
|
||||
|
||||
### 5. XSS防止
|
||||
|
||||
#### HTMLをサニタイズ
|
||||
```typescript
|
||||
import DOMPurify from 'isomorphic-dompurify'
|
||||
|
||||
// 常にユーザー提供のHTMLをサニタイズ
|
||||
function renderUserContent(html: string) {
|
||||
const clean = DOMPurify.sanitize(html, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
|
||||
ALLOWED_ATTR: []
|
||||
})
|
||||
return <div dangerouslySetInnerHTML={{ __html: clean }} />
|
||||
}
|
||||
```
|
||||
|
||||
#### コンテンツセキュリティポリシー
|
||||
```typescript
|
||||
// next.config.js
|
||||
const securityHeaders = [
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
value: `
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-eval' 'unsafe-inline';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: https:;
|
||||
font-src 'self';
|
||||
connect-src 'self' https://api.example.com;
|
||||
`.replace(/\s{2,}/g, ' ').trim()
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] ユーザー提供のHTMLをサニタイズ
|
||||
- [ ] CSPヘッダーを設定
|
||||
- [ ] 検証されていない動的コンテンツのレンダリングなし
|
||||
- [ ] Reactの組み込みXSS保護を使用
|
||||
|
||||
### 6. CSRF保護
|
||||
|
||||
#### CSRFトークン
|
||||
```typescript
|
||||
import { csrf } from '@/lib/csrf'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const token = request.headers.get('X-CSRF-Token')
|
||||
|
||||
if (!csrf.verify(token)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid CSRF token' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// リクエストを処理
|
||||
}
|
||||
```
|
||||
|
||||
#### SameSite Cookie
|
||||
```typescript
|
||||
res.setHeader('Set-Cookie',
|
||||
`session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] 状態変更操作でCSRFトークン
|
||||
- [ ] すべてのCookieでSameSite=Strict
|
||||
- [ ] ダブルサブミットCookieパターンを実装
|
||||
|
||||
### 7. レート制限
|
||||
|
||||
#### APIレート制限
|
||||
```typescript
|
||||
import rateLimit from 'express-rate-limit'
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15分
|
||||
max: 100, // ウィンドウあたり100リクエスト
|
||||
message: 'Too many requests'
|
||||
})
|
||||
|
||||
// ルートに適用
|
||||
app.use('/api/', limiter)
|
||||
```
|
||||
|
||||
#### 高コスト操作
|
||||
```typescript
|
||||
// 検索の積極的なレート制限
|
||||
const searchLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1分
|
||||
max: 10, // 1分あたり10リクエスト
|
||||
message: 'Too many search requests'
|
||||
})
|
||||
|
||||
app.use('/api/search', searchLimiter)
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] すべてのAPIエンドポイントでレート制限
|
||||
- [ ] 高コスト操作でより厳しい制限
|
||||
- [ ] IPベースのレート制限
|
||||
- [ ] ユーザーベースのレート制限(認証済み)
|
||||
|
||||
### 8. 機密データの露出
|
||||
|
||||
#### ロギング
|
||||
```typescript
|
||||
// ❌ 誤り:機密データをログに記録
|
||||
console.log('User login:', { email, password })
|
||||
console.log('Payment:', { cardNumber, cvv })
|
||||
|
||||
// ✅ 正解:機密データを編集
|
||||
console.log('User login:', { email, userId })
|
||||
console.log('Payment:', { last4: card.last4, userId })
|
||||
```
|
||||
|
||||
#### エラーメッセージ
|
||||
```typescript
|
||||
// ❌ 誤り:内部詳細を露出
|
||||
catch (error) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message, stack: error.stack },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
// ✅ 正解:一般的なエラーメッセージ
|
||||
catch (error) {
|
||||
console.error('Internal error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'An error occurred. Please try again.' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] ログにパスワード、トークン、シークレットなし
|
||||
- [ ] ユーザー向けの一般的なエラーメッセージ
|
||||
- [ ] 詳細なエラーはサーバーログのみ
|
||||
- [ ] ユーザーにスタックトレースを露出しない
|
||||
|
||||
### 9. ブロックチェーンセキュリティ (Solana)
|
||||
|
||||
#### ウォレット検証
|
||||
```typescript
|
||||
import { verify } from '@solana/web3.js'
|
||||
|
||||
async function verifyWalletOwnership(
|
||||
publicKey: string,
|
||||
signature: string,
|
||||
message: string
|
||||
) {
|
||||
try {
|
||||
const isValid = verify(
|
||||
Buffer.from(message),
|
||||
Buffer.from(signature, 'base64'),
|
||||
Buffer.from(publicKey, 'base64')
|
||||
)
|
||||
return isValid
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### トランザクション検証
|
||||
```typescript
|
||||
async function verifyTransaction(transaction: Transaction) {
|
||||
// 受信者を検証
|
||||
if (transaction.to !== expectedRecipient) {
|
||||
throw new Error('Invalid recipient')
|
||||
}
|
||||
|
||||
// 金額を検証
|
||||
if (transaction.amount > maxAmount) {
|
||||
throw new Error('Amount exceeds limit')
|
||||
}
|
||||
|
||||
// ユーザーに十分な残高があることを確認
|
||||
const balance = await getBalance(transaction.from)
|
||||
if (balance < transaction.amount) {
|
||||
throw new Error('Insufficient balance')
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] ウォレット署名を検証
|
||||
- [ ] トランザクション詳細を検証
|
||||
- [ ] トランザクション前の残高チェック
|
||||
- [ ] ブラインドトランザクション署名なし
|
||||
|
||||
### 10. 依存関係セキュリティ
|
||||
|
||||
#### 定期的な更新
|
||||
```bash
|
||||
# 脆弱性をチェック
|
||||
npm audit
|
||||
|
||||
# 自動修正可能な問題を修正
|
||||
npm audit fix
|
||||
|
||||
# 依存関係を更新
|
||||
npm update
|
||||
|
||||
# 古いパッケージをチェック
|
||||
npm outdated
|
||||
```
|
||||
|
||||
#### ロックファイル
|
||||
```bash
|
||||
# 常にロックファイルをコミット
|
||||
git add package-lock.json
|
||||
|
||||
# CI/CDで再現可能なビルドに使用
|
||||
npm ci # npm installの代わりに
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
- [ ] 依存関係が最新
|
||||
- [ ] 既知の脆弱性なし(npm auditクリーン)
|
||||
- [ ] ロックファイルをコミット
|
||||
- [ ] GitHubでDependabotを有効化
|
||||
- [ ] 定期的なセキュリティ更新
|
||||
|
||||
## セキュリティテスト
|
||||
|
||||
### 自動セキュリティテスト
|
||||
```typescript
|
||||
// 認証をテスト
|
||||
test('requires authentication', async () => {
|
||||
const response = await fetch('/api/protected')
|
||||
expect(response.status).toBe(401)
|
||||
})
|
||||
|
||||
// 認可をテスト
|
||||
test('requires admin role', async () => {
|
||||
const response = await fetch('/api/admin', {
|
||||
headers: { Authorization: `Bearer ${userToken}` }
|
||||
})
|
||||
expect(response.status).toBe(403)
|
||||
})
|
||||
|
||||
// 入力検証をテスト
|
||||
test('rejects invalid input', async () => {
|
||||
const response = await fetch('/api/users', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email: 'not-an-email' })
|
||||
})
|
||||
expect(response.status).toBe(400)
|
||||
})
|
||||
|
||||
// レート制限をテスト
|
||||
test('enforces rate limits', async () => {
|
||||
const requests = Array(101).fill(null).map(() =>
|
||||
fetch('/api/endpoint')
|
||||
)
|
||||
|
||||
const responses = await Promise.all(requests)
|
||||
const tooManyRequests = responses.filter(r => r.status === 429)
|
||||
|
||||
expect(tooManyRequests.length).toBeGreaterThan(0)
|
||||
})
|
||||
```
|
||||
|
||||
## デプロイ前セキュリティチェックリスト
|
||||
|
||||
すべての本番デプロイメントの前に:
|
||||
|
||||
- [ ] **シークレット**:ハードコードされたシークレットなし、すべて環境変数に
|
||||
- [ ] **入力検証**:すべてのユーザー入力を検証
|
||||
- [ ] **SQLインジェクション**:すべてのクエリをパラメータ化
|
||||
- [ ] **XSS**:ユーザーコンテンツをサニタイズ
|
||||
- [ ] **CSRF**:保護を有効化
|
||||
- [ ] **認証**:適切なトークン処理
|
||||
- [ ] **認可**:ロールチェックを配置
|
||||
- [ ] **レート制限**:すべてのエンドポイントで有効化
|
||||
- [ ] **HTTPS**:本番で強制
|
||||
- [ ] **セキュリティヘッダー**:CSP、X-Frame-Optionsを設定
|
||||
- [ ] **エラー処理**:エラーに機密データなし
|
||||
- [ ] **ロギング**:ログに機密データなし
|
||||
- [ ] **依存関係**:最新、脆弱性なし
|
||||
- [ ] **Row Level Security**:Supabaseで有効化
|
||||
- [ ] **CORS**:適切に設定
|
||||
- [ ] **ファイルアップロード**:検証済み(サイズ、タイプ)
|
||||
- [ ] **ウォレット署名**:検証済み(ブロックチェーンの場合)
|
||||
|
||||
## リソース
|
||||
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||
- [Next.js Security](https://nextjs.org/docs/security)
|
||||
- [Supabase Security](https://supabase.com/docs/guides/auth)
|
||||
- [Web Security Academy](https://portswigger.net/web-security)
|
||||
|
||||
---
|
||||
|
||||
**覚えておいてください**:セキュリティはオプションではありません。1つの脆弱性がプラットフォーム全体を危険にさらす可能性があります。疑わしい場合は、慎重に判断してください。
|
||||
@@ -0,0 +1,361 @@
|
||||
| name | description |
|
||||
|------|-------------|
|
||||
| cloud-infrastructure-security | クラウドプラットフォームへのデプロイ、インフラストラクチャの設定、IAMポリシーの管理、ロギング/モニタリングの設定、CI/CDパイプラインの実装時にこのスキルを使用します。ベストプラクティスに沿ったクラウドセキュリティチェックリストを提供します。 |
|
||||
|
||||
# クラウドおよびインフラストラクチャセキュリティスキル
|
||||
|
||||
このスキルは、クラウドインフラストラクチャ、CI/CDパイプライン、デプロイメント設定がセキュリティのベストプラクティスに従い、業界標準に準拠することを保証します。
|
||||
|
||||
## 有効化するタイミング
|
||||
|
||||
- クラウドプラットフォーム(AWS、Vercel、Railway、Cloudflare)へのアプリケーションのデプロイ
|
||||
- IAMロールと権限の設定
|
||||
- CI/CDパイプラインの設定
|
||||
- インフラストラクチャをコードとして実装(Terraform、CloudFormation)
|
||||
- ロギングとモニタリングの設定
|
||||
- クラウド環境でのシークレット管理
|
||||
- CDNとエッジセキュリティの設定
|
||||
- 災害復旧とバックアップ戦略の実装
|
||||
|
||||
## クラウドセキュリティチェックリスト
|
||||
|
||||
### 1. IAMとアクセス制御
|
||||
|
||||
#### 最小権限の原則
|
||||
|
||||
```yaml
|
||||
# ✅ 正解:最小限の権限
|
||||
iam_role:
|
||||
permissions:
|
||||
- s3:GetObject # 読み取りアクセスのみ
|
||||
- s3:ListBucket
|
||||
resources:
|
||||
- arn:aws:s3:::my-bucket/* # 特定のバケットのみ
|
||||
|
||||
# ❌ 誤り:過度に広範な権限
|
||||
iam_role:
|
||||
permissions:
|
||||
- s3:* # すべてのS3アクション
|
||||
resources:
|
||||
- "*" # すべてのリソース
|
||||
```
|
||||
|
||||
#### 多要素認証(MFA)
|
||||
|
||||
```bash
|
||||
# 常にroot/adminアカウントでMFAを有効化
|
||||
aws iam enable-mfa-device \
|
||||
--user-name admin \
|
||||
--serial-number arn:aws:iam::123456789:mfa/admin \
|
||||
--authentication-code1 123456 \
|
||||
--authentication-code2 789012
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] 本番環境でrootアカウントを使用しない
|
||||
- [ ] すべての特権アカウントでMFAを有効化
|
||||
- [ ] サービスアカウントは長期資格情報ではなくロールを使用
|
||||
- [ ] IAMポリシーは最小権限に従う
|
||||
- [ ] 定期的なアクセスレビューを実施
|
||||
- [ ] 未使用の資格情報をローテーションまたは削除
|
||||
|
||||
### 2. シークレット管理
|
||||
|
||||
#### クラウドシークレットマネージャー
|
||||
|
||||
```typescript
|
||||
// ✅ 正解:クラウドシークレットマネージャーを使用
|
||||
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
|
||||
|
||||
const client = new SecretsManager({ region: 'us-east-1' });
|
||||
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
|
||||
const apiKey = JSON.parse(secret.SecretString).key;
|
||||
|
||||
// ❌ 誤り:ハードコードまたは環境変数のみ
|
||||
const apiKey = process.env.API_KEY; // ローテーションされず、監査されない
|
||||
```
|
||||
|
||||
#### シークレットローテーション
|
||||
|
||||
```bash
|
||||
# データベース資格情報の自動ローテーションを設定
|
||||
aws secretsmanager rotate-secret \
|
||||
--secret-id prod/db-password \
|
||||
--rotation-lambda-arn arn:aws:lambda:region:account:function:rotate \
|
||||
--rotation-rules AutomaticallyAfterDays=30
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] すべてのシークレットをクラウドシークレットマネージャーに保存(AWS Secrets Manager、Vercel Secrets)
|
||||
- [ ] データベース資格情報の自動ローテーションを有効化
|
||||
- [ ] APIキーを少なくとも四半期ごとにローテーション
|
||||
- [ ] コード、ログ、エラーメッセージにシークレットなし
|
||||
- [ ] シークレットアクセスの監査ログを有効化
|
||||
|
||||
### 3. ネットワークセキュリティ
|
||||
|
||||
#### VPCとファイアウォール設定
|
||||
|
||||
```terraform
|
||||
# ✅ 正解:制限されたセキュリティグループ
|
||||
resource "aws_security_group" "app" {
|
||||
name = "app-sg"
|
||||
|
||||
ingress {
|
||||
from_port = 443
|
||||
to_port = 443
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["10.0.0.0/16"] # 内部VPCのみ
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 443
|
||||
to_port = 443
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"] # HTTPS送信のみ
|
||||
}
|
||||
}
|
||||
|
||||
# ❌ 誤り:インターネットに公開
|
||||
resource "aws_security_group" "bad" {
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 65535
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"] # すべてのポート、すべてのIP!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] データベースは公開アクセス不可
|
||||
- [ ] SSH/RDPポートはVPN/bastionのみに制限
|
||||
- [ ] セキュリティグループは最小権限に従う
|
||||
- [ ] ネットワークACLを設定
|
||||
- [ ] VPCフローログを有効化
|
||||
|
||||
### 4. ロギングとモニタリング
|
||||
|
||||
#### CloudWatch/ロギング設定
|
||||
|
||||
```typescript
|
||||
// ✅ 正解:包括的なロギング
|
||||
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';
|
||||
|
||||
const logSecurityEvent = async (event: SecurityEvent) => {
|
||||
await cloudwatch.putLogEvents({
|
||||
logGroupName: '/aws/security/events',
|
||||
logStreamName: 'authentication',
|
||||
logEvents: [{
|
||||
timestamp: Date.now(),
|
||||
message: JSON.stringify({
|
||||
type: event.type,
|
||||
userId: event.userId,
|
||||
ip: event.ip,
|
||||
result: event.result,
|
||||
// 機密データをログに記録しない
|
||||
})
|
||||
}]
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] すべてのサービスでCloudWatch/ロギングを有効化
|
||||
- [ ] 失敗した認証試行をログに記録
|
||||
- [ ] 管理者アクションを監査
|
||||
- [ ] ログ保持を設定(コンプライアンスのため90日以上)
|
||||
- [ ] 疑わしいアクティビティのアラートを設定
|
||||
- [ ] ログを一元化し、改ざん防止
|
||||
|
||||
### 5. CI/CDパイプラインセキュリティ
|
||||
|
||||
#### 安全なパイプライン設定
|
||||
|
||||
```yaml
|
||||
# ✅ 正解:安全なGitHub Actionsワークフロー
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read # 最小限の権限
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# シークレットをスキャン
|
||||
- name: Secret scanning
|
||||
uses: trufflesecurity/trufflehog@main
|
||||
|
||||
# 依存関係監査
|
||||
- name: Audit dependencies
|
||||
run: npm audit --audit-level=high
|
||||
|
||||
# 長期トークンではなくOIDCを使用
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
|
||||
aws-region: us-east-1
|
||||
```
|
||||
|
||||
#### サプライチェーンセキュリティ
|
||||
|
||||
```json
|
||||
// package.json - ロックファイルと整合性チェックを使用
|
||||
{
|
||||
"scripts": {
|
||||
"install": "npm ci", // 再現可能なビルドにciを使用
|
||||
"audit": "npm audit --audit-level=moderate",
|
||||
"check": "npm outdated"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] 長期資格情報ではなくOIDCを使用
|
||||
- [ ] パイプラインでシークレットスキャン
|
||||
- [ ] 依存関係の脆弱性スキャン
|
||||
- [ ] コンテナイメージスキャン(該当する場合)
|
||||
- [ ] ブランチ保護ルールを強制
|
||||
- [ ] マージ前にコードレビューが必要
|
||||
- [ ] 署名付きコミットを強制
|
||||
|
||||
### 6. CloudflareとCDNセキュリティ
|
||||
|
||||
#### Cloudflareセキュリティ設定
|
||||
|
||||
```typescript
|
||||
// ✅ 正解:セキュリティヘッダー付きCloudflare Workers
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const response = await fetch(request);
|
||||
|
||||
// セキュリティヘッダーを追加
|
||||
const headers = new Headers(response.headers);
|
||||
headers.set('X-Frame-Options', 'DENY');
|
||||
headers.set('X-Content-Type-Options', 'nosniff');
|
||||
headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
headers.set('Permissions-Policy', 'geolocation=(), microphone=()');
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
headers
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### WAFルール
|
||||
|
||||
```bash
|
||||
# Cloudflare WAF管理ルールを有効化
|
||||
# - OWASP Core Ruleset
|
||||
# - Cloudflare Managed Ruleset
|
||||
# - レート制限ルール
|
||||
# - ボット保護
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] OWASPルール付きWAFを有効化
|
||||
- [ ] レート制限を設定
|
||||
- [ ] ボット保護を有効化
|
||||
- [ ] DDoS保護を有効化
|
||||
- [ ] セキュリティヘッダーを設定
|
||||
- [ ] SSL/TLS厳格モードを有効化
|
||||
|
||||
### 7. バックアップと災害復旧
|
||||
|
||||
#### 自動バックアップ
|
||||
|
||||
```terraform
|
||||
# ✅ 正解:自動RDSバックアップ
|
||||
resource "aws_db_instance" "main" {
|
||||
allocated_storage = 20
|
||||
engine = "postgres"
|
||||
|
||||
backup_retention_period = 30 # 30日間保持
|
||||
backup_window = "03:00-04:00"
|
||||
maintenance_window = "mon:04:00-mon:05:00"
|
||||
|
||||
enabled_cloudwatch_logs_exports = ["postgresql"]
|
||||
|
||||
deletion_protection = true # 偶発的な削除を防止
|
||||
}
|
||||
```
|
||||
|
||||
#### 検証ステップ
|
||||
|
||||
- [ ] 自動日次バックアップを設定
|
||||
- [ ] バックアップ保持がコンプライアンス要件を満たす
|
||||
- [ ] ポイントインタイムリカバリを有効化
|
||||
- [ ] 四半期ごとにバックアップテストを実施
|
||||
- [ ] 災害復旧計画を文書化
|
||||
- [ ] RPOとRTOを定義してテスト
|
||||
|
||||
## デプロイ前クラウドセキュリティチェックリスト
|
||||
|
||||
すべての本番クラウドデプロイメントの前に:
|
||||
|
||||
- [ ] **IAM**:rootアカウントを使用しない、MFAを有効化、最小権限ポリシー
|
||||
- [ ] **シークレット**:すべてのシークレットをローテーション付きクラウドシークレットマネージャーに
|
||||
- [ ] **ネットワーク**:セキュリティグループを制限、公開データベースなし
|
||||
- [ ] **ロギング**:保持付きCloudWatch/ロギングを有効化
|
||||
- [ ] **モニタリング**:異常のアラートを設定
|
||||
- [ ] **CI/CD**:OIDC認証、シークレットスキャン、依存関係監査
|
||||
- [ ] **CDN/WAF**:OWASPルール付きCloudflare WAFを有効化
|
||||
- [ ] **暗号化**:静止時および転送中のデータを暗号化
|
||||
- [ ] **バックアップ**:テスト済みリカバリ付き自動バックアップ
|
||||
- [ ] **コンプライアンス**:GDPR/HIPAA要件を満たす(該当する場合)
|
||||
- [ ] **ドキュメント**:インフラストラクチャを文書化、ランブックを作成
|
||||
- [ ] **インシデント対応**:セキュリティインシデント計画を配置
|
||||
|
||||
## 一般的なクラウドセキュリティ設定ミス
|
||||
|
||||
### S3バケットの露出
|
||||
|
||||
```bash
|
||||
# ❌ 誤り:公開バケット
|
||||
aws s3api put-bucket-acl --bucket my-bucket --acl public-read
|
||||
|
||||
# ✅ 正解:特定のアクセス付きプライベートバケット
|
||||
aws s3api put-bucket-acl --bucket my-bucket --acl private
|
||||
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
|
||||
```
|
||||
|
||||
### RDS公開アクセス
|
||||
|
||||
```terraform
|
||||
# ❌ 誤り
|
||||
resource "aws_db_instance" "bad" {
|
||||
publicly_accessible = true # 絶対にこれをしない!
|
||||
}
|
||||
|
||||
# ✅ 正解
|
||||
resource "aws_db_instance" "good" {
|
||||
publicly_accessible = false
|
||||
vpc_security_group_ids = [aws_security_group.db.id]
|
||||
}
|
||||
```
|
||||
|
||||
## リソース
|
||||
|
||||
- [AWS Security Best Practices](https://aws.amazon.com/security/best-practices/)
|
||||
- [CIS AWS Foundations Benchmark](https://www.cisecurity.org/benchmark/amazon_web_services)
|
||||
- [Cloudflare Security Documentation](https://developers.cloudflare.com/security/)
|
||||
- [OWASP Cloud Security](https://owasp.org/www-project-cloud-security/)
|
||||
- [Terraform Security Best Practices](https://www.terraform.io/docs/cloud/guides/recommended-practices/)
|
||||
|
||||
**覚えておいてください**:クラウドの設定ミスはデータ侵害の主要な原因です。1つの露出したS3バケットまたは過度に許容されたIAMポリシーは、インフラストラクチャ全体を危険にさらす可能性があります。常に最小権限の原則と多層防御に従ってください。
|
||||
Reference in New Issue
Block a user