本文へスキップ
公開済みリソース消費の制御不備 / サービス拒否

CVE-2026-73635: Apache StrutsのリクエストLocaleによるキャッシュ枯渇

固定Localeが設定されていないApache Strutsで、リクエスト由来のLocale値によりlocalized-text cacheが際限なく増加し、Java heapを枯渇させる問題です。

公開

アドバイザリ情報
製品
Apache Struts
コンポーネント
Localized-text cacheとリクエストLocale処理
脆弱性クラス
リソース消費の制御不備 / サービス拒否
信頼境界
未認証リクエストのLocaleが、プロセス全体で使うlocalized-text cacheのキーへ影響する境界。
攻撃の前提条件
固定Localeを設定していない、ネットワークから到達可能なStrutsアプリケーション。
影響
キャッシュの無制限な増加、Java heapの枯渇、他利用者に対するサービス拒否。
根本原因
リクエスト由来のLocaleをlocalized-text lookupに利用する一方、対応するキャッシュのサイズが制限されていなかった。
検証バージョン
Apache S2-074に記載された影響範囲
影響を受けるバージョン
2.0.0–2.3.37, 2.5.0–2.5.33, 6.0.0–6.10.0, 7.0.0–7.2.1
修正バージョン
6.11.0, 7.3.0
開示ステータス
Apache Software Foundationが公開
CVE
CVE-2026-73635
JVN
JVN#08517956
クレジット
Kuniyoshi Noguchi (野口 晋義), (@KuniNogu)

TL;DR

Apache Struts S2-074は、 Localized-text処理に存在するサービス拒否の脆弱性です。固定Localeを設定していない Applicationでは、Strutsが受信RequestのLocaleを型変換・Validation error等の Message lookupへ使用していました。対応するCacheは無制限に増加できたため、未認証の Remote clientがRequest localeを変化させ、Java heapを枯渇させて他利用者へのServiceを 停止させることが可能でした。

影響範囲は2.0.0–2.3.37、2.5.0–2.5.33、6.0.0–6.10.0、7.0.0–7.2.1です。 少なくとも6.11.0または7.3.0へ更新してください。Apacheが案内するWorkaroundは、 struts.localeを対応する固定値に設定することです。

JVN#08517956ではCVSS v4.0 8.7が 付与され、公開Reporterクレジットも確認できます。

検知で重要なのはRequest rateだけではありません。Cache entryを作るRequest由来値の カーディナリティを、heap増加やGC負荷と関連付ける必要があります。

調査理由 / なぜ重要か

この問題は、非対称なResource消費の分かりやすい例です。Request自体は小さく正常に 見えても、Serverに新しいCache entryを保持させられます。異なる値を繰り返すことで、 一般的なVolume型DoSの閾値を下回るTrafficでもProcess全体の状態を増加させられます。

同じ設計Riskは、Tenant ID、Model名、Tool ID、Callback ID、Header値、利用者が選ぶ LocaleをKeyにするCacheにも存在します。攻撃者が影響できるKey空間には、意味的な ValidationとResourceのHard limitが両方必要です。AuthenticationはMemory管理の制御に ならず、S2-074は影響を受ける設定では未認証で到達可能です。

Trust Boundary

関係するデータフローは次のとおりです。

remote request
  -> servlet request locale
  -> Struts localized-text lookup
  -> process-wide cache key / entry
  -> retained Java heap

struts.localeが固定されていない場合、Request localeはRemoteから制御可能な Request単位のContextから、Process全体で保持される状態へ渡ります。Trust Boundaryは 単なるHTTP input parsingではなく、一時的な入力がRequest終了後もResource costを持つ Cacheへ変換される箇所です。

Apacheは、固定Localeを設定したApplicationはLocalized-text lookupにRequest由来Localeを 使用しないため影響を受けないと説明しています。一般的なServlet stackではRequest localeはAccept-Language等のLocale negotiationから解決されますが、防御側は特定の Proxy・Container動作を仮定せず、Applicationが実際に利用したLocaleを記録すべきです。

根本原因

二つの通常機能が、安全でない形で組み合わされていました。

  1. 固定Localeがない場合に、StrutsがRequest由来Localeを選択する。
  2. Localized-textの結果を、Size上限のない内部Cacheへ保持する。

未認証ClientがProcess全体のCache keyの種類数を増やせることにより、脆弱性になりました。 正当なLocale数、設定された最大数、有限容量のEviction policyによってCache growthが 制約されていませんでした。

これはResource lifetimeのBugです。一つのLocale値をParseできるかだけでなく、時間経過 とともに攻撃者が何種類の値をProcessへ保持させられるかを検証する必要があります。

Safe reproducer

共有環境やProductionに対してheap枯渇を試してはいけません。JVMを停止させずに、次の 安全なValidationで必要なSecurity propertyを確認できます。

  1. 隔離Networkに使い捨てのStruts Applicationを構築し、意図的に小さいJVM heapと Test timeoutを設定する。
  2. 通常のLocalized-text lookupを行うRouteを一つ用意する。人工的なMemory allocation loopは追加しない。
  3. Struts version、struts.locale設定、実効Request locale、heap使用量、GC activity、 Response statusを記録する。
  4. 少数に固定したSynthetic locale corpusを低いRateで送信する。Corpusと総Request数は、 承認済みLab budget未満に制限する。
  5. 6.11.0または7.3.0でMemoryが上限内に保たれ、Applicationが応答を続けることを確認 する。Cache metricsを計装できる場合は、Entry数が設定上限を超えないことも確認する。
  6. 固定struts.localeでも繰り返し、Request localeの変更がLookupに使うLocaleを変えない ことを確認する。

非破壊的なTest harnessの制御ロジックは次のように表せます。

for locale in FINITE_SYNTHETIC_LOCALE_CORPUS:
    send_one_request(locale)
    stop_if(heap_delta > LAB_SAFETY_BUDGET or service_unhealthy)
 
assert service_healthy
assert fixed_release_cache_is_bounded

短時間のTestでheap増加が見えないことを、旧Versionが安全である証拠にしてはいけません。 Versionと設定の棚卸しが、Exposure判定の正本です。有限Corpusは修正とTelemetryの検証用で あり、Service停止を再現するためのものではありません。

修正差分の評価

Apacheは修正版における二つの補完的な制御を説明しています。

  • Localized-text cacheを有限化し、struts.i18n.cacheMaxSizeで最大Sizeを設定可能にする。
  • struts.locale.validateRequestLocaleにより、Request由来LocaleをRuntimeで利用可能な Locale集合へ制限可能にする。

Apacheによると、後者のRequest locale制限はBackward compatibilityのためDefaultで無効 です。したがって、Validationを有効にしなくても成立しなければならないResource safety 制御は、Cacheの有限化です。Cacheが上限へ達するとEntryは透過的にEvictionされます。 同じ修正が7.3.0と6.x Maintenance releaseの6.11.0に含まれます。

struts.localeを対応する固定値にするWorkaroundは、攻撃者が制御するLocaleをLookupから 除外します。即時のRisk低減には有効ですが、Support終了済み2.x branchを更新せず使い 続ける根拠にはなりません。

Fix reviewでは、新しい設定Keyが存在するだけでなく、Concurrency下でもMemoryが有限で あることを確認します。公開Bulletinが説明するのはBehavior changeであり、本記事は 非公開の行単位Patch historyやPrivate test resultを断定しません。

回帰テスト

テスト期待結果
固定struts.localeへ異なるRequest localeを送る常に設定済みLocaleを利用
Validation無効で有限の多数Variantを送るCacheが設定上限内に留まる
struts.locale.validateRequestLocale有効時に利用不能Localeを送る安全にFallbackし、無制限な新規Keyを作らない
Validation有効時に利用可能Localeを送る正しいLocalized messageを返す
Cacheが設定最大数へ到達Evictionし、Serviceは正常を維持
Eviction済みLocaleを再要求Staleな言語混在なく正しく再計算
有限の多数VariantをConcurrency下で送るHeap、Latency、GCがLab budget内
型変換・Validation errorがLookupを起動どちらの経路も有限Cacheを利用

Fallback languageとCache evictionの正しさも確認します。Memoryを直しても、別利用者の Localeを返す、古いMessageを保持する、Concurrency raceを作る場合は完全な修正では ありません。

Detection

高カーディナリティなLocale入力

実効LocaleをApplicationまたは信頼できるEdgeで記録します。次のSPLは、その値が request_localeへ正規化されている前提です。5分間に20種類という閾値は初期仮説に すぎません。正規TrafficとNATの特性に合わせて調整してください。

index=YOUR_WEB_INDEX request_locale=*
| bin _time span=5m
| stats count as requests dc(request_locale) as distinct_locales
        values(user_agent) as user_agents
  by _time src_ip
| where distinct_locales > 20
| sort - distinct_locales

Service全体のLocale種類数も計算します。攻撃者がSource addressを分散する場合や、Reverse proxyにより多数Clientが同じAddressに見える場合があります。法令とPolicy上取得できる 範囲で、Session ID、TLS client fingerprint、User agent、Route、Edge request IDもPivotに 利用します。

JVMとの相関

Locale cardinalityの増加を次と相関します。

  • Baselineへ戻らないOld generationまたは総heap占有率。
  • Full GC回数・Pause timeの増加。
  • AllocationまたはPromotion pressure。
  • OutOfMemoryError、Health check失敗、Container restart、Process exit。
  • Localized-text lookupを起動するRequestのLatency増加。

Request volumeだけでは弱いSignalです。ほぼすべてのRequestが新しい保持Keyを作る場合、 低いRequest rateでも問題になります。

Incident Response

  1. 代表的なRequest evidenceを保全しながら、信頼できるEdgeで異常なLocale patternを Rate limitまたはFilterする。
  2. 全Instanceの正確なStruts versionとstruts.locale設定を記録する。Fleet全体が同じ という前提を置かない。
  3. Access log、実効Locale telemetry、GC log、JVM/Container metrics、Restart history、 Deployment metadataを保全する。
  4. 6.11.0または7.3.0へ更新する。直ちに更新できない場合は、Apacheの案内どおり対応 する固定struts.localeを設定する。
  5. Evidence収集後に影響JVMを計画的にRestartして保持状態を解消し、修正済みまたは緩和 済みInstanceだけがTrafficを処理することを確認する。
  6. Memory上限のRegression testを実行し、復旧後のheapを監視する。

Heap dumpにはCredentialや利用者Dataが含まれ、取得自体が運用負荷になる場合があります。 組織のForensics・Privacy policyに従い、DiskとMemoryに十分な余裕がある場合だけ取得します。

Disclosure Timeline

  • 報告日: S2-074には公開されていません。
  • 2026-08-13: 公開S2-074 pageにLast updatedとして記録され、Reporterとして Kuniyoshi Noguchi(野口 晋義), @KuniNoguを掲載。
  • 2026-08-25: JVNがJVN#08517956をCVSS v4.0 8.7および公開Reporter クレジットとともに公開。
  • 修正版: S2-074は6.11.0と7.3.0を最低修正版として案内。本記事は非公開のPatch日・ Validation日を推測しません。

Commit historyから非公開のTriage・調整日を再構成していません。

一次情報