0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SAST,DAST,ファズテスト,Web APIファズテスト,レビューApp

Posted at

SAST,DAST,ファズテスト,Web APIファズテスト,レビューApp

(トップページはこちら) - (アプリケーションのセキュリティを始める)

はじめに

セキュリティ脆弱性の修正コストは、本番環境で発見された場合、開発段階の100倍以上になるという調査結果があります。また、マージリクエストのレビューで「ローカル環境で動作確認できない」という理由で承認が遅れるケースは、開発チームの生産性を大きく低下させます。

GitLabは、これらの課題に対して実用的なソリューションを提供しています。本記事では、CI/CDパイプラインに統合可能なセキュリティテスト(SAST、DAST、APIファジング)と、一時的なプレビュー環境を自動構築するレビューアプリの実装方法を解説します。

本記事で扱う内容:

  • 各セキュリティテスト手法の特徴と使い分け
  • レビューアプリによる効率的なレビュープロセス
  • セキュリティテストとレビューアプリを組み合わせた実践的なワークフロー
  • 段階的な導入戦略と注意点

1. セキュリティテストの全体像

1.1 なぜ複数のテスト手法が必要なのか

単一のセキュリティテストでは、すべての脆弱性を検出できません。各手法には明確な役割分担があります。

各手法の特徴:

手法 検出対象 実行タイミング 実行時間の目安 誤検知率
SAST コードレベルの脆弱性 コミット直後 1-5分 中程度
DAST 実行時の脆弱性 デプロイ後 10-30分 低い
APIファジング API層の脆弱性 デプロイ後 5-15分 低い

1.2 段階的な導入戦略

すべてを一度に導入する必要はありません。以下の順序での導入を推奨します。

フェーズ1: SAST導入 (1-2週間)

  • 最も導入が簡単で、即座に効果が出る
  • デプロイ環境が不要
  • 開発者へのフィードバックが最速

フェーズ2: レビューアプリ導入 (2-4週間)

  • レビュープロセスの効率化
  • DAST/APIファジングの準備

フェーズ3: DAST/APIファジング導入 (2-3週間)

  • レビューアプリを活用した動的テスト
  • 包括的なセキュリティカバレッジの実現

2. SAST (静的アプリケーションセキュリティテスト)

2.1 SASTの仕組みと利点

SASTは、ソースコードを実行せずに脆弱性を検出します。コンパイル前の段階で問題を発見できるため、修正コストを最小限に抑えられます。

検出可能な脆弱性の例:

  • SQLインジェクション
  • クロスサイトスクリプティング (XSS)
  • ハードコードされた認証情報
  • 安全でない暗号化アルゴリズムの使用
  • パストラバーサル

2.2 対応言語とアナライザー

GitLabは、ティアに応じて2種類のアナライザーを提供します。

GitLab Advanced SAST (Ultimate版のみ):

  • ファイル間、関数間の解析が可能
  • より高精度な検出
  • 誤検知の削減

標準アナライザー (全ティア):

  • Semgrepベースのオープンソースアナライザー
  • 基本的な脆弱性検出

完全サポート言語:
C、C++、C#、Go、Java、JavaScript、TypeScript、PHP、Python、Ruby、YAML、Java Properties

標準アナライザーのみサポート:
Apex、Elixir、Groovy、Kotlin、Objective-C、Scala、Swift

2.3 実装手順

2.3.1 最小構成での導入

.gitlab-ci.ymlに以下を追加するだけで開始できます:

include:
  - template: Jobs/SAST.gitlab-ci.yml

前提条件:

  • Linux/amd64のGitLab Runner (DockerまたはKubernetes executor)
  • .gitlab-ci.ymltestステージが定義されていること

2.3.2 実行結果の確認

パイプライン実行後、以下の場所で結果を確認できます:

  1. パイプラインのSecurityタブ (全ティア)

    • 検出された脆弱性の一覧
    • JSONレポートのダウンロード
  2. マージリクエストウィジェット (Ultimate版)

    • 新規に検出された脆弱性
    • 修正された脆弱性
    • インライン表示
  3. 脆弱性レポート (Ultimate版)

    • プロジェクト全体の脆弱性管理
    • ステータス追跡

脆弱性情報の詳細:

# 表示される情報の例
脆弱性タイトル: "SQL Injection in user authentication"
重大度: Critical
ファイル: app/models/user.rb
行番号: 45
説明: |
  ユーザー入力が適切にサニタイズされずにSQLクエリに使用されています。
  攻撃者が任意のSQLコマンドを実行できる可能性があります。
修正方法: |
  パラメータ化クエリまたはORMを使用してください。
  例: User.where("email = ?", params[:email])
CWE: CWE-89

2.4 最適化とカスタマイズ

2.4.1 誤検知の削減

特定のルールを無効化する場合、.gitlab/sast-ruleset.tomlを作成:

[semgrep]
  description = "プロジェクト固有のルールセット"
  
  # 特定のルールを無効化
  [[semgrep.ruleset]]
    disable = true
    [semgrep.ruleset.identifier]
      type = "semgrep_id"
      value = "gosec.G107-1"
  
  # 複数のルールを一括無効化
  [[semgrep.ruleset]]
    disable = true
    [semgrep.ruleset.identifier]
      type = "cwe"
      value = "CWE-338"

2.4.2 スキャン対象の最適化

不要なディレクトリを除外してスキャン時間を短縮:

variables:
  # デフォルト: spec,test,tests,tmp
  # プロジェクトに応じて追加
  SAST_EXCLUDED_PATHS: "spec,test,tests,tmp,node_modules,vendor,dist,build"
  
  # スキャン深度の調整(デフォルト: 20)
  SEARCH_MAX_DEPTH: 15

2.4.3 特定アナライザーの設定

SpotBugs(Java/Scala)でプリコンパイルを使用:

stages:
  - build
  - test

include:
  - template: Jobs/SAST.gitlab-ci.yml

build:
  image: maven:3.6-jdk-8-slim
  stage: build
  script:
    - mvn package -Dmaven.repo.local=./.m2/repository
  artifacts:
    paths:
      - .m2/
      - target/

spotbugs-sast:
  dependencies:
    - build
  variables:
    MAVEN_REPO_PATH: $CI_PROJECT_DIR/.m2/repository
    COMPILE: "false"

2.5 パフォーマンスとコストの考慮

実行時間の目安:

  • 小規模プロジェクト(~10,000行): 1-2分
  • 中規模プロジェクト(~100,000行): 3-5分
  • 大規模プロジェクト(100,000行以上): 5-10分

リソース要件:

  • メモリ: 2-4GB
  • CPU: 2コア推奨

コスト削減のヒント:

# マージリクエストでのみ実行
sast:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

3. DAST (動的アプリケーションセキュリティテスト)

3.1 DASTの特徴と必要性

DASTは、実行中のアプリケーションを外部から検査します。SASTでは検出できない以下の脆弱性を発見できます:

  • 認証・認可の不備
  • セッション管理の問題
  • サーバー設定の脆弱性
  • ランタイム環境固有の問題

SASTとの補完関係:

3.2 前提条件と制約

必須要件:

  • デプロイ済みのテスト環境
  • アクセス可能なURL
  • Ultimate版ライセンス

注意点:

  • 本番環境では実行しない(負荷がかかるため)
  • テストデータを使用する
  • スキャン中は他の変更を加えない

3.3 基本的な実装

stages:
  - build
  - test
  - deploy
  - dast

include:
  - template: DAST.gitlab-ci.yml

# テスト環境へのデプロイ
deploy_test:
  stage: deploy
  script:
    - echo "テスト環境へデプロイ"
    - ./deploy-to-test.sh
  environment:
    name: test
    url: https://test.example.com

# DASTスキャン
dast:
  variables:
    DAST_WEBSITE: https://test.example.com
  dependencies:
    - deploy_test

3.4 実行時間とリソース

スキャン時間の目安:

  • 小規模サイト(~10ページ): 10-15分
  • 中規模サイト(~50ページ): 20-30分
  • 大規模サイト(100ページ以上): 30-60分

時間短縮のテクニック:

dast:
  variables:
    DAST_WEBSITE: https://test.example.com
    # スキャン範囲を制限
    DAST_PATHS: "/api,/admin"
    # 除外パス
    DAST_EXCLUDE_URLS: "https://test.example.com/static"

4. APIファジングテスト

4.1 APIファジングの役割

APIファジングは、APIエンドポイントに対して予期しない入力を送信し、以下を検出します:

  • 入力検証の不備
  • エラーハンドリングの問題
  • レート制限の欠如
  • 認証バイパス

4.2 対応API形式と設定方法

サポートされる仕様:

  • OpenAPI v2/v3
  • GraphQL Schema
  • HTTP Archive (HAR)
  • Postman Collection v2.0/v2.1

4.2.1 OpenAPI仕様を使用する場合

stages:
  - build
  - test
  - deploy
  - fuzz

include:
  - template: API-Fuzzing.gitlab-ci.yml

apifuzzer_fuzz:
  variables:
    FUZZAPI_OPENAPI: openapi.json
    FUZZAPI_TARGET_URL: https://api.example.com

4.2.2 GraphQLの場合

apifuzzer_fuzz:
  variables:
    FUZZAPI_GRAPHQL: schema.graphql
    FUZZAPI_TARGET_URL: https://api.example.com/graphql

4.3 Dockerサービスとの統合

アプリケーションをDockerコンテナとして起動し、同じネットワーク内でテスト:

variables:
  FF_NETWORK_PER_BUILD: "true"

stages:
  - build
  - fuzz

include:
  - template: API-Fuzzing.gitlab-ci.yml

# アプリケーションのビルド
build:
  services:
    - name: docker:dind
      alias: dind
  image: docker:20.10.16
  stage: build
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

# APIファジング実行
apifuzzer_fuzz:
  services:
    # データベース
    - name: postgres:13
      alias: db
    # アプリケーション
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: app
  variables:
    FUZZAPI_TARGET_URL: http://app:3000
    FUZZAPI_OPENAPI: openapi.json
    # データベース接続情報
    DATABASE_URL: postgresql://postgres@db/test

4.4 実行時間の最適化

apifuzzer_fuzz:
  variables:
    FUZZAPI_TARGET_URL: https://api.example.com
    FUZZAPI_OPENAPI: openapi.json
    # テストケース数を制限
    FUZZAPI_HTTP_MAX_REQUESTS: 100
    # タイムアウト設定
    FUZZAPI_HTTP_REQUEST_TIMEOUT: 30

5. レビューアプリの実装

5.1 レビューアプリがもたらす価値

従来のレビュープロセスの課題:

  • レビュアーがローカル環境を構築する必要がある
  • 環境差異による「手元では動く」問題
  • デザイナーやPMが実際の動作を確認できない

レビューアプリによる改善:

  • URLを共有するだけでレビュー可能
  • 環境の一貫性が保証される
  • 非エンジニアも参加できる

5.2 基本的な実装パターン

5.2.1 シンプルな構成

stages:
  - build
  - test
  - deploy

review_app:
  stage: deploy
  script:
    - echo "レビューアプリをデプロイ"
    # 実際のデプロイコマンド
    - ./scripts/deploy-review.sh
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop_review_app
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

stop_review_app:
  stage: deploy
  script:
    - echo "レビューアプリを削除"
    - ./scripts/cleanup-review.sh
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

5.2.2 Kubernetes環境での実装

review_app:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    # 環境変数の設定
    - export KUBE_NAMESPACE="review-$CI_COMMIT_REF_SLUG"
    - export APP_URL="https://$CI_COMMIT_REF_SLUG.review.example.com"
    
    # Namespaceの作成
    - kubectl create namespace $KUBE_NAMESPACE || true
    
    # アプリケーションのデプロイ
    - |
      cat <<EOF | kubectl apply -f -
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: app
        namespace: $KUBE_NAMESPACE
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: review-app
        template:
          metadata:
            labels:
              app: review-app
          spec:
            containers:
            - name: app
              image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
              ports:
              - containerPort: 3000
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: app-service
        namespace: $KUBE_NAMESPACE
      spec:
        selector:
          app: review-app
        ports:
        - port: 80
          targetPort: 3000
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        name: app-ingress
        namespace: $KUBE_NAMESPACE
      spec:
        rules:
        - host: $CI_COMMIT_REF_SLUG.review.example.com
          http:
            paths:
            - path: /
              pathType: Prefix
              backend:
                service:
                  name: app-service
                  port:
                    number: 80
      EOF
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop_review_app
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

stop_review_app:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - export KUBE_NAMESPACE="review-$CI_COMMIT_REF_SLUG"
    - kubectl delete namespace $KUBE_NAMESPACE
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

5.3 自動停止の設定

5.3.1 時間ベースの自動停止

review_app:
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    auto_stop_in: 3 days  # 3日後に自動停止

5.3.2 マージ時の自動停止

マージリクエストがマージまたはクローズされた際に自動的に停止:

review_app:
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop_review_app
    auto_stop_in: 1 week

stop_review_app:
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  # マージ時は自動実行、それ以外は手動
  when: manual

5.4 ルートマップによるナビゲーション強化

ルートマップを使用すると、ソースファイルから対応するレビューアプリのページに直接ジャンプできます。

5.4.1 基本的なルートマップ

.gitlab/route-map.yml:

# 静的ページ
- source: 'src/pages/index.html'
  public: 'index.html'

- source: 'src/pages/about.html'
  public: 'about.html'

# 動的ルーティング(正規表現)
- source: /src\/pages\/(.+)\.html/
  public: '\1.html'

# ブログ記事(日付付き)
- source: /src\/posts\/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+)\.md/
  public: 'blog/\1/\2/\3/\4/'

# APIドキュメント
- source: /docs\/api\/(.+)\.md/
  public: 'api-docs/\1/'

5.4.2 Next.js/Nuxt.jsでの例

# Pagesディレクトリベースのルーティング
- source: /pages\/index\.tsx/
  public: '/'

- source: /pages\/about\.tsx/
  public: '/about'

# 動的ルート
- source: /pages\/blog\/\[slug\]\.tsx/
  public: '/blog/example-post'

- source: /pages\/users\/\[id\]\.tsx/
  public: '/users/1'

# ネストされたルート
- source: /pages\/(.+)\.tsx/
  public: '/\1'

5.5 コスト管理

レビューアプリは便利ですが、リソースコストがかかります。

コスト削減のベストプラクティス:

# 1. ドラフトMRではレビューアプリを作成しない
review_app:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:/

# 2. 特定のラベルが付いた場合のみ作成
review_app:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /needs-review-app/

# 3. 自動停止時間を短く設定
review_app:
  environment:
    auto_stop_in: 2 days

# 4. 夜間に未使用の環境を自動削除
cleanup_old_reviews:
  stage: cleanup
  script:
    - ./scripts/cleanup-inactive-reviews.sh
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"

6. セキュリティテストとレビューアプリの統合

6.1 統合による相乗効果

レビューアプリとセキュリティテストを組み合わせることで、以下が実現できます:

  1. 一貫した環境でのテスト: レビューアプリに対してDASTとAPIファジングを実行
  2. 早期フィードバック: マージ前にセキュリティ問題を発見
  3. レビュアーの負担軽減: セキュリティチェックが自動化される

6.2 完全な統合パイプライン

stages:
  - build
  - test
  - deploy
  - security
  - cleanup

variables:
  # 共通変数
  REVIEW_APP_URL: https://$CI_COMMIT_REF_SLUG.review.example.com

# SASTテンプレートのインクルード
include:
  - template: Jobs/SAST.gitlab-ci.yml
  - template: DAST.gitlab-ci.yml
  - template: API-Fuzzing.gitlab-ci.yml

# ビルドジョブ
build:
  stage: build
  image: docker:20.10.16
  services:
    - docker:dind
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# SAST(ビルド前に実行可能)
sast:
  stage: test
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# レビューアプリのデプロイ
deploy_review:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - export KUBE_NAMESPACE="review-$CI_COMMIT_REF_SLUG"
    - kubectl create namespace $KUBE_NAMESPACE || true
    - |
      cat <<EOF | kubectl apply -f -
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: app
        namespace: $KUBE_NAMESPACE
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: review-app
        template:
          metadata:
            labels:
              app: review-app
          spec:
            containers:
            - name: app
              image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
              ports:
              - containerPort: 3000
              env:
              - name: DATABASE_URL
                value: "postgresql://postgres:password@db:5432/app"
            - name: db
              image: postgres:13
              env:
              - name: POSTGRES_PASSWORD
                value: "password"
              - name: POSTGRES_DB
                value: "app"
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: app-service
        namespace: $KUBE_NAMESPACE
      spec:
        selector:
          app: review-app
        ports:
        - port: 80
          targetPort: 3000
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        name: app-ingress
        namespace: $KUBE_NAMESPACE
        annotations:
          cert-manager.io/cluster-issuer: "letsencrypt-prod"
      spec:
        tls:
        - hosts:
          - $CI_COMMIT_REF_SLUG.review.example.com
          secretName: review-app-tls
        rules:
        - host: $CI_COMMIT_REF_SLUG.review.example.com
          http:
            paths:
            - path: /
              pathType: Prefix
              backend:
                service:
                  name: app-service
                  port:
                    number: 80
      EOF
    # デプロイ完了を待機
    - kubectl wait --for=condition=available --timeout=300s deployment/app -n $KUBE_NAMESPACE
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.review.example.com
    on_stop: stop_review
    auto_stop_in: 3 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# DAST(レビューアプリに対して実行)
dast:
  stage: security
  variables:
    DAST_WEBSITE: $REVIEW_APP_URL
    DAST_FULL_SCAN_ENABLED: "false"  # 時間短縮のため
  dependencies:
    - deploy_review
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# APIファジング(レビューアプリに対して実行)
apifuzzer_fuzz:
  stage: security
  variables:
    FUZZAPI_TARGET_URL: $REVIEW_APP_URL
    FUZZAPI_OPENAPI: openapi.json
    FUZZAPI_HTTP_MAX_REQUESTS: 50  # 時間短縮のため
  dependencies:
    - deploy_review
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# レビューアプリの停止
stop_review:
  stage: cleanup
  image: bitnami/kubectl:latest
  script:
    - export KUBE_NAMESPACE="review-$CI_COMMIT_REF_SLUG"
    - kubectl delete namespace $KUBE_NAMESPACE
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

6.3 パイプラインの実行フロー

6.4 実行時間の最適化

完全なパイプラインは時間がかかるため、以下の最適化を推奨します。

6.4.1 段階的なセキュリティチェック

# ドラフトMR: SASTのみ
sast:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# レビュー準備完了: SAST + レビューアプリ
deploy_review:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:/

# 最終レビュー: 全セキュリティテスト
dast:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /security-check/

apifuzzer_fuzz:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /security-check/

6.4.2 並列実行の活用

# DASTとAPIファジングを並列実行
dast:
  stage: security
  needs:
    - deploy_review

apifuzzer_fuzz:
  stage: security
  needs:
    - deploy_review

6.4.3 キャッシュの活用

sast:
  cache:
    key: sast-$CI_COMMIT_REF_SLUG
    paths:
      - .sast-cache/

build:
  cache:
    key: build-$CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
      - .gradle/

6.5 結果の統合表示

マージリクエストでは、すべてのセキュリティテスト結果が統合されて表示されます(Ultimate版)。

マージリクエストウィジェットに表示される情報:

  • 新規検出された脆弱性の数(SAST、DAST、APIファジング別)
  • 修正された脆弱性の数
  • レビューアプリへのリンク
  • 各脆弱性の詳細(重大度、ファイル、説明)

7. 実践的な導入ガイド

7.1 小規模プロジェクトでの導入例

プロジェクト規模: 開発者2-5名、コードベース~50,000行

推奨構成:

stages:
  - test
  - deploy

include:
  - template: Jobs/SAST.gitlab-ci.yml

# レビューアプリ(手動デプロイ)
review_app:
  stage: deploy
  script:
    - ./deploy-to-heroku.sh
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://$CI_COMMIT_REF_SLUG.herokuapp.com
    on_stop: stop_review
    auto_stop_in: 2 days
  when: manual
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

stop_review:
  stage: deploy
  script:
    - heroku apps:destroy --app $CI_COMMIT_REF_SLUG --confirm $CI_COMMIT_REF_SLUG
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

期待される効果:

  • パイプライン実行時間: 3-5分
  • セキュリティ問題の早期発見
  • レビュー時間の短縮(30-50%)

7.2 中規模プロジェクトでの導入例

プロジェクト規模: 開発者5-20名、コードベース~200,000行

推奨構成:

stages:
  - build
  - test
  - deploy
  - security

variables:
  REVIEW_APP_URL: https://$CI_COMMIT_REF_SLUG.review.example.com

include:
  - template: Jobs/SAST.gitlab-ci.yml
  - template: DAST.gitlab-ci.yml

build:
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

# SASTは常に実行
sast:
  stage: test

# レビューアプリは自動デプロイ
review_app:
  stage: deploy
  script:
    - kubectl apply -f k8s/review-app.yaml
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: $REVIEW_APP_URL
    on_stop: stop_review
    auto_stop_in: 5 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:/

# DASTはラベル付きMRのみ
dast:
  stage: security
  variables:
    DAST_WEBSITE: $REVIEW_APP_URL
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /security-review/

stop_review:
  stage: deploy
  script:
    - kubectl delete -f k8s/review-app.yaml
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual

期待される効果:

  • 通常のパイプライン: 5-10分
  • セキュリティレビュー付き: 15-25分
  • 脆弱性検出率の向上(60-80%)

7.3 大規模プロジェクトでの導入例

プロジェクト規模: 開発者20名以上、コードベース200,000行以上

推奨構成:

stages:
  - build
  - test
  - deploy
  - security-fast
  - security-deep

variables:
  REVIEW_APP_URL: https://$CI_COMMIT_REF_SLUG.review.example.com

include:
  - template: Jobs/SAST.gitlab-ci.yml
  - template: DAST.gitlab-ci.yml
  - template: API-Fuzzing.gitlab-ci.yml

# ビルドの並列化
build:
  stage: build
  parallel:
    matrix:
      - SERVICE: [frontend, backend, api]
  script:
    - docker build -t $CI_REGISTRY_IMAGE/$SERVICE:$CI_COMMIT_SHA ./services/$SERVICE
    - docker push $CI_REGISTRY_IMAGE/$SERVICE:$CI_COMMIT_SHA

# SASTの最適化
sast:
  stage: test
  variables:
    SAST_EXCLUDED_PATHS: "spec,test,tests,tmp,node_modules,vendor,dist,build,public"
    SEARCH_MAX_DEPTH: 10
  cache:
    key: sast-$CI_COMMIT_REF_SLUG
    paths:
      - .sast-cache/

# レビューアプリ(マイクロサービス対応)
review_app:
  stage: deploy
  script:
    - ./scripts/deploy-microservices.sh
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: $REVIEW_APP_URL
    on_stop: stop_review
    auto_stop_in: 7 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:/

# 高速セキュリティチェック(常に実行)
dast_quick:
  stage: security-fast
  variables:
    DAST_WEBSITE: $REVIEW_APP_URL
    DAST_FULL_SCAN_ENABLED: "false"
    DAST_PATHS: "/api,/admin"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

# 詳細セキュリティチェック(ラベル付きのみ)
dast_full:
  stage: security-deep
  variables:
    DAST_WEBSITE: $REVIEW_APP_URL
    DAST_FULL_SCAN_ENABLED: "true"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /full-security-scan/
  when: manual

apifuzzer_fuzz:
  stage: security-deep
  variables:
    FUZZAPI_TARGET_URL: $REVIEW_APP_URL
    FUZZAPI_OPENAPI: openapi.json
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS =~ /full-security-scan/
  when: manual

stop_review:
  stage: deploy
  script:
    - ./scripts/cleanup-microservices.sh
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual

期待される効果:

  • 通常のパイプライン: 10-15分
  • 高速セキュリティチェック付き: 20-30分
  • 完全セキュリティスキャン: 40-60分
  • 複数チームでの並行開発が可能

7.4 よくある失敗パターンと対策

7.4.1 パイプラインが遅すぎる

問題: すべてのセキュリティテストを毎回実行し、パイプラインが1時間以上かかる

対策:

# 段階的なチェック
sast:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

dast:
  rules:
    # 特定のラベルまたはデフォルトブランチへのマージ時のみ
    - if: $CI_MERGE_REQUEST_LABELS =~ /security-check/
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

7.4.2 レビューアプリのコストが高すぎる

問題: 多数のレビューアプリが放置され、インフラコストが増大

対策:

review_app:
  environment:
    # 短い自動停止時間
    auto_stop_in: 2 days
  rules:
    # ドラフトMRでは作成しない
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:/

# 定期的なクリーンアップジョブ
cleanup_old_reviews:
  stage: cleanup
  script:
    - ./scripts/cleanup-inactive-reviews.sh
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"

7.4.3 誤検知が多すぎる

問題: SASTの誤検知が多く、開発者が無視するようになる

対策:

# .gitlab/sast-ruleset.toml
[semgrep]
  description = "プロジェクト固有のルールセット"
  
  # 誤検知の多いルールを無効化
  [[semgrep.ruleset]]
    disable = true
    [semgrep.ruleset.identifier]
      type = "semgrep_id"
      value = "javascript.lang.security.audit.xss.react-dangerouslysetinnerhtml"
# .gitlab-ci.yml
variables:
  # テストコードを除外
  SAST_EXCLUDED_PATHS: "spec,test,tests,tmp,__tests__,__mocks__"

7.4.4 DASTがレビューアプリにアクセスできない

問題: レビューアプリのデプロイ完了前にDASTが開始される

対策:

deploy_review:
  script:
    - kubectl apply -f k8s/review-app.yaml
    # デプロイ完了を待機
    - kubectl wait --for=condition=available --timeout=300s deployment/app
    # ヘルスチェック
    - |
      for i in {1..30}; do
        if curl -f $REVIEW_APP_URL/health; then
          echo "アプリケーション起動完了"
          break
        fi
        echo "起動待機中... ($i/30)"
        sleep 10
      done

dast:
  needs:
    - deploy_review
  # さらに待機時間を追加
  before_script:
    - sleep 30

8. まとめと次のステップ

8.1 導入による効果

適切に実装されたセキュリティテストとレビューアプリの統合により、以下の効果が期待できます:

セキュリティ面:

  • 脆弱性の早期発見(開発段階で80-90%)
  • 本番環境でのインシデント削減(60-70%)
  • セキュリティレビューの自動化

開発効率面:

  • レビュー時間の短縮(30-50%)
  • 手戻りの削減(40-60%)
  • デプロイ頻度の向上(2-3倍)

コスト面:

  • セキュリティインシデント対応コストの削減
  • QA工数の削減(20-30%)
  • インフラコストの最適化

8.2 段階的な導入ロードマップ

第1週: SAST導入

  • .gitlab-ci.ymlにSASTテンプレートを追加
  • 初回スキャン結果の確認
  • 誤検知の多いルールを調整

第2-3週: SAST最適化

  • 除外パスの設定
  • カスタムルールセットの作成
  • チームへの教育

第4-5週: レビューアプリ導入

  • 簡単なデプロイスクリプトの作成
  • 手動デプロイでの動作確認
  • 自動デプロイへの移行

第6-7週: レビューアプリ最適化

  • 自動停止の設定
  • ルートマップの追加
  • コスト監視の実装

第8-9週: DAST/APIファジング導入

  • レビューアプリに対するDASTスキャン
  • APIファジングの設定
  • 実行時間の最適化

第10週以降: 継続的改善

  • パイプラインのパフォーマンス監視
  • セキュリティルールの定期的な見直し
  • チームフィードバックの収集と改善

8.3 成功のためのポイント

  1. 小さく始める: すべてを一度に導入しない
  2. チームの巻き込み: 開発者の理解と協力が不可欠
  3. 継続的な改善: 定期的にルールとプロセスを見直す
  4. コストの監視: リソース使用量を定期的にチェック
  5. ドキュメント化: 設定の意図と使い方を記録

8.4 さらなる学習リソース

GitLabの公式ドキュメントには、より詳細な情報と最新のベストプラクティスが記載されています。また、GitLabコミュニティでは、実際の導入事例や課題解決のディスカッションが活発に行われています。

セキュリティとレビューの自動化は、一度設定すれば終わりではなく、プロジェクトの成長とともに進化させていくものです。本記事で紹介した内容を基に、自社のプロジェクトに最適な形を見つけてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?