ストーリー
佐藤CTOがパイプライン設計書を広げました。
対象プロジェクト:マイクロサービスECプラットフォーム
技術スタック
| コンポーネント | 技術 |
|---|---|
| バックエンド | TypeScript (Node.js) |
| フロントエンド | Next.js |
| インフラ | Terraform + AWS |
| コンテナ | Docker + EKS |
| CI/CD | GitHub Actions |
| レジストリ | Amazon ECR |
Mission 1:Pre-commit セキュリティゲートの設計(15分)
開発者がコードをコミットする前に動作するセキュリティチェックを設計してください。
要件
- シークレットスキャンの設定
- Lintingセキュリティルールの定義
- 開発者体験を損なわない実行速度(10秒以内)
- .pre-commit-config.yamlを作成
解答例
# .pre-commit-config.yaml
repos:
# シークレットスキャン
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
name: Detect Secrets
# セキュリティ Lint(軽量チェックのみ)
- repo: https://github.com/semgrep/semgrep
rev: v1.50.0
hooks:
- id: semgrep
name: Security Lint
args:
- '--config'
- 'p/typescript'
- '--config'
- '.semgrep/quick-checks.yml'
- '--error'
- '--timeout'
- '5' # 5秒タイムアウト
# Dockerfile セキュリティチェック
- repo: https://github.com/hadolint/hadolint
rev: v2.12.0
hooks:
- id: hadolint
name: Dockerfile Lint
# 秘密鍵の検出
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: detect-private-key
- id: detect-aws-credentials
- id: check-added-large-files
args: ['--maxkb=500']
# .semgrep/quick-checks.yml(軽量ルール)
rules:
- id: no-eval
pattern: eval($X)
message: "eval() は禁止されています"
languages: [typescript, javascript]
severity: ERROR
- id: no-innerhtml
pattern: $EL.innerHTML = $X
message: "innerHTML は XSSリスクがあります"
languages: [typescript, javascript]
severity: WARNING
- id: no-hardcoded-password
patterns:
- pattern: |
const password = "..."
message: "パスワードのハードコードは禁止です"
languages: [typescript, javascript]
severity: ERROR
Mission 2:CI パイプラインのセキュリティゲート設計(15分)
GitHub ActionsでのCIパイプラインにセキュリティチェックを組み込んでください。
要件
- SAST(Semgrep + SonarQube)
- SCA(Snyk + npm audit)
- コンテナスキャン(Trivy)
- IaCスキャン(Checkov)
- SBOM生成
- セキュリティゲート(全チェック通過を必須に)
解答例
# .github/workflows/security-ci.yml
name: Security CI Pipeline
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true
jobs:
# ===== Phase 1: コードスキャン(並列実行) =====
sast-semgrep:
name: SAST (Semgrep)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/typescript
.semgrep/
sast-sonarqube:
name: SAST (SonarQube)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: sonarsource/sonarqube-scan-action@v2
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
- uses: sonarsource/sonarqube-quality-gate-check@v1
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
sca:
name: SCA (Snyk + npm audit)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: License Check
run: npx license-checker --failOn "GPL-3.0;AGPL-3.0"
secret-scan:
name: Secret Scanning
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
iac-scan:
name: IaC Security (Checkov)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bridgecrewio/checkov-action@master
with:
directory: ./infrastructure
framework: terraform
output_format: sarif
# ===== Phase 2: ビルドとコンテナスキャン =====
container-security:
name: Container Security
needs: [sast-semgrep, sca, secret-scan]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t app:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: app:${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
# ===== Security Gate =====
security-gate:
name: Security Gate
needs: [sast-semgrep, sast-sonarqube, sca, secret-scan, iac-scan, container-security]
runs-on: ubuntu-latest
steps:
- name: All Security Checks Passed
run: echo "All security checks passed. Ready for merge."
Mission 3:DAST統合の設計(15分)
ステージング環境へのデプロイ後に自動実行されるDASTパイプラインを設計してください。
要件
- OWASP ZAPによるフルスキャン
- APIセキュリティテスト(OpenAPI仕様ベース)
- スキャン結果の通知とチケット自動起票
- スキャンの除外ルール設定
解答例
# .github/workflows/dast.yml
name: DAST Pipeline
on:
deployment_status:
# ステージングデプロイ完了後に実行
jobs:
dast-scan:
if: >-
github.event.deployment_status.state == 'success' &&
github.event.deployment_status.environment == 'staging'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Wait for application readiness
run: |
TARGET_URL="${{ github.event.deployment_status.target_url }}"
for i in $(seq 1 30); do
if curl -s -o /dev/null -w "%{http_code}" "$TARGET_URL/health" | grep -q "200"; then
echo "Application is ready"
exit 0
fi
echo "Waiting... ($i/30)"
sleep 10
done
echo "Application not ready after 5 minutes"
exit 1
- name: OWASP ZAP API Scan
uses: zaproxy/action-api-scan@v0.7.0
with:
target: ${{ github.event.deployment_status.target_url }}/api/openapi.json
format: openapi
rules_file_name: .zap/rules.tsv
allow_issue_writing: true
- name: OWASP ZAP Full Scan
uses: zaproxy/action-full-scan@v0.10.0
with:
target: ${{ github.event.deployment_status.target_url }}
rules_file_name: .zap/rules.tsv
- name: Notify Results
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "DAST scan detected vulnerabilities in staging",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*DAST Scan Alert*\nVulnerabilities found in staging deployment.\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Results>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SECURITY_SLACK_WEBHOOK }}
Mission 4:セキュリティダッシュボードとメトリクス設計(15分)
パイプライン全体のセキュリティ状況を可視化するダッシュボードを設計してください。
要件
- KPI/メトリクスの定義
- ダッシュボードのレイアウト設計
- アラートルールの設定
- レポーティングの自動化
解答例
// セキュリティダッシュボードのデータモデル
interface SecurityDashboard {
overview: {
totalVulnerabilities: number;
criticalCount: number;
highCount: number;
mediumCount: number;
lowCount: number;
mttrCritical: string; // Mean Time to Remediate
mttrHigh: string;
pipelinePassRate: number; // %
sbomCoverage: number; // %
};
trends: {
period: string;
newVulnerabilities: number;
resolvedVulnerabilities: number;
openVulnerabilities: number;
}[];
byCategory: {
sast: { total: number; critical: number; falsePositiveRate: number };
sca: { total: number; critical: number; outdatedDeps: number };
dast: { total: number; critical: number; lastScanDate: string };
container: { total: number; critical: number; baseImageAge: number };
iac: { total: number; critical: number; policyViolations: number };
};
}
// アラートルール
interface AlertRule {
name: string;
condition: string;
severity: 'critical' | 'warning' | 'info';
channel: string;
cooldown: string;
}
const alertRules: AlertRule[] = [
{
name: 'Critical Vulnerability Detected',
condition: 'new_vulnerability.severity == "critical"',
severity: 'critical',
channel: '#security-incidents',
cooldown: '0m', // 即座に通知
},
{
name: 'MTTR SLA Breach',
condition: 'vulnerability.age > sla[vulnerability.severity]',
severity: 'warning',
channel: '#security-alerts',
cooldown: '4h',
},
{
name: 'Pipeline Pass Rate Drop',
condition: 'pipeline.pass_rate < 80',
severity: 'warning',
channel: '#engineering',
cooldown: '24h',
},
{
name: 'Secret Leak Detected',
condition: 'secret_scan.findings > 0',
severity: 'critical',
channel: '#security-incidents',
cooldown: '0m',
},
];
SLA(Service Level Agreement)
| 重要度 | 検出からの修正期限 | エスカレーション |
|---|---|---|
| Critical | 24時間以内 | 4時間後に自動エスカレーション |
| High | 7日以内 | 3日後にリマインド |
| Medium | 30日以内 | 14日後にリマインド |
| Low | 90日以内 | バックログで管理 |
評価基準
| 評価項目 | 配点 | 合格ライン |
|---|---|---|
| Pre-commitゲートの設計 | 20% | 実行速度と網羅性のバランスが取れている |
| CIパイプラインの設計 | 30% | SAST/SCA/コンテナ/IaCが適切に統合されている |
| DAST統合の設計 | 25% | 自動化されたDASTと通知フローが設計されている |
| ダッシュボードとメトリクス | 25% | KPIとSLAが定義されている |
推定読了時間: 60分