CVE-2026-23869 Denial of Service trong React Server Components
Lỗ hổng Denial of Service mức độ nghiêm trọng trong React 19 đang cho phép kẻ tấn công làm tê liệt CPU máy chủ mà không cần xác thực. Nguy cơ này đang bị khai thác trực tiếp trên thực tế và đe dọa sự ổn định của mọi hệ thống đang vận hành React Server Components.
CVE-2026-23869 là gì?
CVE-2026-23869 là lỗ hổng bảo mật mức độ CAO (7.5/10) ảnh hưởng đến các ứng dụng web sử dụng React 19 có tính năng Server Actions. Đây là lỗ hổng thứ ba trong chuỗi liên quan đến cùng một vấn đề. Các bản vá trước đó vào tháng 12/2025 và tháng 1/2026 đều chưa xử lý triệt để. Lỗ hổng này đang bị khai thác ngoài thực tế , kẻ tấn công không cần tài khoản, không cần kỹ thuật cao, chỉ cần gửi một request được tạo sẵn là có thể khiến server ngừng hoạt động hoàn toàn.
| CVE ID | CVE-2026-23869 |
| CVSS 3.1 | AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-400 · CWE-502 |
| Reporter | Facebook, Inc. (CNA) |
| Advisory | GHSA-479c-33wc-g2pg |
| Related CVEs | CVE-2026-23864 / CVE-2025-55182 CVE-2025-55184 |
| Package | Lỗ hổng | Đã vá |
|---|---|---|
| react-server-dom-webpack react-server-dom-parcel react-server-dom-turbopack |
19.0.0–19.0.4 19.1.0–19.1.5 19.2.0–19.2.4 |
19.0.5 19.1.6 19.2.5 |
React 18.x trở xuống · SPA thuần client-side · App không dùng Server Actions · Static site generators
Ba lần vá trong chưa đầy ba tháng
Cơ chế gây tê liệt hệ thống
React Server Components vận hành dựa trên giao thức Flight protocol để truyền tải dữ liệu giữa client và server. Khi người dùng thực hiện một Server Action, yêu cầu này được gửi đi dưới dạng multipart/form-data và máy chủ sẽ tiến hành deserialize payload thông qua hàm reviveModel().
Tuy nhiên, lỗ hổng phát sinh do React không thực hiện kiểm tra các key trong payload là own property hay inherited prototype property. Kẻ tấn công lợi dụng sơ hở này để chèn các tham chiếu vòng lặp vô hạn (circular reference) và thực hiện kỹ thuật prototype traversal nhằm đánh lừa trình giải mã của hệ thống. Thay vì xử lý các tác vụ thông thường, máy chủ sẽ bị vắt kiệt tài nguyên CPU để giải quyết các yêu cầu độc hại không có điểm dừng, dẫn đến tình trạng treo hoàn toàn ngay lập tức.
DoS truyền thống cần lưu lượng lớn. Lỗ hổng này chỉ cần một vài request tinh vi, mỗi request khóa chết CPU trong 60 giây. Với server 4 core, chỉ cần 4 request đồng thời để hệ thống ngừng hoàn toàn.
Rủi ro thực tế khi bị khai thác
6 bước từ request đến DoS hoàn toàn
$@0) và prototype traversal (__proto__:then).reviveModel() bắt đầu phân tích payload mà không xác thực.$@0, gọi .then() nhưng .then() trỏ lại chính object đó — vòng lặp vô tận bắt đầu.4 kỹ thuật bypass WAF của attacker
Các kỹ thuật dưới đây được ghi nhận trong thực tế. Đây là lý do WAF rules cần nhiều lớp thay vì chỉ dựa vào một pattern.
charset=shift_jis trong Content-Type. WAF không decode đúng nhưng Next.js vẫn xử lý bình thường — signature matching thất bại.\u00xx để mã hóa ký tự đặc biệt. WAF không normalize Unicode trước khi so khớp — payload vượt qua kiểm tra.Regex patterns cần triển khai
| Pattern | ||
|---|---|---|
| Header Patterns — điều kiện tiên quyết | Loại | Vai trò |
(?i)(?:next-action|rsc-action-id) | HEADER KEY | Xác định RSC request — điều kiện tiên quyết |
(?i)text/x-component | ACCEPT/CT | RSC content type |
(?i)next-router-state-tree | OPTIONAL | Header phụ, kết hợp để tăng độ chính xác |
(?i)React2ShellScanner|Nuclei.*CVE | BLOCK | Scanner đã biết đang chủ động quét |
| Body Patterns — payload độc hại | Mức độ | Mô tả |
\$\@\d+ | CRITICAL | Self-reference signature — dấu hiệu rõ nhất của circular reference attack |
(?i)"status"\s*:\s*"resolved_model" | CRITICAL | Fake Chunk object giả lập internal React state |
(?i):constructor["'] | CRITICAL | Truy cập Function constructor qua prototype chain |
(?i)_response["']\s*: | HIGH | Response object hijacking |
(?i)_formData["']\s*: | HIGH | Form data hijacking |
(?i)__proto__|prototype\s*: | CRITICAL | Prototype pollution attack |
(?i)charset\s*= | HIGH | Charset bypass technique |
\\u00 | HIGH | Unicode escape evasion |
Rules chi tiết theo từng nền tảng
Luôn test ở chế độ COUNT/LOG trước, sau đó mới chuyển sang BLOCK.
Compatible: ModSecurity 2.x / 3.x (Apache, Nginx, IIS).
# ═══════════════════════════════════════════════════════════════ # ModSecurity Rules — CVE-2026-23869 / React Server Components DoS # Compatible: ModSecurity 2.x / 3.x (Apache, Nginx, IIS) # ═══════════════════════════════════════════════════════════════ # --- Rule 1: Detect RSC request headers --- SecRule REQUEST_METHOD "POST" \ "id:900001, phase:1, pass, nolog, chain" SecRule REQUEST_HEADERS_NAMES "(?i)(?:next-action|rsc-action-id)" \ "setvar:tx.rsc_request=1" # --- Rule 2: Block $@ self-reference (CRITICAL) --- SecRule TX:rsc_request "@eq 1" \ "id:900002, phase:2, deny, status:403, \ log, msg:'RSC Flight self-reference attack ($@)', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "\$\@\d+" \ "t:urlDecodeUni,t:jsDecode,t:utf8toUnicode" # --- Rule 3: Block exploit payload signatures --- SecRule TX:rsc_request "@eq 1" \ "id:900003, phase:2, deny, status:403, \ log, msg:'RSC exploit payload detected', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY \ "(?i)(?:resolved_model[\"']|:constructor[\"']|_response[\"']\s*:|_formData[\"']\s*:)" \ "t:urlDecodeUni,t:jsDecode,t:utf8toUnicode" # --- Rule 4: Block prototype pollution --- SecRule TX:rsc_request "@eq 1" \ "id:900004, phase:2, deny, status:403, \ log, msg:'RSC prototype pollution attempt', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "(?i)(?:__proto__|prototype\s*:)" \ "t:urlDecodeUni,t:jsDecode" # --- Rule 5: Block charset manipulation bypass --- SecRule TX:rsc_request "@eq 1" \ "id:900005, phase:2, deny, status:403, \ log, msg:'RSC charset bypass attempt', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "(?i)charset\s*=" \ "t:urlDecodeUni" # --- Rule 6: Block unicode escape bypass --- SecRule TX:rsc_request "@eq 1" \ "id:900006, phase:2, deny, status:403, \ log, msg:'RSC unicode escape evasion', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "\\\\u00" \ "t:none" # --- Rule 7: Limit body size for RSC requests (128KB) --- SecRule TX:rsc_request "@eq 1" \ "id:900007, phase:2, deny, status:413, \ log, msg:'RSC request body too large', \ tag:'CVE-2026-23869', severity:3, chain" SecRule REQUEST_BODY_LENGTH "@gt 131072" # --- Rule 8: Block known scanners --- SecRule REQUEST_HEADERS:User-Agent \ "(?i)(?:React2ShellScanner|Nuclei.*CVE-2025-55182)" \ "id:900008, phase:1, deny, status:403, \ log, msg:'RSC vulnerability scanner blocked', severity:2"
Dashboard: Security → WAF → Custom Rules.
# ═══════════════════════════════════════════════════════════════ # Cloudflare WAF Custom Rule — CVE-2026-23869 # Dashboard → Security → WAF → Custom Rules → Create Rule # ═══════════════════════════════════════════════════════════════ # --- Rule 1: Block exploit payloads (Pro/Business — 8KB inspect) --- (expression) (http.request.method eq "POST") and (any(http.request.headers.names[*] matches "(?i)(?:next-action|rsc-action-id)")) and ( (http.request.body.size ge 8192) or (http.request.body.raw matches "(?i)charset=") or (http.request.body.raw contains "\\u00") or ( (http.request.body.raw contains "_formData") and (http.request.body.raw contains "_response") and (http.request.body.raw contains "resolved_model") and (http.request.body.raw contains ":constructor") ) ) Action: Block # --- Rule 2: Block self-reference pattern --- (expression) (http.request.method eq "POST") and (any(http.request.headers.names[*] matches "(?i)(?:next-action|rsc-action-id)")) and (http.request.body.raw matches "\$@[0-9]") Action: Block # --- Rule 3: Rate limit RSC endpoints --- (expression) (http.request.method eq "POST") and (any(http.request.headers.names[*] matches "(?i)(?:next-action|rsc-action-id)")) Action: Rate Limit — 30 requests / 60s per IP # --- Managed Rules (bật sẵn) --- Rule IDs (Paid): 33aa8a8a948b48b28d40450c5fb92fba ← Primary exploit bc1aee59731c488ca8b5314615fce168 ← Bypass variants 2694f1610c0b471393b21aef102ec699 ← Promise recursion DoS 1d54691cb822465183cb49e2f562cf5c ← Scanner detection Rule IDs (Free): 2b5d06e34a814a889bee9a0699702280 ← Primary exploit cbdd3f48396e4b7389d6efd174746aff ← Bypass variants
Console → WAF → Web ACL → Add custom rule.
// ═══════════════════════════════════════════════════════════ // AWS WAF Custom Rule — CVE-2026-23869 // Deploy via Console → WAF → Web ACL → Rules → Add custom // ═══════════════════════════════════════════════════════════ { "Name": "ReactRSC_CVE_2026_23869", "Priority": 10, "Action": { "Block": {} }, "Statement": { "AndStatement": { "Statements": [ // Condition 1: POST method only { "RegexMatchStatement": { "FieldToMatch": { "Method": {} }, "RegexString": "POST", "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ] } }, // Condition 2: RSC header present { "RegexMatchStatement": { "FieldToMatch": { "Headers": { "MatchPattern": { "All": {} }, "MatchScope": "KEY", "OversizeHandling": "MATCH" } }, "RegexString": "(?i)(?:next-action|rsc-action-id)", "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ] } }, // Condition 3: Malicious body patterns { "OrStatement": { "Statements": [ { "RegexMatchStatement": { "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } }, "RegexString": "\\$\\@", "TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE_UNI" }, { "Priority": 1, "Type": "JS_DECODE" }, { "Priority": 2, "Type": "UTF8_TO_UNICODE" } ] } }, { "RegexMatchStatement": { "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } }, "RegexString": "(?i)resolved_model[\"']|:constructor[\"']|_response[\"']\\s*:|_formData[\"']\\s*:", "TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE_UNI" }, { "Priority": 1, "Type": "JS_DECODE" }, { "Priority": 2, "Type": "UTF8_TO_UNICODE" } ] } }, { "RegexMatchStatement": { "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } }, "RegexString": "(?i)charset\\s*=", "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ] } } ] } } ] } }, "VisibilityConfig": { "CloudWatchMetricsEnabled": true, "MetricName": "ReactRSC_CVE_2026_23869", "SampledRequestsEnabled": true } } // Managed Rule (bật thêm): // AWSManagedRulesKnownBadInputsRuleSet v1.24+
Portal → Application Gateway / Front Door → WAF Policy.
// ═══════════════════════════════════════════════════════════ // Azure WAF Custom Rule — CVE-2026-23869 // Portal → Application Gateway / Front Door → WAF Policy // ═══════════════════════════════════════════════════════════ { "name": "BlockReactRSCExploit", "priority": 1, "ruleType": "MatchRule", "action": "Block", "matchConditions": [ { // Condition 1: Request has RSC action header "matchVariables": [ { "variableName": "RequestHeaders", "selector": "next-action" } ], "operator": "Any" }, { // Condition 2: Body contains exploit patterns "matchVariables": [ { "variableName": "PostArgs" } ], "operator": "Contains", "matchValues": [ "constructor", "__proto__", "prototype", "_response", "resolved_model", "_formData", "$@" ], "transforms": [ "Lowercase", "UrlDecode", "RemoveNulls" ] } ] }
Console → Network Security → Cloud Armor.
# ═══════════════════════════════════════════════════════════ # Google Cloud Armor — CVE-2026-23869 # Console → Network Security → Cloud Armor → Policy → Rules # ═══════════════════════════════════════════════════════════ # --- Rule 1: Use preconfigured WAF rules --- Condition: ( has(request.headers['next-action']) || has(request.headers['rsc-action-id']) ) && evaluatePreconfiguredWaf('cve-canary', { 'sensitivity': 0, 'opt_in_rule_ids': [ 'google-mrs-v202512-id000001-rce', 'google-mrs-v202512-id000002-rce' ] }) Action: deny-403 Priority: 100 # --- Rule 2: Rate limit RSC endpoints --- Condition: has(request.headers['next-action']) || has(request.headers['rsc-action-id']) Action: rate_based_ban Rate limit: 50 requests / 60s per IP Ban duration: 300s Priority: 200
Security Center → Application Security → Custom Rules.
// ═══════════════════════════════════════════════════════════ // Akamai WAF — CVE-2026-23869 // Security Center → Application Security → Custom Rules // ═══════════════════════════════════════════════════════════ { "name": "react2shell_cve_2026_23869", "conditions": { "all": [ { "type": "requestMethodMatch", "positiveMatch": true, "values": ["POST"] }, { "type": "requestHeaderMatch", "positiveMatch": true, "headerName": "*", "matchOperator": "MATCHES_REGEX", "matchValue": "(?i)(?:next-action|rsc-action-id)" }, { "any": [ { "type": "contentLengthMatch", "positiveMatch": true, "matchOperator": "GREATER_THAN", "matchValue": 16384 }, { "type": "requestBodyMatch", "matchOperator": "MATCHES_REGEX", "matchValue": "(?i)content-type\\s*:[^\\r\\n]*charset\\s*=" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "\\u00" }, { "all": [ { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "resolved_model" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": ":constructor" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "_response" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "_formData" } ] } ] } ] }, "action": "DENY" }
Cấu hình webserver như lớp phòng thủ bổ sung
Ngay cả khi WAF bị bypass, giới hạn ở tầng webserver vẫn giảm thiểu đáng kể impact. Cấu hình tối thiểu: giới hạn body 128KB, timeout 30 giây.
# ═══════════════════════════════════════════════════ # Nginx — CVE-2026-23869 Protection # Add to server{} or location{} block # ═══════════════════════════════════════════════════ # --- Detect RSC headers via map --- map $http_next_action $is_rsc_next { default 0; "~." 1; } map $http_rsc_action_id $is_rsc_action { default 0; "~." 1; } map "$is_rsc_next$is_rsc_action" $is_rsc { default 0; "~1" 1; } # --- Rate limiting zone --- limit_req_zone $binary_remote_addr zone=rsc_limit:10m rate=10r/s; server { # Max body size (giới hạn payload) client_max_body_size 128k; # Timeout ngắn để kill hung connections proxy_read_timeout 30s; proxy_send_timeout 30s; proxy_connect_timeout 10s; location / { # Block known scanners if ($http_user_agent ~* "(React2ShellScanner|Nuclei.*CVE-2025-55182)") { return 403; } # Apply rate limit on RSC requests limit_req zone=rsc_limit burst=5 nodelay; # Reject text/x-component on non-RSC routes if ($http_accept ~* "text/x-component") { # Only allow if route is expected RSC endpoint # Customize per your app routing } proxy_pass http://backend; } }
# ═══════════════════════════════════════════════════ # Apache — CVE-2026-23869 Protection # Add to VirtualHost or .htaccess # Requires: mod_rewrite, mod_headers, mod_ratelimit # ═══════════════════════════════════════════════════ RewriteEngine On # --- Block known vulnerability scanners --- RewriteCond %{HTTP_USER_AGENT} "(?i)(React2ShellScanner|Nuclei.*CVE-2025-55182)" RewriteRule .* - [F,L] # --- Limit request body size (128KB) --- LimitRequestBody 131072 # --- Timeout settings --- TimeOut 30 ProxyTimeout 30 # --- Block RSC exploit patterns (requires mod_security or mod_rewrite) --- # Block if Next-Action header + suspicious body patterns SetEnvIfNoCase ^Next-Action$ "." RSC_REQUEST SetEnvIfNoCase ^RSC-Action-Id$ "." RSC_REQUEST # Rate limit RSC endpoints (mod_ratelimit) <If "env('RSC_REQUEST') == '1'"> SetOutputFilter RATE_LIMIT SetEnv rate-limit 512 </If> # --- If using mod_security, include the ModSecurity rules above ---
# ═══════════════════════════════════════════════════ # HAProxy — CVE-2026-23869 Protection # ═══════════════════════════════════════════════════ frontend http_front bind *:443 ssl crt /etc/ssl/cert.pem # --- Detect RSC headers --- acl is_rsc_next req.hdr(next-action) -m found acl is_rsc_action req.hdr(rsc-action-id) -m found acl is_rsc is_rsc_next or is_rsc_action acl is_post method POST # --- Rate limit: 100 req / 10s per IP --- stick-table type ip size 100k expire 60s store http_req_rate(10s) acl rsc_rate_exceeded sc0_http_req_rate gt 100 http-request track-sc0 src if is_rsc is_post http-request deny deny_status 429 if is_rsc is_post rsc_rate_exceeded # --- Block oversized RSC requests (128KB) --- acl body_too_large req.body_size gt 131072 http-request deny deny_status 413 if is_rsc is_post body_too_large # --- Block scanner user agents --- acl is_scanner req.hdr(user-agent) -i -m reg "React2ShellScanner|Nuclei.*CVE-2025" http-request deny deny_status 403 if is_scanner # --- Timeouts --- timeout client 30s timeout server 30s timeout connect 10s default_backend app_servers backend app_servers server app1 127.0.0.1:3000 check
# ═══════════════════════════════════════════════════ # Caddy — CVE-2026-23869 Protection # Caddyfile configuration # ═══════════════════════════════════════════════════ example.com { # --- Rate limit RSC endpoints --- rate_limit { zone rsc_zone { key {remote_host} events 30 window 60s } match { method POST header Next-Action * } } # --- Request body size limit --- request_body { max_size 128KB } # --- Block scanner user agents --- @scanner { header_regexp User-Agent "(?i)(React2ShellScanner|Nuclei.*CVE-2025)" } respond @scanner 403 # --- Timeouts --- servers { timeouts { read_body 30s read_header 10s write 30s idle 120s } } reverse_proxy localhost:3000 }
Vá lỗi & Cập nhật
WAF và webserver config là các lớp phòng thủ quan trọng nhưng không triệt để. Nâng cấp lên phiên bản đã vá là cách duy nhất xử lý hoàn toàn.
# npm npm install react-server-dom-webpack@19.0.5 npm install react-server-dom-parcel@19.0.5 npm install react-server-dom-turbopack@19.0.5 # Hoặc cho branch 19.1.x / 19.2.x npm install react-server-dom-webpack@19.1.6 npm install react-server-dom-webpack@19.2.5 # yarn yarn add react-server-dom-webpack@19.0.5 # pnpm pnpm add react-server-dom-webpack@19.0.5
# Đảm bảo lockfile đã cập nhật COPY package.json package-lock.json ./ RUN npm ci # Build lại image docker build -t myapp:patched . docker push myapp:patched
# Kiểm tra version đã vá npm ls react-server-dom-webpack \ react-server-dom-parcel \ react-server-dom-turbopack # Expected: 19.0.5, 19.1.6, hoặc 19.2.5+
| Nền tảng | Hành động | Ghi chú |
|---|---|---|
| Vercel | Redeploy | Tự động cập nhật runtime. |
| Netlify | Update + redeploy | Kiểm tra Next.js runtime trong build log. |
| AWS Amplify | Update + WAF | Bật AWSManagedRulesKnownBadInputsRuleSet v1.24+ |
| CF Pages | Bật WAF rules | Workers đã được bảo vệ ở runtime layer. |
| Self-hosted | Update + WAF + WS | Áp dụng toàn bộ rules trong trang này. |
Checklist bảo vệ theo ưu tiên thời gian
VNIS — Giải pháp bảo mật &
tăng tốc Web/App/API
Ngay khi CVE-2026-23869 được công bố, VNIS của VNETWORK đã nhanh chóng cập nhật rule phòng vệ, chặn đứng các request khai thác nhắm vào React Server Components, giúp hệ thống vận hành ổn định và không gián đoạn.
Giải pháp VNIS WAAP bảo mật được kiến tạo từ AI – nơi AI không chỉ là công cụ hỗ trợ, mà đóng vai trò trung tâm trong việc xây dựng giải pháp, dự báo, nhận diện và phòng chống các cuộc tấn công mạng ngày càng tinh vi.

Tài liệu mang tính chất tham khảo và phòng thủ. Kiểm tra nguồn chính thức để có thông tin mới nhất.