対策・学習12分で読める

脆弱性(CVE)対応の実務:直しきって、再発を監視し続ける手順

依存ライブラリの脆弱性(CVE)を直しきるための実務手順です。スキャン・修正・隔離・監視の「完了の4定義」を軸に、アラート疲れの防ぎ方や修正をデプロイで消さない運用を解説します。

本文を読む
公開日
更新日
目次

対象:少人数で複数のWebアプリ(フレームワーク混在)を運用していて、依存ライブラリの脆弱性(CVE)対応を「一度ちゃんと直しきりたい」人。ここでは攻撃手順は扱わず、直して・消えないようにして・監視し続けるまでの実務だけを扱います。きっかけになりがちな事故の例は 公開済みRCEを放置して不正課金された話 を参照。

当サイトの視点:小さな現場ほど『仕組み2つ』で守れる

人手が少ない現場で効くのは、派手なツールより2つの規律です。①自動の変化検知(増えた脆弱性だけ通知する)②local→push→deploy(本番は受け取るだけ、編集しない)。当サイトも同じ考えで、依存監査(pnpm audit)を毎デプロイ前+日次cronで回し、本番へは push-to-deploy で配るだけにしています。1回直して終わりにせず、通知する仕組みを置いて続ける運用にするのが、結局いちばん安く長続きします。

対応完了の4つの条件を先に決める

中途半端に「終わった」と言わないための歯止めです。この4つが揃うまでは未完、と先に決めておきます。

① スキャン

現状を数字で把握する

② 修正

非devの重大/高を修正する

③ 隔離/引き継ぎ

直せない・孤立は明示的に

④ 監視

日次の変化検知を載せる

脆弱性対応の完了=4点セット。1つでも欠けると、直したつもりでも脆弱性が残る。

① スキャン:現状を件数で把握する

まず、どこに何があるかを機械で把握します。手動の不定期チェックは必ず途切れます。

1

ロックファイルを自動探索してスキャン

composer.lock / package-lock.json / pnpm-lock.yaml を探し、OSSのスキャナ(osv-scanner や pnpm audit)に毎日かける。ロックファイルを読むだけなので負荷はごく軽い。
2

重大度は『1つの数字』ではないと知る

同じ脆弱性が、CVSSでは高(例7.5)でもベンダー精査では中、ということは普通にある。CVSSは機械算出で過大評価しがち。どの基準で閾値を引くかを決め、チーム全員が『今どちらを使っているか』を共有する(→ CVSSとは)。実際に悪用されているか(KEV)と掛け合わせて優先度を出す。
3

ノイズは『無視』でなく『根拠を持って除外』

テスト/ビルド専用のdev依存は本番バンドルに載らないことが多く、実害が無い場合がある。スキャナのグループ判定でdevを通知から外しつつ、なぜ除外したかを台帳に残す。後で「これ直ってないけど?」に説明できる状態にしておく。

② 修正:脆弱なバージョンそのものを更新する

症状を止める応急処置と、原因の修正は別の作業です。両方やって初めて終わりです。

応急処置だけ(原因はそのまま)

  • リバースプロキシで“それっぽい”リクエストだけ弾く
  • 症状が止まったので「対応完了」と考える
  • 脆弱な依存はそのまま=RCE等が悪用できる状態のまま

原因の修正(正しい)

  • 脆弱な依存を修正版へ更新して脆弱性そのものをなくす
  • 更新後、兆候が消えたかをログで確認
  • 応急処置と原因の修正を別作業として両方やる

メジャー跨ぎは『本番に出す前にビルド検証』

パッチ当てと違い、メジャー版跨ぎ(フレームワーク14→15、UIツール4→6 等)は破壊的変更を伴う。確認せずに上げて本番ビルドが失敗すると、サービスが止まる。本番と同一ランタイム(同じNode/PHP版のコンテナ)に編集後ソースを置き、ビルド/型チェックを完全に通してからpush する。出たエラーは1つずつ直す(同期→非同期化のcodemod、CSSの複数行class、型名前空間の廃止 等)。旧コンテナが動き続ける構成なら、失敗時も無停止で切り戻せる。

修正が次のデプロイで消えないことまで確認する

中身が100点でも、次のデプロイで消える運用なら0点です。ここが一番起きやすい失敗です。

1

本番の作業ツリーで直接commitしない

本番で直接commitすると中央リポと履歴が分岐し、次のデプロイ(pull / checkout -f)で上書きされて修正が消える。本番は『デプロイ結果を受け取る場所』であって編集する場所ではない。
2

必ず local→push→deploy の一方向に統一

編集はローカル→commit→push→(本番は)pull/自動デプロイ。やむを得ず本番で確認した変更も、必ずローカルへ巻き戻して push し直す(→ 本番は受け取るだけにする話)。
3

HTTP 200を『正常』と信じない

共用ホスティング等では、致命的エラーでも200が返ることがある。検証は必ず実コンテンツで行う。本文に期待する見出し/テキストが描画されているか(curl | grep)、エラーログに新規が出ていないかを見て、DB依存やパラメータ付きの動的ルートまで確認する。キャッシュで反映が遅れることもあるので時間をおくかクリア後に。

③ 隔離/引き継ぎ:直せないものを明記する

全部を今すぐ直せるとは限りません。直せない・別管轄・もう使っていない、を曖昧にしないのがコツです。

1

孤立・EOLコードは『消す前に隔離』

もう配信していない旧ソース(EOLフレームワーク等)は、いきなり削除せずリネームで隔離し、「いつ・なぜ隔離したか・元の配信先」を書いたマーカーを残す。スキャン対象からも外す。『消す』は不可逆だが『隔離』は戻せて経緯も残る。
2

本当に未参照かを確定してから

リバースプロキシ設定・ビルド構成・マウントを追い、どこからも参照されていないことを確認してから隔離する。誤って再配信される事故を防ぐ。
3

直せない/別管轄は明示ハンドオフ

自分で直せないものは、放置でなく『誰に・何を』を明示して引き継ぐ。未対応を『見えない放置』にしないことが、棚卸しの価値。

④ 監視:日次で変化を検知する仕組みを入れる

ここまで来て、最後に通知する仕組みを置きます。これが無いと、せっかく直しても再発に気づけません。

『全部毎日鳴らす』でなく『増えたら鳴らす』

スキャン結果を前回との差分(state file)で比較し、新規の重大/高が出た時だけ通知する。毎日同じ内容が届くとすぐ無視される(アラート疲れ)。通知はメール1通に要約(新規・解消・現状)し、cronで日次。共用サーバーでもスキャナ1バイナリ+cronで十分回る(負荷は数ミリ秒〜・niceを噛ませれば安心)。

差分
増えた脆弱性だけ通知=疲れない
日次
cronで回し続ける
1通
新規・解消・現状を要約
④まで
監視を載せて初めて完了

棚卸しで見つかるAPIキーやSSH鍵

CVEを直しきる過程では、依存以外の問題もよく一緒に見つかります。代表は2つです。公開ディレクトリに置き忘れた秘密ファイル(webrootに古いトークンが放置、共通テンプレ由来なら全台に同じ問題)と、侵害され得る一時環境に渡したroot鍵。どちらも「1か所漏れたら全部」を生む典型なので、CVE対応と同じ棚卸しのタイミングで点検します(この2つはそれぞれ単独で深掘りする価値があります)。秘密の基本は 秘密の安全な保管 と 最低限チェックリスト を参照。

次に読む

よくある質問

Q脆弱性対応はどこまでやれば『完了』ですか?
A

4点が揃って初めて完了です。①スキャンで現状を数字にした②非devの重大/高を修正した③直せない・別管轄・孤立コードは明示的に隔離/引き継ぎした④日次の変化検知(再発・新規をキャッチする監視)を載せた。特に④を載せるまでは完了ではありません。依存は明日また脆弱になるからです。

Q毎日スキャンするとアラートに疲れませんか?
A

『全部を毎日鳴らす』とすぐ無視されます。前回結果との差分を取り、新規の重大/高が出た時だけ通知する『変化検知』にします。毎日同じ内容を送らないことが、結局いちばん長続きする設計です。

Q修正したのに、また脆弱に戻ることがあるのはなぜ?
A

本番の作業ツリーで直接commitすると、次のデプロイ(pull や checkout -f)で上書きされて修正が消えます。必ず local→push→deploy の一方向に統一し、本番は『受け取るだけ』にします。修正の中身が完璧でも、消える運用なら成果はゼロです。