Node.jsとPythonプロジェクトのソースコードとライブラリの脆弱性スキャンにSnykを使う方法(SCA)

Security tutorial - IT technology blog
Security tutorial - IT technology blog

以前、真夜中にSSHへのブルートフォース攻撃を受けたことがあります。それ以来、本番環境に上げてから考えるのではなく、プロジェクトの最初からセキュリティを設定するようにしています。見落とされがちなのがサードパーティライブラリの脆弱性です。何も確認せずにnpm installpip installを実行してしまいがちですよね。

そこで登場するのがSnykです。このSCA(Software Composition Analysis)ツールはpackage.jsonrequirements.txtPipfileなどを読み込み、独自のCVEデータベースと照合して既知の脆弱性を検出し、パッチ済みバージョンを提案してくれます。Snyk Vulnerability DBは、NVDより数日から数週間早くCVEを更新することが多く、エクスプロイトが実際に悪用されている場合には特に重要です。そして、CI/CDに直接統合できるため、手作業は不要です。

5分ですぐに試してみる

始めるためにアカウントは不要です。Snyk CLIをインストールして、現在のプロジェクトをスキャンしてみましょう。

# npmでグローバルインストール
npm install -g snyk

# グローバルインストールしたくない場合はnpxを使う
npx snyk --version

そのままプロジェクトディレクトリで実行します。

# Node.jsの場合
cd my-node-project
snyk test

# Pythonの場合
cd my-python-project
snyk test --file=requirements.txt

初回実行時は認証が必要です。

snyk auth

ブラウザが自動で開いてログインできます。無料プランではオープンソースプロジェクトを無制限にスキャンできます。認証が完了したらsnyk testを再実行すると、すぐに結果が表示されます。

Testing /home/user/my-node-project...

Found 3 vulnerabilities in 2 paths

  ✗ High severity vulnerability found in lodash
    Description: Prototype Pollution
    Info: https://snyk.io/vuln/SNYK-JS-LODASH-567746
    Introduced through: [email protected]
    Fix: upgrade to [email protected]

  ✗ Medium severity vulnerability found in axios
    ...

Snykはどのように動作するのか

SnykはSASTではなく、コードのロジックを読むわけではありません。代わりにロックファイルまたはマニフェストファイルを読み込み、推移的依存関係を含む依存関係ツリー全体を構築して、各パッケージをCVEデータベースと照合します。

対応ファイル

  • Node.js: package.jsonpackage-lock.jsonyarn.lock
  • Python: requirements.txtPipfilePipfile.lockpyproject.tomlpoetry.lock
  • その他: Go modules、Maven、Gradle、Composer、Ruby Gems、.NETなど

Pythonではrequirements.txtよりPipfile.lockを使うことが多いです。間接的に引き込まれるパッケージも含め、依存関係ツリーをより完全に把握できるためです。

# Pipfile.lockでスキャン(より正確)
snyk test --file=Pipfile.lock

# Snykに自動検出させる場合
snyk test

スキャン結果の読み方

Snykが報告する各脆弱性には以下の情報が含まれます。

  • Severity: Critical / High / Medium / Low
  • CVE IDと詳細リンク
  • Introduced through: どのパッケージがこの脆弱性を引き込んだか
  • Fix: どのバージョンにアップグレードすれば修正されるか

Introduced throughの情報が最も役立つと感じています。以前のプロジェクトでは、webpackloader-utilslodashという依存チェーンで[email protected]が引き込まれていることを突き止めるのに1時間近くかかりました。Snykなら数秒でトレースして、アップグレードの提案まで出してくれます。

CI/CDへの統合

手動実行はクイックデバッグには便利ですが、Snykの真価はパイプラインに組み込むことにあります。誰かが覚えていなくても、すべてのPRが自動でスキャンされるようになります。

GitHub Actions

# .github/workflows/security.yml
name: Snyk Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master   # またはpython@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

SNYK_TOKENをGitHub Secretsに追加します(snyk authまたはSnykダッシュボードから取得)。--severity-threshold=highは重要なオプションです。設定しないと、修正方法のないLow/Mediumの脆弱性が数十件あるだけでパイプラインが失敗し、チームが1週間以内にこのチェックを無効化してしまいます。

GitLab CI

# .gitlab-ci.yml
snyk_scan:
  image: node:18
  stage: test
  script:
    - npm install -g snyk
    - snyk auth $SNYK_TOKEN
    - snyk test --severity-threshold=high
  allow_failure: false
  only:
    - merge_requests
    - main

CI環境でのPythonプロジェクト

# PythonのGitHub Actions設定
- name: Set up Python
  uses: actions/setup-python@v4
  with:
    python-version: '3.11'

- name: Install dependencies
  run: pip install -r requirements.txt

- name: Run Snyk
  uses: snyk/actions/python@master
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  with:
    args: --file=requirements.txt --severity-threshold=high

応用編:snyk monitorと継続的な監視

snyk testは実行した時点のスキャンのみ行います。一方snyk monitorは異なります。依存関係ツリーのスナップショットをSnykクラウドに保存し、継続的に監視します。デプロイ後に新しいCVEが発見された場合、再実行しなくてもSnykがメールやSlackで自動的に通知してくれます。

# デプロイ後またはCI/CD成功後に実行
snyk monitor --project-name="my-app-production"

私のパイプラインでは、snyk testはPRチェック時に実行して新しい脆弱性があるときマージをブロックし、snyk monitorはデプロイ後の監視のためにdeploy-to-productionステップの最後に実行しています。この2つは補完関係にあり、どちらかが不要ということはありません。

snyk fixによる自動修正

# 自動修正を試みる(マイナー/パッチのアップグレードのみ)
snyk fix

# 適用前に差分を確認する
snyk fix --dry-run

Node.jsではこの機能はかなり使えます。Snykがpackage.jsonを自動修正してnpm installを実行し、互換性を保ちながら安全なバージョンに更新してくれます。Pythonはまだ実験的な機能なので、慎重に使いましょう。

意図的な脆弱性の無視

CVEが自分のユースケースに適用されない場合があります。たとえば、まったく使っていない機能に関連する脆弱性などです。パイプラインが永遠に失敗し続けるのを避けるために:

# 特定の脆弱性を30日間無視する
snyk ignore --id=SNYK-JS-LODASH-567746 --reason="Not exploitable in our use case" --expiry=2026-05-01

このコマンドはプロジェクトに.snykファイルを作成します。このファイルをgitにコミットしましょう。3ヶ月後に「なぜこのCVEが無視されているのか」と聞かれたとき、Slackの履歴を掘り起こさなくても理由が分かります。

実際のプロジェクトから得た実践的なTips

1. 最初からSnykで全部ブロックしようとしない。古いコードベースには数十から数百の脆弱性があることが多く、Snykを組み込んでパイプラインをすぐに失敗させると、チームがこのツールを嫌いになる最速の方法です。まず--severity-threshold=criticalで始め、数スプリント後にhighに下げ、それからmediumに進みましょう。

2. npm audit/pip-auditと組み合わせる。各ツールは独自のデータベースを持っており、一方が検出して他方が見落とす場合があります。Pythonでは両方実行することが多いです。

# pip-audit — PyPAのツール、PyPI Advisoryデータベースを使用
pip install pip-audit
pip-audit -r requirements.txt

# Snykと比較する
snyk test --file=requirements.txt

3. Dockerイメージもスキャンする。コンテナ内のOSパッケージも攻撃対象になりますが、依存関係以上に見落とされがちです。Snykはこちらもスキャンできます。

# ビルド済みイメージをスキャン
snyk container test my-app:latest

# 使用前にレジストリのイメージをスキャン
snyk container test python:3.11-slim

ベースイメージを選ぶ前にこれを実行することが多いです。python:3.11-slimはプリインストールされているパッケージが少ないため、python:3.11よりOSの脆弱性が数十件少ないのが一般的です。Dockerfileにコミットする前に確認する価値があります。

4. 無料プランは小規模チームには十分。Snykの無料プランではオープンソースプロジェクトを無制限にスキャンでき、プライベートリポジトリは月200テストまで可能です。小規模チームや個人プロジェクトには十分です。有料プランは主にSAST、ライセンスコンプライアンス、複数チームの管理機能が追加されます。

5. 今日すぐベースラインを作る。CI/CDへの統合がまだでも大丈夫です。本番コードベースでsnyk monitorを一度実行するだけで完了します。新しいCVEが出てきたときにSnykが通知してくれるので、追加作業は不要です。

ファイアウォール、SSHキー、2FAは誰もが気にします。でも依存関係はそうではありません。しかし、最近のサプライチェーン攻撃の多くはここが侵入口になっています。Snykのセットアップにかかる時間は約10分。放置した場合のリスクは、それよりずっと計り知れません。

Share: