Phiên bản Node.js của bạn có dính lỗ hổng Brotli RCE? Kiểm tra ngay trong 2 phút

CVE-2026-111497 là một lỗ hổng trong bộ giải mã Brotli của Node.js đang bị khai thác tích cực. Hãy kiểm tra phiên bản bạn đang dùng chỉ trong hai phút, xem những framework và HTTP client nào gọi tới bộ giải mã này, cập nhật bản vá theo từng phương thức cài đặt, và áp dụng các biện pháp giảm...

Ashley Innocent

Ashley Innocent

5 tháng 11 2026

Phiên bản Node.js của bạn có dính lỗ hổng Brotli RCE? Kiểm tra ngay trong 2 phút

Apidog cho doanh nghiệp

Triển khai tại chỗ

SSO & RBAC

Tuân thủ SOC 2

Khám phá Apidog Enterprise

Hãy chạy lệnh này ngay bây giờ. Nếu phiên bản bạn đang dùng thấp hơn mức tối thiểu trong bảng dưới đây, hệ thống của bạn có nguy cơ bị ảnh hưởng bởi lỗ hổng Node.js Brotli RCE (CVE-2026-111497), và kẻ tấn công hiện đã bắt đầu khai thác nó.

node -v
Dòng phiên bản Bị ảnh hưởng Đã vá từ Mới nhất (05/11/2026)
26.x Dưới 26.12.1 26.12.1 26.13.0
24.x Dưới 24.22.1 24.22.1 24.22.2
22.x Dưới 22.23.4 22.23.4 22.23.4
25.x, 20.x, cũ hơn End-of-Life: Chưa được vá và không còn được đánh giá Không có bản vá Nâng cấp lên 24 hoặc 26

Để kiểm tra thêm lần nữa, bạn có thể truy vấn trực tiếp từ binary:

node -p "require('node:zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH"

Bản vá đã bổ sung thêm hằng số này. Chúng tôi đã chạy thử nghiệm trên chín bản build macOS chính thức thuộc ba dòng phiên bản được hỗ trợ: mọi bản build đã vá đều in ra -17, và mọi bản build cũ hơn đều in ra undefined. Tuy nhiên, hãy tin tưởng kết quả từ node -v trước tiên, vì một bản phân phối Linux (distro) khi backport bản vá có thể không thêm hằng số này.

Đừng dựa vào process.versions.brotli. Giá trị này đều trả về 1.2.0 trên cả 24.21.0 lẫn 24.22.1, và 1.1.0 trên cả 22.23.3 lẫn 22.23.4. Node.js đã cherry-pick bản vá mà không thay đổi phiên bản hiển thị, do đó trường thông tin này không thể cho bạn biết hệ thống đã được vá hay chưa.

Tóm tắt nhanh (TL;DR)

Câu hỏi Câu trả lời
Lỗ hổng này là gì? Lỗi ghi ngoài vùng bộ nhớ (out-of-bounds write) trong bộ giải mã Brotli đi kèm với Node.js, còn được gọi là Node2Shell hoặc HTTP2Shell
Mức độ ảnh hưởng Gây crash ứng dụng, rò rỉ bộ nhớ (memory disclosure), hoặc thực thi mã từ xa (remote code execution)
Mức độ nghiêm trọng HIGH (Cao) theo Node.js; 9.8 CRITICAL (Nghiêm trọng - CVSS 3.1) theo NVD
Đã bị khai thác chưa? Có. CISA KEV đã đưa lỗ hổng này vào danh mục vào ngày 27 tháng 10 năm 2026
Có cần đăng nhập không? Không, theo vector của NVD và phân tích từ Akamai
Đối tượng nào có nguy cơ? Các ứng dụng giải mã dữ liệu Brotli không kiểm soát: request body trong Express 5, response của fetch(), các lệnh gọi trực tiếp đến node:zlib
Cách khắc phục Nâng cấp runtime, build lại image, khởi động lại các tiến trình
Có giải pháp tạm thời không? Khuyến cáo chính thức không đề cập giải pháp nào. Các biện pháp đối phó tạm thời bên dưới giúp giảm thiểu phơi nhiễm nhưng không bảo vệ toàn diện
Timeline of CVE-2026-111497: Node.js fixes on October 21, CVE published October 22, exploit traffic reported October 26, CISA KEV listing October 27, KEV due date November 10, next Node.js security release November 11

ELLIO đã ghi nhận lưu lượng khai thác qua các cảm biến bảo mật của mình vào ngày 26 tháng 10, chỉ năm ngày sau khi bản vá được phát hành. Payload thu thập được nhắm mục tiêu vào Node.js 24.22.0 trên kiến trúc x86-64. Mặc dù giai đoạn cuối của payload đó đã thất bại trong các thử nghiệm của ELLIO, nhưng ELLIO cũng xác nhận rằng việc thực thi mã hoàn toàn có thể xảy ra trên các bản build 24.22.0 mặc định. CISA đã ấn định hạn chót cho các cơ quan liên bang Hoa Kỳ hành động là ngày 10 tháng 11, đồng thời đánh dấu tình trạng sử dụng trong mã độc tống tiền (ransomware) là "Chưa xác định" (Unknown). Dữ liệu đo kiểm từ xa của Akamai cho thấy tính đến ngày 26 tháng 10, có tới 61% hệ thống mạng vẫn đang chạy các phiên bản bị ảnh hưởng.

Giải mã Node2Shell: Cơ chế hoạt động của lỗ hổng Brotli

Node2Shell là một lỗi ghi ngoài vùng bộ nhớ (out-of-bounds write) nằm trong bộ giải mã Brotli được tích hợp sẵn bên trong Node.js runtime. Một luồng nén có cấu trúc bất thường (malformed) có thể khiến bộ giải mã ghi tràn qua phần cuối của bộ đệm nội bộ 8 byte, dẫn đến làm hỏng bộ nhớ tiến trình. Hậu quả có thể khiến ứng dụng bị crash, rò rỉ dữ liệu bộ nhớ, hoặc mã độc do kẻ tấn công kiểm soát được thực thi ngay trên máy chủ của bạn.

Nguyên nhân gốc rễ bắt nguồn từ việc phân loại sai lỗi. Một thao tác chuyển đổi khối (block switch) không hợp lệ lại được trả về dưới dạng "cần thêm dữ liệu đầu vào" (I need more input) thay vì "dữ liệu này đã bị hỏng" (this data is broken), khiến bộ giải mã dạng luồng (streaming decoder) liên tục ghi thêm byte vào bộ đệm nhỏ đó.

Lỗi này nằm trong thư viện Brotli của Google, từ phiên bản 1.0.3 đến 1.2.0, vì vậy đây không chỉ là vấn đề riêng của Node.js. Node.js đã vá bản sao nội bộ bằng cách cherry-pick commit upstream 0ed4268a vào bản phát hành 24.22.1 cùng các nhánh tương ứng khác. Lỗ hổng được báo cáo bởi hai nhà nghiên cứu Khai Tran và Sylvie Mayer thuộc OpenAI Codex Security.

Các công cụ quét lỗ hổng phụ thuộc (dependency scanner) thông thường sẽ không thể phát hiện ra điều này, bởi bộ giải mã đi kèm trực tiếp trong runtime chứ không phải một package độc lập. Lệnh npm install vẫn báo cáo 0 lỗ hổng cho dự án thử nghiệm Express 5 của chúng tôi khi chạy trên phiên bản Node.js 24.15.0 chưa được vá.

Những đối tượng nào có nguy cơ bị tấn công?

Một runtime chứa lỗ hổng mới chỉ là một nửa điều kiện. Nửa còn lại là phải có một đường dẫn đưa dữ liệu Brotli không đáng tin cậy vào bộ giải mã. Chúng tôi đã thử nghiệm từng đường dẫn trên Node.js 24.22.1 bằng cách gửi một body không hợp lệ vô hại (chuỗi văn bản not-valid-brotli) kèm header Content-Encoding: br, đồng thời phục vụ dữ liệu Brotli hợp lệ cho từng client.

Đường dẫn / Thư viện Phiên bản thử nghiệm Có kích hoạt bộ giải mã? Kết quả ghi nhận
Express 5 express.json() 5.3.0 (body-parser 2.4.0) Có, theo mặc định 400, “Decompression failed”
Express 4 express.json() 4.22.3 (body-parser 1.20.9) Không 415, unsupported encoding
Koa @koa/bodyparser 6.1.0 Có, theo mặc định 500, lỗi BrotliDecoder trong log
Fastify cùng @fastify/compress 9.3.0 Có, sau khi đăng ký 400, FST_CP_ERR_INVALID_CONTENT
Fastify thuần 5.12.8 Không 400, invalid JSON
Server node:http thuần hoặc http.get 24.22.1 Không Dữ liệu byte thô đi qua trực tiếp
fetch() tích hợp sẵn 24.22.1 Có, luôn luôn Tự động giải mã response
axios, got, node-fetch 1.21.0, 16.2.0, 3.3.2 Có, theo mặc định Tự động giải mã response
zlib.brotliDecompress*, createBrotliDecompress Mọi phiên bản Có Đây chính là decoder API
Diagram of paths into the Brotli decoder in node:zlib: Express 5 parsers, Koa body parsers and Fastify with @fastify/compress on the inbound side, fetch, axios and got on the outbound side, while Express 4, plain Fastify and bare node:http do not reach it

Có hai kết quả rất đáng để lưu ý kỹ hơn. Thứ nhất, fetch() vẫn giải mã Brotli ngay cả khi request đã chỉ định Accept-Encoding: identity, bởi vì nó hoạt động theo header của response chứ không phụ thuộc vào yêu cầu của bạn. Bất kỳ máy chủ nào mà ứng dụng của bạn gọi tới đều có thể tiếp cận bộ giải mã, khiến các tính năng như webhook tester, xem trước liên kết (link previewer) hay nhập URL (URL importer) đều gặp rủi ro. Thứ hai, bản thân mã lỗi 400 chưa chứng minh được điều gì: cả Express 5 và Fastify thuần đều trả về mã này, nhưng chỉ có Express 5 là thực sự chạy qua bộ giải mã. Hãy đọc kỹ thông báo lỗi chi tiết.

Bạn có thể xác định các đường dẫn phơi nhiễm trong dự án bằng ba lệnh sau:

# Direct decoder use
grep -rnE "brotliDecompress|createBrotliDecompress" src/

# Parsers that decode request bodies
npm ls express body-parser @koa/bodyparser @fastify/compress

# Base images and pinned versions
grep -rnE "^FROM .*node" --include='Dockerfile*' .

Chỉ cần thêm một lệnh gọi fetch() mới cũng có thể làm thay đổi hoàn toàn cục diện, vì vậy dù hiện tại "không tiếp cận được", giải pháp tối ưu vẫn luôn là cập nhật bản vá.

Cách khắc phục và cập nhật bản vá cho CVE-2026-111497

Hãy nâng cấp lên bản build mới nhất trong dòng phiên bản bạn đang dùng, chứ đừng chỉ dừng lại ở mức tối thiểu. Tính đến ngày 5 tháng 11, các nguồn cài đặt sau đây đều đã có bản build được vá lỗi:

Phương thức cài đặt Hành động cần thực hiện
nvm nvm install 24 && nvm alias default 24
Homebrew brew upgrade node (hoặc node@24, node@22)
Docker FROM node:24.22.2-slim, sau đó chạy docker build --pull
Gói phân phối Linux (distro) Làm theo khuyến cáo bảo mật của bản phân phối bạn dùng, vì họ có thể backport bản vá dưới số hiệu phiên bản cũ hơn
CI Cập nhật .nvmrc, engines và node-version, sau đó rebuild lại
Serverless Ghi log process.version từ function đã triển khai; đừng mặc định là nền tảng đám mây đã tự động cập nhật

Tiếp theo, hãy xử lý hai sơ hở phổ biến mà nhiều người thường bỏ sót:

Cuối cùng, hãy chạy lại các bước kiểm tra ở đầu bài viết này bên trong container đã triển khai, sau đó tiếp tục kiểm tra trên CI runner và máy tính của lập trình viên. Để kiểm tra bản build bảo mật mới nhất cho từng dòng phiên bản:

curl -s https://nodejs.org/dist/index.json | jq -r '[.[] | select(.security == true and (.version | test("^v(22|24|26)\\.")))] | group_by(.version | split(".")[0]) | .[] | max_by(.date) | "\(.version)  \(.date)"'

Bạn vẫn đang dùng Node.js 20 hoặc 25? Sẽ không có bản build nào được phát hành cho bạn. Hãy tham khảo kế hoạch End-of-Life của Node.js 20 và tiến hành nâng cấp ngay.

Các giải pháp giảm thiểu rủi ro nếu chưa thể cập nhật ngay hôm nay

Những giải pháp này chỉ giúp bạn có thêm thời gian. Không biện pháp nào có thể thay thế hoàn toàn việc nâng cấp.

  1. Từ chối br ngay tại tầng biên (edge). Chặn Content-Encoding: br tại proxy hoặc bộ cân bằng tải của bạn, lưu ý kiểm tra không phân biệt chữ hoa/thường và quét trong cả danh sách giá trị như gzip, br.
  2. Từ chối ngay trong ứng dụng. Đặt đoạn middleware này trước các body parser. Trong thử nghiệm Express 5 của chúng tôi, đoạn mã này trả về mã 415:
app.use((req, res, next) => {
  const encoding = req.headers['content-encoding'] || '';
  if (/\bbr\b/i.test(encoding)) return res.status(415).end();
  next();
});
  1. Tắt tính năng giải nén tự động. Cấu hình express.json({ inflate: false }) cũng trả về mã lỗi 415 cho cùng request đó. Thiết lập này cũng sẽ từ chối cả body nén bằng gzip, vì vậy hãy kiểm tra kỹ các client của bạn trước.
  2. Kiểm soát các response chiều ra (outbound). Thư viện axios hỗ trợ tùy chọn decompress: false, giúp giữ nguyên các byte chưa giải mã trong thử nghiệm của chúng tôi. fetch() thì không có công tắc chuẩn nào để tắt, do đó bạn nên giới hạn các host mà nó có thể truy cập bằng danh sách cho phép (allowlist) và các quy tắc egress.
  3. Giới hạn phạm vi ảnh hưởng (blast radius). Phân tách mạng cho các dịch vụ Node.js, đồng thời giới hạn quyền đọc các tệp core dump, vì ELLIO đã lưu ý rằng sự cố crash có thể làm lộ dữ liệu trong bộ nhớ tại đó.

Hãy đặc biệt chú ý đến dấu hiệu mà ELLIO đã phát hiện đầu tiên: những đợt bùng nổ các request body nén bằng Brotli nhằm thăm dò xem máy chủ nào giải mã chúng. Lưu lượng Content-Encoding: br bất thường gửi tới một JSON API, hoặc sự gia tăng đột biến các trường hợp worker bị crash, là những tín hiệu đáng để bạn điều tra ngay lập tức.

Kiểm tra API hoạt động ổn định sau nâng cấp: Hãy để AI agent đảm nhận

Việc chuyển đổi phiên bản runtime có thể làm thay đổi hành vi của TLS, DNS và HTTP, và bản phát hành tháng 10 vừa qua đã tác động đến cả ba thành phần này. Đừng so sánh thủ công. Hãy để coding agent của bạn chạy Apidog CLI trước và sau khi nâng cấp, rồi báo cáo những điểm có sự thay đổi.

npm install -g apidog-cli@latest
apidog login --with-token <YOUR_TOKEN>
npx -y skills add https://apidog.com   # pick your agent when prompted

Đưa câu lệnh (prompt) này cho agent của bạn:

Run Apidog test suite <id> in project <id> against staging and save the
results to reports/before. After I upgrade Node.js, add a test case for
POST /orders that sends the plain text "not-valid-brotli" with
Content-Encoding: br, asserts a 4xx status, then checks GET /health
returns 200. Rerun the suite to reports/after and list every request
whose result changed.

Agent sẽ thực thi các lệnh sau:

apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/before
apidog cli-schema get test-case-create
apidog cli-schema validate test-case-create --file case.json
apidog test-case create --project <id> --file case.json
apidog run --test-case <caseId> --project <id> -e <envId> -r cli,json
apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/after

Test case bổ sung đó thực chất là một bài kiểm tra tình trạng (health check), không phải mã khai thác: phần body là văn bản thuần túy, và trong thử nghiệm của chúng tôi, nó đều thất bại với cùng một lỗi padding trên cả bản build bị lỗi lẫn bản build đã vá. Tuy nhiên, nó giúp phát hiện một lỗ hổng xử lý thực tế: bài test Koa của chúng tôi đã trả về lỗi 500 do lỗi giải mã không được xử lý, trong khi một public API đúng chuẩn phải phản hồi bằng mã 4xx. Hãy chỉ chạy thử nghiệm này trên môi trường staging.

Tài liệu tham khảo lệnh run liệt kê đầy đủ các cờ tùy chọn, và hướng dẫn CI/CD sẽ giúp bạn đưa quy trình chạy JUnit tương tự vào pipeline của mình. Gói miễn phí của Apidog hỗ trợ tối đa 4 người dùng.

Vì sao mốc thời gian 11 tháng 11 lại đáng chú ý tiếp theo?

Dự án Node.js đã thông báo trước về một bản phát hành mới cho các dòng 26.x, 24.x và 22.x vào khoảng hoặc ngay sau ngày 11 tháng 11 năm 2026, tiếp tục ở mức độ nghiêm trọng cao nhất là HIGH. Hiện vẫn chưa có mã CVE nào được công bố. Hướng dẫn về bản phát hành tháng 11 của chúng tôi sẽ trình bày cách thức chuẩn bị.

Hãy vá lỗ hổng Brotli ngay từ bây giờ thay vì chờ đợi để gộp chung vào đợt cập nhật đó. Lỗ hổng này hiện đã có thông tin chi tiết công khai và lưu lượng khai thác thực tế trên mạng, hơn nữa quá trình nâng cấp bạn thực hiện trong tuần này sẽ đóng vai trò như một buổi diễn tập cho đợt tiếp theo: cùng quy trình kiểm kê, cùng thao tác rebuild image, cùng bộ test suite. Các lỗ hổng PHP2Shell và React2Shell trước đây cũng từng diễn biến theo chiều hướng tương tự.

Câu hỏi thường gặp (FAQ)

CVE-2026-111497 đã bị khai thác trong thực tế chưa? Có. CISA đã thêm lỗ hổng này vào danh mục Known Exploited Vulnerabilities vào ngày 27 tháng 10 năm 2026 dưới mục Google Brotli, và ELLIO đã ghi nhận lưu lượng tấn công từ ngày 26 tháng 10.

Lỗ hổng này ở mức High hay Critical? Cả hai cách gọi đều có căn cứ thực tế. Node.js xếp loại ở mức HIGH. NVD chấm điểm 9.8 CRITICAL theo thang điểm CVSS 3.1, trong khi điểm số CVSS 4.0 do Google công bố là 9.2. Hãy lên kế hoạch dựa trên thực tế là lỗ hổng này đang bị khai thác.

Tôi đang dùng Express 4 thì có an toàn không? Phần request body của bạn không tiếp cận bộ giải mã, nhưng các lệnh gọi fetch(), axios và trực tiếp từ node:zlib thì vẫn có thể tiếp cận. Bản phát hành này cũng đã vá thêm chín lỗ hổng CVE khác, vì vậy dù sao bạn vẫn nên nâng cấp.

Node.js 20 có bị ảnh hưởng không? Dự án Node.js không đánh giá các dòng phiên bản đã kết thúc vòng đời (End-of-Life), và chúng sẽ không nhận được bản vá nào. Hãy coi các phiên bản 20.x và 25.x là chưa được vá và không được đánh giá, sau đó tiến hành nâng cấp.

Lỗ hổng này có ảnh hưởng đến nền tảng nào khác ngoài Node.js không? Có. Khuyến cáo bảo mật của GitHub đề cập đến các package PyPI brotli và brotlicffi, các nhà cung cấp cũng đã ghi nhận bản vá cho Bun 1.4.4 và .NET. Bạn cần cập nhật riêng từng bộ giải mã, vì việc nâng cấp Node.js chỉ bảo vệ bản sao đi kèm của chính nó.

Thực hành thiết kế API trong Apidog

Khám phá cách dễ dàng hơn để xây dựng và sử dụng API