log4j 2025/2026年の「設定がサイレントに無効化される」一連の公告に対する自己点検ツール。 実行時依存ゼロ、単一 jar、ネットワーク接続なし。
⚠️ まず前提を明確にしておきます。これらはすべて medium であり、RCE は一件もなく、Log4Shell のようなものではありません。 共通点は 設定が一切のエラーもなく無効化されてしまう ことです —— プロパティがサイレントに無視され、 プロパティがサイレントに改名され、ログがサイレントに破棄される。そのため、バージョン番号を見ただけでは自分が影響を受けているかどうかは分からず、設定を読む必要があります。 CVE-2021-44228(Log4Shell)をお探しの場合、このツールは役に立ちません。
java -jar log4j-check.jar app.jar # fat jar 里带配置,一步到位
java -jar log4j-check.jar ./target ./src # 构建产物 + 源码
java -jar log4j-check.jar ~/.m2/repository --no-config
Dependabot は「log4j に N 個の脆弱性がある」と教えてくれます。このツールは、それが答えない 3 つのことに答えます:
log4j2.xml に書かれている。読めるし、正確に読める。この7件のうち、6件の修正版はすべて ≤ 2.25.4。そのため大多数の人は「2.25.4 に上げる」という答えに至る。
しかし CVE-2026-49844 は 2.25.5 を要求する(2.26 系では 2.26.1)。その advisory の原文には、
自分が CVE-2026-34481 の不完全な修正であると書かれている:
バージョン
2.25.4でリリースされた修正は、影響を受けるすべてのコードパスをカバーしていませんでした。 残存する問題には CVE-2026-49844 が割り当てられました …
📌 訂正(2026-08-16)
本節にはもともと「そしてこの1件こそが Dependabot が構造的に報告できない那一条だ」と書かれていた —— その記述は今では半分しか正しくない。
GHSA-qv9r-c865-cp47は 2026-08-13 に GitHub によってreviewedに変更され、パッケージ情報が補完された。 補われた2つの範囲は>= 2.13.1, < 2.25.5と>= 2.26.0, < 2.26.1である:
- ✅ 2.x を動かしている人は、今なら Dependabot のアラートを受け取れる。
- 🔴 しかし
3.xプレビュー系(3.0.0-alpha1~3.0.0-beta2)には今も対応する範囲がなく、その系統では依然として報告されない。アップグレードの結論はまったく変わっていない ——
log4j-apiは依然として 2.25.5(2.26 系では 2.26.1)が必要。 変わったのは「機械がそれを認識できるかどうか」だけである。本ツールの判定表はバージョン系統ごとに1件ずつ更新済みで、レポートに印字される数字は表から算出され、ハードコードされていない。🔑 この出来事自体が、元のあの例よりも記録に値する:advisory の形態は時間とともに上流によって補完されるが、 あなたが前回スキャンしたときの結論は自動で更新されないし、誰も通知してくれない。 「そのときスキャンした」と「今安全である」は、別のことである。
その GitHub advisory(GHSA-qv9r-c865-cp47)は2026-08-13 より前は unreviewed であり、
かつ vulnerabilities 配列は空だった —— パッケージ名も、バージョン範囲も、修正版もなく、
その期間 Dependabot はあなたの依存ツリーと照合できるデータを一切取得できなかった。
OSV.dev 上でも当時は同様だった:他の6件は GHSA alias 経由で Maven 座標を取得できるが、それだけは取得できなかった
(OSV 内では GHSA alias すら持っていなかった)。これは3つ目のソースにおける独立した裏付けであり、
tools/recheck_before_publish.py は再実行のたびに再検証する
(このスクリプトは今では「すでに補完された」という事実自体も報告する)。
🔴 この1件を「Dependabot は使えない」と読んではいけない。 これは「そのデータが当時存在しなかった」のであって、「このツールにバグがある」のではない。
unreviewedは GitHub の正常なフロー状態であり、08-13 の補完こそがそのフローが正常に完了したことの証である。
もう1本のチェーンは CVE-2025-68161(2.25.3)→ CVE-2026-34477(2.25.4)である:
verifyHostName 属性は 2.12.0 で導入されたにもかかわらず、2.25.3 までずっと黙って無視され、
設定しても設定していないのと同じだった。68161 に従って 2.25.3 に上げ、かつ(システムプロパティではなく)属性でホスト名検証を設定している人は、依然としてやられる。
| モジュール | ヒット項目 | 上げるべき先 |
|---|---|---|
log4j-core | 34477 / 34478 / 34480 / 68161 | 2.25.4 |
log4j-api | 49844 | 2.25.5(2.26 系:2.26.1) |
log4j-1.2-api | 34479 | 2.25.4 |
log4j-layout-template-json | 34481 | 2.25.4 |
目標バージョンは同じ数字ではない。 そして大多数のプロジェクトの pom には log4j-core しか書かれていない
—— その1つの座標から逆引きすると 4件しか見つからず、残りの3件は彼らにとって存在しないも同然である。
このツールは4つのモジュールをそれぞれスキャンしてバージョンを求め、それぞれ判定する。4つのモジュールのバージョンがずれている場合(BOM による上書き、 手動での除外、古い WAR に2世代の jar が詰め込まれている場合)には、それを明示的に報告する。
| CVE | トリガー条件(公式原文より) |
|---|---|
CVE-2026-34477 | <Ssl> の verifyHostName 属性 + Socket / Syslog / SMTP appender |
CVE-2025-68161 | SocketAppender + TLS |
CVE-2026-34478 | 直接 Rfc5424Layout を設定(SyslogAppender を使う場合は影響なし) |
CVE-2026-34480 | log4j-core の XmlLayout |
CVE-2026-34479 | ブリッジの Log4j1XmlLayout、または log4j 1 互換レイヤー + org.apache.log4j.xml.XMLLayout |
CVE-2026-34481 | JsonTemplateLayout + MapMessage/ObjectMessage 内の浮動小数点値 |
CVE-2026-49844 | JsonTemplateLayout の message resolver、または MapMessage.asJson() |
実測(実際のコンポーネント、下記参照):同じセットに影響を受けるバージョンの4つのモジュールを入れた場合、
Console + RollingFile + PatternLayout)→ バージョン層で 7件 報告、真の該当 0件Socket+<Ssl verifyHostName>+Rfc5424Layout+XmlLayout がある → 7件報告、真の該当 4件2つの負の判定基準は公式原文に明記されているもので、別項目として独立させている:
SyslogAppender を使うものは 34478 に該当しない。HTTP appender のみを使い verifyHostname を使うものは 34477 に該当しない。
この項目と「見つからなかった」の信頼度はまったく異なる —— 前者には原文の裏付けがあり、後者は単に我々が見ていないだけである。
🔴
verifyHostName(大文字の N、<Ssl>、該当する)とverifyHostname(小文字の n、HTTP appender、 公式に影響なしと明記)は一文字の大文字小文字の違いしかないが、結論はまったく逆である。 同様にXmlLayout(34480、log4j-core)はLog4j1XmlLayout(34479、ブリッジ)の部分文字列である。 名前を grep するだけでは、本来負う必要のない CVE を余計に背負わせてしまう。だから設定層では構造化解析を行い、テキストマッチングはしない。
| 形式 | 読み方 | 属性の帰属を正確に判定できるか |
|---|---|---|
log4j2*.xml(strict モードの <Appender type="…"> を含む) | DOM 解析、XXE は無効化済み | ✅ |
log4j2*.properties | xxx.type = Foo に基づいてプレフィックス→プラグインのマッピングを構築 | ✅ |
log4j2*.yaml / .json | テキストマッチング(JDK には YAML パーサーがなく、本ツールはゼロ依存を貫いている) | ❌「テキスト根拠」としてマーク |
.java | テキストマッチング(コメントは除去済み、文字列を認識) | ❌ |
設定はアーカイブ内部もスキャンする(BOOT-INF/classes/、WEB-INF/classes/)、
なので Spring Boot の fat jar を1つ渡すだけでノイズ低減もできる —— ソースツリーは不要。
log4j-spring.xml / log4j2-test.xml / log4j.xml(1 互換層)のいずれも認識する。
「トリガー条件が見つからない」は安全を意味しない、少なくとも4つの状況で偽の安心になる:
ConfigurationBuilder / Configurator.initialize);log4j2.configurationFile が別の場所を指す、コンテナ内にマウントされる);「トリガー条件がすべて成立」も該当確定を意味しない:ほとんどの項目はさらに「攻撃者がログに記録される値を制御できる」ことを要求し、 この点はツールでは判定できない。したがって結論は優先順位付けにしか使えず、事故の宣言には使えない。
本ツールは log4j 1.x(log4j:log4j)をカバーしない。スキャンすると警告は出すが、判定はしない —— それは別のコードベースである。
python tools/gen_rules.py # 从两个一手源重建 CveTable.java + Triggers.java
logging.apache.org/cyclonedx/vdr.xml)——
エントリ全集 + モジュールごとの正確な区間 + 説明原文。
🔴
/repos/apache/logging-log4j2/security-advisoriesは実測で 0 件を返す —— Apache は GitHub Security Advisories を使っていない。このエンドポイントだけを調べると「log4j はとても平穏」という結果になり、しかもエラーは出ない。
17 個のアサーション、いずれか 1 つでも満たさなければ中止してファイルを書かない。そのうちいくつかは要となるもの:
| アサーション | 固定する事柄 |
|---|---|
| ASSERT2 | 二重ソースの盲点:公式 7 件 vs 単一座標 4 件 vs 四座標 6 件、3 つの数字をすべて計測すること |
| ASSERT13 | その 1 件の構造的盲点は検証可能な理由を示せなければならない、文言に決め打ちで書いてはならない |
| ASSERT7 | 各修正版を Central で実測 + 負の対照(検出器が本当に「取得できない」と言うことを証明) |
| ASSERT8 | 修正版は区間の上限から導出した → 公式 recommendation の原文に逐語で現れなければならない |
| ASSERT9 | 2 本のパッチギャップ連鎖の原文の言い回し + 後段の修正版は本当に上位でなければならない |
| ASSERT10 | 最も一般的な修正版にアップグレードしてもなお漏れが残る —— さもなければ「積集合を取る」ことに価値はない |
| ASSERT14 | XmlLayout≠Log4j1XmlLayout、verifyHostName≠verifyHostname、正例・負例をそれぞれ 1 回ずつテスト |
| ASSERT17 | 説明原文は完全な文でなければならず、asciidoc の強制改行で折られた残句であってはならない |
tools/recheck_before_publish.py):
生成スクリプトの出力を一切読まず、3 つのソース(OSV.dev / 公式セキュリティページ / Maven Central)に切り替えて再計算実物の成果物での再検証で、単体テストでは検出できない問題を 3 件発見し、すべて修正 + 回帰テストを追加した:
log4j-api jar に同梱されている Log4j-charsets.properties が、ユーザーの log4j2 設定とみなされていた →
「今回は設定が見つからなかった」が「確認したが見つからなかった」と訳され、しかも classpath 上に log4j-api がある限り、
この誤訳は起き続ける。