python-cryptography 一日三發:從萬用字元 DNS 繞過到 Bleichenbacher oracle
GitHub Advisory Database · 2026-07-31
2026 年 7 月 31 日,GitHub Advisory Database 同一天公告了三則 python-cryptography(pyca/cryptography)的通報:GHSA-m2h6-j472-rp4c(CVE-2026-69248,萬用字元繞過名稱限制)、GHSA-jwv3-5hgf-82ww(重複自簽中繼憑證造成路徑建構阻斷服務)、GHSA-g6cj-pr64-35w5(CVE-2026-69247,PKCS#7 解密的 Bleichenbacher oracle)。三者互不相依,卻同時觸及這套函式庫在憑證鏈驗證與訊息解密上的邊界處理。其中影響最深、機制也最經典的是最後一則——一個針對 RSA PKCS#1 v1.5 的自適應選擇密文攻擊(adaptive chosen-ciphertext attack)在 2026 年的重現。
漏洞機制:Bleichenbacher oracle 如何運作
Bleichenbacher 攻擊由 Daniel Bleichenbacher 於 1998 年提出,鎖定 RSA PKCS#1 v1.5 填充格式:合法明文必須以 0x00 0x02 開頭,若伺服器在解密後會依「填充格式是否合法」回傳不同結果(無論是錯誤訊息、狀態碼或回應時間),攻擊者就能把伺服器當作一個「填充是否正確」的問答機(oracle)。攻擊者不需要私鑰,只需要反覆送出經過精心構造的密文,並根據 oracle 的是/否回應,用數論方法逐步縮小明文的可能範圍,最終在數萬次查詢內完整還原明文或偽造簽章。這正是 GHSA-g6cj-pr64-35w5 的核心:pkcs7_decrypt_der、pkcs7_decrypt_pem、pkcs7_decrypt_smime 三個函式在解密 PKCS#7 EnvelopedData 的 RecipientInfo.encryptedKey(本質是以 RSA PKCS#1 v1.5 加密的內容加密金鑰 CEK)時,會依失敗階段回傳不同訊息。
具體而言,通報列出四種可被外部觀察到的結果:RSA 填充不合法時回傳「Decryption failed」;填充合法但還原出的金鑰長度不對時回傳「Invalid key size (N) for AES」,其中 N 就是從 RSA 運算還原出的確切長度;長度正確但金鑰內容錯誤時回傳「Invalid padding bytes」;全部正確才吐出明文。這四種可區分的錯誤等於把 Bleichenbacher 攻擊需要的 oracle 白白送給攻擊者,只要應用程式會對不受信任的 EnvelopedData 自動解密並把結果(哪怕只是錯誤類型)回報出去,攻擊者就能重建原本的攻擊流程,逐步逼近 CEK 的真實值。
另兩則通報機制不同但同樣涉及信任邊界。GHSA-m2h6-j472-rp4c 指出,當中繼 CA 被限制只能簽發如 foo.example.com 的 DNS 名稱(透過 permittedSubtrees 名稱限制),若葉節點憑證的 DNS SAN 帶有萬用字元 *.example.com,python-cryptography 的驗證器仍會接受它——等於讓萬用字元繞過了 RFC 5280 定義的名稱限制邊界,形成一種「sub-CA scope escape」。換言之,一個原本被限縮在單一子網域的中繼 CA,能藉由萬用字元憑證取得簽發整個上層網域憑證的能力。GHSA-jwv3-5hgf-82ww 則是另一種資源耗用問題:當憑證鏈中出現重複的自簽中繼憑證時,路徑建構演算法的複雜度會隨之指數成長,讓驗證單一憑證鏈的計算成本失控,形成阻斷服務。
受影響版本
| CVE / GHSA | 嚴重度 | 受影響版本 | 修補版本 |
|---|---|---|---|
CVE-2026-69247 / GHSA-g6cj-pr64-35w5 | 8.2 High | >=44.0.0, <50.0.0 | 50.0.0 |
CVE-2026-69248 / GHSA-m2h6-j472-rp4c | 6.9 Moderate | <=48.0.0 | 49.0.0 |
GHSA-jwv3-5hgf-82ww | 本文未核實 CVSS 數字 | 詳見該通報頁面 | 隨主要版本一併修補 |
三則通報影響的版本區間彼此交錯但不完全重疊:Bleichenbacher oracle 只影響 44.0.0 到 50.0.0 之前的版本,萬用字元繞過則涵蓋所有 48.0.0 及更早版本。這意味著同一份 pyca/cryptography 安裝,很可能同時落在兩個受影響範圍之內,升級時無法只挑其中一個修補版本了事。
修補與緩解
GHSA-g6cj-pr64-35w5 的修補方式依循 RFC 3218 的建議:在使用私鑰之前先決定好內容加密演算法,讓所有失敗路徑回傳一致的結果,消除可觀察的差異。GHSA-m2h6-j472-rp4c 的修補則是收緊萬用字元與名稱限制的比對邏輯,不再允許萬用字元跳脫 permittedSubtrees 的範圍。對於仍在處理不受信任 PKCS#7 訊息、或依賴中繼 CA 名稱限制做隔離的系統,升級到 50.0.0(或至少 49.0.0)是唯一可靠的緩解方式,單純調整錯誤訊息文字或關閉細節記錄都無法從根本消除 oracle。
GHSA-m2h6-j472-rp4c(CVE-2026-69248):萬用字元 DNS 名稱繞過permittedSubtrees限制GHSA-jwv3-5hgf-82ww:重複自簽中繼憑證觸發路徑建構指數複雜度,形成阻斷服務GHSA-g6cj-pr64-35w5(CVE-2026-69247):PKCS#7 EnvelopedData 解密的錯誤訊息差異構成 Bleichenbacher oracle
原始來源:GHSA-m2h6-j472-rp4c、GHSA-jwv3-5hgf-82ww、GHSA-g6cj-pr64-35w5
Apache NiFi 一日四彈:Parameter Context 授權模型的連環破口
oss-security mailing list · 2026-08-03
2026 年 8 月 3 日,oss-security 郵件論壇同一天公告了四則 Apache NiFi 的 CVE。一則是 HTTP 請求解壓縮造成的資源耗用型阻斷服務(CVE-2026-68981),另外三則(CVE-2026-68980、CVE-2026-62354、CVE-2026-68979)全部圍繞著 NiFi 的「Parameter Context」授權模型。三則授權缺陷分別出現在資產刪除、驗證請求與參數更新三個不同的 REST API 端點,彼此獨立卻共享同一種結構性弱點。
什麼是 Parameter Context
NiFi 的資料流由一連串 Processor(處理器)與 Controller Service 組成,設定值原本容易被寫死在流程裡,例如資料庫連線字串、API 端點或密碼。Parameter Context 是 NiFi 用來集中管理這類設定值的機制:使用者先在 Parameter Context 裡定義具名參數,再讓 Process Group 內的元件以 #{parameterName} 的語法引用它,讓同一份流程定義能在開發、測試、正式環境之間切換參數而不必修改流程本身。Parameter Context 也可以掛接外部來源(資料庫、HashiCorp Vault、雲端 Secrets Manager 等)自動抓取參數值。
關鍵在於,Parameter Context 本身擁有獨立的存取政策,決定誰能檢視或修改這組參數;而實際「使用」這些參數的 Processor、Controller Service 則有各自的元件層級授權政策,決定誰能啟動、停止或設定它們。這兩層授權原本應該被一致地檢查——修改參數形同間接修改了所有引用它的元件——但 NiFi 的多個 REST API 端點各自實作了驗證邏輯,而不是共用同一套授權中介層,於是同樣形狀的漏洞在不同端點各自發生了一次。
漏洞機制:三個端點、同一種破口
CVE-2026-68980(Low)出現在資產刪除端點:框架僅依據請求中「宣稱」的 Parameter Context Identifier 來檢查授權,卻沒有驗證該 Asset 實際上是否真的屬於這個 Context。擁有某個 Parameter Context 寫入權限的使用者,可以藉此刪除其實屬於另一個 Context 的資產,只要伺服端沒有對不同 Context 實施差異化授權,就不受影響。
CVE-2026-62354(High,由 MBBank 的 Nguyen Van Hiep 回報)出現在驗證請求端點:只要有讀取權限,客戶端就能送出「提議」的參數值,讓伺服端以這組替代設定去呼叫元件既有的驗證方法。這讓原本只該檢視設定的唯讀使用者,也能間接觸發並觀察元件在不同設定組合下的驗證行為,修補後改為要求寫入權限才能送出驗證請求。
CVE-2026-68979(Medium)影響最複雜:更新 Parameter Context 的 API 只檢查呼叫者對該 Context 本身的讀寫權限,卻沒有檢查對「引用這個參數的元件」是否也有權限。在使用了元件層級授權政策、且參數值內含可執行腳本的部署裡,更新參數可能在元件自動驗證時觸發程式碼執行,即使該元件從未被啟動。影響範圍侷限在同時使用元件層級授權且元件處於停止狀態的部署,但攻擊路徑本質上是一種權限提升。
受影響版本
| CVE | 嚴重度 | 受影響版本 | 修補版本 |
|---|---|---|---|
CVE-2026-68981 | High | 1.5.0 - 2.10.0 | 2.11.0 |
CVE-2026-68980 | Low | 2.0.0 - 2.10.0 | 2.11.0 |
CVE-2026-62354 | High | 1.10.0 - 2.10.0 | 2.11.0 |
CVE-2026-68979 | Medium | 1.10.0 - 2.10.0 | 2.11.0 |
四則 CVE 的受影響版本範圍雖然起點不同(最早回溯到 1.5.0 或 1.10.0),但修補版本全部指向同一個 2.11.0。這代表升級一次即可同時處理四個問題,不需要分批打不同的修補版本。
修補與緩解
CVE-2026-68981 的修補把回應壓縮邏輯搬到 Jetty Server 層,並直接停用應用層對 gzip 編碼請求的解壓縮,從根本避免「限制壓縮後大小、卻放任解壓縮後大小」的落差。三個 Parameter Context 授權問題則分別在對應端點補上「驗證資源實際歸屬」(資產刪除)、「要求寫入權限」(驗證請求)與「檢查引用元件授權」(參數更新)的邏輯。對於沒有啟用元件層級或 Context 層級差異化授權政策的部署,這幾則問題的實際影響有限,但只要環境中存在多租戶或分權管理的需求,升級到 2.11.0 都是必要動作。
CVE-2026-68981:HTTP 請求解壓縮未限制輸出大小,造成資源耗用型阻斷服務CVE-2026-68980:Parameter Context 資產刪除未驗證資產實際歸屬,形成授權繞過CVE-2026-62354:唯讀使用者可送出 Parameter Context 驗證請求,屬授權不當CVE-2026-68979:Parameter Context 更新未檢查引用元件的授權,可能導致非預期程式碼執行
原始來源:oss-security 2026/08/03/13、oss-security 2026/08/03/12、oss-security 2026/08/03/11、oss-security 2026/08/03/10
undici 一日五發:重試攔截器如何讓回應長度與宣告的 Content-Length 脫鉤
GitHub Advisory Database · 2026-07-29
2026 年 7 月 29 日,GitHub Advisory Database 同一天公告五則 undici 的資安通報。undici 是 Node.js 內建 fetch 與 HTTP client 的底層實作,任何弱點的影響面都不只是單一函式庫,而是牽動整個 Node.js 生態系與所有基於它建置的 proxy、gateway 服務。五則通報涵蓋 CRLF 注入、Cache-Control 解析歧異、Cookie 屬性注入,以及一個牽涉「重試攔截器」、與請求走私(request smuggling)同源的回應失步問題——這也是本次唯一牽涉到轉發層基礎設施安全邊界的一則。
漏洞機制:重試攔截器引發的回應失步
GHSA-8xcm-r25x-g524(CVE-2026-16728)發生在 undici 的 retry 攔截器處理 Range 請求的場景。當上游伺服器對一個 Range 請求回應 206 Partial Content,卻在後續重試、續傳時給出的 Content-Length 與實際傳輸的位元組數不一致,retry 攔截器會把這些片段拼接後仍照原樣把(錯誤的)Content-Length 往上層應用曝露。結果是應用層看到的回應主體長度和它信任的 Content-Length header 對不上,若這個應用本身是一個會把上游 header 和 body 原樣轉發、卻不重新計算長度的 proxy 或 gateway,下游就可能發生回應失步、連線 hang 住,或回應內容被錯誤地拼接。這與傳統 HTTP request smuggling 的成因同構:都是「宣告的長度」與「實際位元組邊界」不一致,讓兩端對同一個連線上的訊息邊界產生分歧解讀。
GHSA-v3r7-h72x-cjcm(CVE-2026-16729)則是 setCookie 序列化時的兩個屬性注入缺陷。第一,validateCookieDomain 沒有像 validateCookiePath 一樣拒絕分號(0x3B),所以像 example.com; SameSite=None 這樣的 domain 值會原封不動被組進 Domain=example.com; SameSite=None。第二,unparsed 陣列的處理迴圈只檢查每個項目是否包含等號,並未對其內容消毒,導致像 X-Custom=val; HttpOnly 這樣的項目會把 HttpOnly 原樣注入到最終的 Set-Cookie 字串。只要應用程式把使用者可控的輸入傳進這兩個欄位,攻擊者就能繞過 SameSite 的 CSRF 防護,或是強制加上、抹除 Secure/HttpOnly 屬性。
另外三則通報同樣值得留意,但機制相對單純。GHSA-m8rv-5g2x-5cg5 是透過 blob-like body 的 type 屬性夾帶 CRLF 字元,注入額外的標頭或分隔符。GHSA-jr45-8vmc-qm54 源於 undici 解析 Cache-Control 指令時,對 = 前後的空白字元處理和某些代理伺服器不一致,可能造成跨使用者的快取內容洩漏。而 GHSA-4cwx-7wf7-3272 則是刻意構造的、退化形式的 private 快取指令,除了可能引發跨使用者資訊洩漏,還能在解析當下讓程序直接崩潰。
受影響版本
| GHSA / CVE | CVSS | 受影響版本 | 修補版本 |
|---|---|---|---|
GHSA-8xcm-r25x-g524 / CVE-2026-16728 | 4.8 Moderate | <6.28.0、>=7.0.0 <7.29.0、>=8.0.0 <8.9.0 | 6.28.0 / 7.29.0 / 8.9.0 |
GHSA-v3r7-h72x-cjcm / CVE-2026-16729 | 4.8 Moderate | <6.28.0、>=7.0.0 <7.29.0、>=8.0.0 <8.9.0 | 6.28.0 / 7.29.0 / 8.9.0 |
這兩則深入檢視的通報影響版本範圍完全相同,涵蓋 6.x、7.x、8.x 三條發布線,顯示問題出現在這幾條分支共用的核心程式碼路徑上。其餘三則通報的完整版本範圍與 CVSS 分數請以各自的 GHSA 頁面為準,本文未逐一深入擷取,以免與已核實的資料混雜。
修補與緩解
兩則深入分析的漏洞都已在 6.28.0、7.29.0、8.9.0 修補。GHSA-8xcm-r25x-g524 的修補讓 retry 攔截器在重組片段回應時,依照實際接收到的位元組數重新計算並修正曝露給應用層的 Content-Length,不再信任上游可能不一致的原始標頭。對於把 undici 用作反向代理或閘道器轉發層的部署,升級是唯一能避免回應長度與實際內容脫鉤的方式,單純在應用層加驗證無法補上攔截器內部的落差。GHSA-v3r7-h72x-cjcm 的修補則讓 validateCookieDomain 與 unparsed 欄位的處理比照既有的路徑驗證邏輯,一併拒絕分號等會被解讀成屬性分隔符的字元。
GHSA-m8rv-5g2x-5cg5:透過 blob-like body 的type屬性夾帶 CRLF,注入額外標頭GHSA-jr45-8vmc-qm54:Cache-Control指令中=前後空白處理不一致,導致跨使用者資訊洩漏GHSA-v3r7-h72x-cjcm(CVE-2026-16729):domain/setCookie欄位未消毒分號,構成 Cookie 屬性注入GHSA-8xcm-r25x-g524(CVE-2026-16728):重試攔截器重組回應時Content-Length與實際內容不符,造成下游回應失步GHSA-4cwx-7wf7-3272:退化形式的 private 快取指令,造成跨使用者資訊洩漏與解析時崩潰
原始來源:GHSA-m8rv-5g2x-5cg5、GHSA-jr45-8vmc-qm54、GHSA-v3r7-h72x-cjcm、GHSA-8xcm-r25x-g524、GHSA-4cwx-7wf7-3272