パイプラインでセキュリティとコンプライアンスをチェックする理由

チームが初めてCI/CDパイプラインを構築するとき、追加するチェックは通常、明白な技術的なものばかりです。コードがコンパイルできるか、ユニットテストが通るか、アプリケーションが起動できるか。これは理にかなっています。その段階では、コードが壊れることや機能が動かないことが最も顕著な問題だからです。

しかし、実際のユーザーがアプリケーションを使い始めると、新たな疑問が生じます。このコードにセキュリティホールはないか?あのライブラリに既知の脆弱性はないか?サーバーの設定は会社のポリシーに従っているか?誰かが誤ってパスワードをコミットしていないか?

ほとんどのチームはこれらの質問に手動で答えています。セキュリティチームが定期的に監査を実施し、誰かがリリース前にコンプライアンスチェックリストを記入します。この手動アプローチには3つの問題があります。

第一に、チェックはコードが完成した後、時には本番環境にデプロイされた後に行われます。問題が見つかった場合、修正には開発チームとセキュリティチームの間でやり取りが発生します。これはコストがかかり、時間もかかります。

第二に、手動チェックには一貫性がありません。同じ人でも日によって見落としが異なります。また、同じルールを異なる人が異なる解釈をすることもあります。

第三に、チームが1日に複数回デプロイする場合、手動プロセスでは追いつけません。ボトルネックがコードを書くことから、承認やチェックリストの待機に移ってしまいます。

自動化されたゲートキーパーとしてのパイプライン

これこそが、セキュリティとコンプライアンスのチェックをパイプライン内に組み込むべき理由です。考え方はシンプルです。変更がプッシュされるたびに、パイプラインが同じチェックを同じ方法で実行し、一貫した結果を提供します。

セキュリティの脆弱性があれば、コードが本番環境に到達する前にパイプラインが通知します。設定が会社のポリシーに違反していれば、問題が広がる前にパイプラインがプロセスを停止します。

これはセキュリティチームを置き換えるものではありません。明らかな問題を自動的に検出することで、セキュリティチームが人間の判断を必要とするより難しい問題に集中できるようにするものです。

速度に関する懸念

多くのチームは、セキュリティチェックを追加するとパイプラインが遅くなることを心配します。この懸念はもっともですが、問題はチェック自体ではありません。問題は、どのチェックがいつ重要かを考慮せずに、すべてのコミットですべてのチェックを実行することです。

15分かかる依存関係の脆弱性スキャンをすべてのコミットで実行すれば、開発者は確かに不満を感じるでしょう。しかし、同じ重いスキャンを1日1回、またはメインブランチへのマージ前だけ実行すれば、開発速度への影響は最小限です。

鍵は、高速なチェックと低速なチェックを分離することです。

高速なチェックは数秒で完了します。リポジトリに誤ってコミットされたシークレットのスキャンはほとんど時間がかかりません。ライブラリのライセンスが許容範囲内かどうかのチェックも同様に高速です。これらのチェックはすべてのコミットで実行すべきです。問題を早期に発見し、遅延を誰も気にしないほど軽量です。

低速なチェックは数分以上かかります。脆弱性データベースに対する依存関係のスキャンには、データのダウンロードと比較が必要です。既知のCVEに対するコンテナイメージのスキャンには、レイヤーとインストールされたパッケージの分析が含まれます。これらのチェックは価値がありますが、すべてのコードプッシュで実行する必要はありません。プルリクエストが作成されたとき、またはステージング環境にデプロイする前に実行します。

パイプラインにおけるセキュリティチェックの実際

具体的に見ていきましょう。以下は、パイプラインに組み込むべきセキュリティとコンプライアンスチェックの一般的なカテゴリです。

シークレットスキャン。 APIキー、パスワード、トークン、証明書がリポジトリにコミットされていないかを検出するツール。高速に実行でき、すべてのコミットで実行すべきです。誰かが誤って認証情報をプッシュした場合、コードが本番環境に到達する前に、すぐに知る必要があります。

依存関係スキャン。 プロジェクトの依存関係を既知の脆弱性データベースと比較するチェック。使用しているライブラリに公開されたCVEがあるかどうかを教えてくれます。プルリクエスト時と本番デプロイ前に実行します。

静的アプリケーションセキュリティテスト(SAST)。 コードを実行せずにソースコードのセキュリティパターンを分析するツール。SQLインジェクションリスク、クロスサイトスクリプティングの脆弱性、安全でない暗号化関数などを探します。SASTツールの速度はさまざまですが、適度なサイズのコードベースでは1〜2分以内に実行できるものが多いです。

コンテナイメージスキャン。 コンテナイメージをビルドする場合、ベースイメージとインストールされたパッケージの脆弱性をスキャンします。アプリケーションコードが直接管理しないOSレイヤーやランタイム依存関係の問題を検出します。

以下は、GitHub Actionsワークフローでのコンテナイメージスキャンステップの例です。

- name: Scan container image for vulnerabilities
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'my-app:${{ github.sha }}'
    format: 'table'
    exit-code: '1'
    severity: 'CRITICAL,HIGH'

このステップは、ビルドされたイメージに対してTrivyを実行し、結果をテーブル形式で出力します。CriticalまたはHighの深刻度の脆弱性が見つかった場合、ジョブは失敗します。exit-code: '1'によりパイプラインが停止し、自動化されたゲートキーパーとして機能します。

Infrastructure as Code(IaC)スキャン。 Terraform、CloudFormation、または類似のツールでインフラストラクチャを定義している場合、それらの定義の設定ミスをスキャンします。オープンなセキュリティグループ、暗号化されていないストレージ、過度に寛容なIAMポリシーなどが対象です。

ライセンスコンプライアンス。 依存関係のライセンスがプロジェクトのライセンスや会社のポリシーと互換性があるかをチェックします。これは、商用製品や外部に配布される可能性のあるプロジェクトにとって特に重要です。

実践的な導入アプローチ

これらすべてを一度に実装する必要はありません。最も差し迫ったリスクに対処するチェックから始めましょう。

チームが過去に誤ってシークレットをコミットしたことがあるなら、シークレットスキャンから始めます。脆弱なライブラリで問題が発生したことがあるなら、依存関係スキャンから始めます。インフラストラクチャチームが本番環境で設定ミスを発見したなら、IaCスキャンから始めます。

チェックを段階的に追加します。高速なチェックはすべてのコミットで実行します。低速なチェックはプルリクエスト時またはステージングデプロイ前にスケジュールします。パイプラインがチェックの失敗を通知し、開発者が何を修正すべきか明確にわかるように、失敗メッセージを明確にします。

パイプラインのクイックチェックリスト

  • シークレットスキャンはすべてのコミットで実行
  • 依存関係スキャンはプルリクエスト時と本番デプロイ前に実行
  • SASTはプルリクエスト時に実行
  • コンテナイメージスキャンはステージングデプロイ前に実行
  • Infrastructure as Codeスキャンはインフラストラクチャ変更時に実行
  • ライセンスコンプライアンスはプルリクエスト時に実行
  • 高速なチェック(数秒)はすべてのコミットで実行
  • 低速なチェック(数分)はプルリクエスト時またはステージング前に実行

真の価値

パイプラインにおけるセキュリティとコンプライアンスのチェックは、速度を低下させる余分な官僚主義ではありません。チームが自信を持ってより速く動けるようにするガードレールです。すべての変更が同じ自動チェックを通過するとき、このリリースに既知の脆弱性があるかどうか、会社のポリシーに違反していないかどうかを疑問に思う必要はありません。パイプラインがすでにそれらの質問に答えています。

チームは機能の構築とバグの修正に集中できます。基本的なセキュリティとコンプライアンスのフィルターが自動的、一貫性を持って、即座に実行されていることを知りながら。これが、何も問題が起こらないことを願うのと、明らかな問題がないことを知っていることの違いです。