Web Bot Auth: IETF vừa chuẩn hoá xác thực bot bằng chữ ký số, tôi tự ký một request thử trên beru.io.vn
Ngày 1/9/2026, IETF xuất bản draft-ietf-webbotauth-httpsig-protocol-00 — chuẩn hoá Web Bot Auth, xác thực bot bằng chữ ký số Ed25519 thay cho IP allowlist và User-Agent dễ giả. Tôi trích spec, tự dựng một request ký đúng công thức RFC 9421 bằng Node.js, verify ra true, rồi đem well-known path mà spec yêu cầu ra test trên beru.io.vn — kết quả 404 — và phát hiện hai nguồn (Cloudflare 2025, IETF 2026) viết well-known path lệch nhau một chữ.

Trong bài này
Ngày 1/9/2026, IETF xuất bản draft-ietf-webbotauth-httpsig-protocol-00 — tài liệu đầu tiên mang tên chính thức của working group webbotauth, thay cho một loạt draft cá nhân đã tồn tại từ 2025. Đây là bản đặc tả cho Web Bot Auth: xác thực bot bằng chữ ký số thay vì dựa vào IP hay User-Agent. Tôi đọc spec, tự dựng một request ký bằng Ed25519 đúng theo công thức RFC 9421, rồi đem test cái well-known path mà spec yêu cầu vào chính beru.io.vn — kết quả là 404, và một phát hiện nhỏ về hai bản spec đang viết sai lệch nhau đúng một chữ.
Xác thực bot bằng chữ ký số: vấn đề cũ, cách giải mới
Từ trước tới giờ, một trang web muốn biết "con bot đang gõ cửa mình có đúng là nó tự nhận không" chỉ có hai công cụ: IP allowlist (nhà cung cấp bot công bố dải IP, server so khớp) và User-Agent header (bot tự khai tên, server tin theo lời khai). Cả hai đều có lỗ hổng cố hữu. Dải IP thay đổi, được thuê lại, hoặc bị dùng chung qua NAT/proxy — công bố hôm nay đúng không có nghĩa còn đúng sau vài tháng. Còn User-Agent thì ai cũng gõ được: curl -A "GPTBot/1.0" là xong, chẳng có gì ngăn một request bất kỳ tự xưng là GPTBot.
Web Bot Auth thay hai cơ chế đó bằng chữ ký mật mã trên từng request. Bot giữ một private key, ký một tập thành phần cố định của HTTP request (tối thiểu là @authority hoặc @target-uri), gắn chữ ký vào header Signature cùng với Signature-Input mô tả những gì đã ký. Server verify bằng public key lấy từ một địa chỉ well-known do chính bot công bố. Không cần danh sách IP tĩnh, không cần tin vào một chuỗi text tự khai — chỉ cần phép toán chữ ký số đúng hay sai.
Đây không phải ý tưởng hoàn toàn mới của tháng 9/2026. Nhưng thứ mới, và là lý do bài này tồn tại, là: draft vừa chuyển từ "vài công ty tự viết riêng" sang "tài liệu chính thức của một IETF working group", với ngày cập nhật là 1/9/2026 — cách lúc tôi viết bài này đúng hai ngày.
Từ draft cá nhân của Cloudflare đến tài liệu chính thức của IETF
Dòng thời gian, lấy từ chính các tài liệu IETF và Cloudflare:
- 1/7/2025: Cloudflare công bố tích hợp HTTP Message Signatures vào chương trình Verified Bots Program của họ, dựa trên draft cá nhân
draft-meunier-webbotauth-httpsig-protocoldo Thibault Meunier (Cloudflare) viết. Đây là lúc cơ chế này lần đầu chạy thật trên hạ tầng production, dù bản thân đặc tả vẫn chỉ là draft cá nhân, "no formal standing in the IETF standards process". - Trong năm 2025-2026: IETF lập charter cho working group
webbotauth, với mục tiêu công khai ghi trong charter là "standardize methods for cryptographically authenticating automated clients and providing additional information about their operators to Web sites." Charter đặt hai mốc standards-track spec vào tháng 4/2026, và một Best Current Practice (BCP) vận hành vào tháng 8/2026. - 1/9/2026:
draft-meunier-webbotauth-httpsig-protocolchính thức được working group nhận nuôi (adopt), đổi tên thànhdraft-ietf-webbotauth-httpsig-protocol-00. Đồng tác giả lúc này có thêm Sandor Major của Google, bên cạnh Meunier của Cloudflare.
Cái tên -00 ở cuối không phải ngẫu nhiên. Trong quy ước đặt tên của IETF, một draft nằm ngoài working group đánh số theo tên tác giả (draft-meunier-...) và số thứ tự riêng của tác giả đó. Khi working group chính thức "adopt" một draft, nó được đổi tên theo mẫu draft-ietf-<wg>-... và số thứ tự reset lại về -00 — bất kể bản trước đó đã đi tới -02 hay -05. Vụ đổi tên này, do đó, chính là tín hiệu công khai, kiểm chứng được qua datatracker, rằng đặc tả đã bước từ "một công ty đề xuất" sang "cả nhóm làm việc IETF đứng sau". Datatracker liệt kê rõ: tài liệu hiện tại là "Active Internet-Draft (webbotauth WG)", "Replaces draft-meunier-webbotauth-httpsig-protocol", hết hạn ngày 5/3/2027.
Đáng chú ý: charter ghi mốc standards-track spec là tháng 4/2026, nhưng bản -00 chính thức đầu tiên tới tận 1/9/2026 mới xuất hiện — trễ khoảng 5 tháng so với mốc ban đầu. Đây là thứ tôi kiểm được trực tiếp từ trang charter của working group, không phải suy đoán: mốc ghi "April 2026" cho spec, còn ngày cập nhật thật của -00 là "2026-09-01".
Cơ chế: header nào, ký cái gì, verify ra sao
Phần lõi kỹ thuật nằm ở ba header và một well-known path. Tôi trích nguyên văn từ chính draft -00, giữ đúng các từ khoá RFC 2119.
Signature-Agent là header khai báo danh tính bot, ở dạng Structured Field kiểu Dictionary theo RFC 9651 §3.2. Giá trị của mỗi member "MUST be String Items that contain a URI, whose scheme MUST be https." Mỗi member còn mang tham số type, có thể là directory, jwks_uri, hoặc cimd; nếu bỏ trống, mặc định là directory. Ví dụ đúng cú pháp mà spec đưa ra:
Signature-Agent: sig1="https://signature-agent.test"
Signature-Agent: sig1="https://signature-agent.test/jwks.json";type=jwks_uri
Signature-Input và Signature đi theo cặp, theo khuôn của RFC 9421 (HTTP Message Signatures — bản thân RFC này ban hành trước, mô tả cơ chế ký HTTP tổng quát; webbotauth chỉ là một "profile" áp cơ chế đó vào use-case xác thực bot). Draft yêu cầu: agent "MUST include at least one of the following components: @authority or @target-uri", và "MUST include the following @signature-params: created, expires, keyid, and tag." Header Signature-Agent bản thân nó cũng "MUST be signed as a component" — nghĩa là chữ ký không chỉ xác nhận request, mà còn xác nhận chính cái danh tính agent tự khai không bị đổi giữa đường.
Giá trị keyid không phải chuỗi tuỳ ý: nó "MUST be a base64url JWK SHA-256 Thumbprint" theo RFC 7638 (với khoá RSA/EC) hoặc theo RFC 8037 Appendix A.3 (với Ed25519) — tức là chính public key băm ra, không phải một ID do bot tự đặt tên. Tham số tag thì cố định: "MUST be web-bot-auth" cho chữ ký trên request thường, và http-message-signatures-directory cho chữ ký trên chính response của well-known directory (Appendix B của draft).
Về thuật toán, draft giới hạn "to algorithms registered in the HTTP Signature Algorithms section" của IANA, và cấm thẳng: "MUST NOT use shared HMAC" (tham chiếu §3.3.3 RFC 9421) — nghĩa là bắt buộc dùng khoá bất đối xứng, không cho phép khoá đối xứng dùng chung. Ed25519 và RSA-PSS-SHA512 được nêu làm ví dụ, không có thuật toán nào bị bắt buộc tuyệt đối, nhưng tinh thần spec nghiêng hẳn về Ed25519 vì khoá và chữ ký ngắn, tốc độ ký/verify nhanh — phù hợp để chạy trên mọi request thay vì chỉ vài request quan trọng.
Thời hạn hiệu lực: expires "SHOULD be no more than 24 hours." Đây là SHOULD chứ không phải MUST — server verify được quyền chấp nhận cửa sổ ngắn hơn nếu risk model của họ đòi hỏi khắt khe hơn, draft ghi rõ "verifiers should avoid accepting signatures with freshness windows longer than their risk model permits." Cơ chế chống replay dựa trên created/expires cộng nonce theo §7.2.2 của RFC 9421; draft webbotauth "defines no additional nonce requirements" — tức là không phát minh thêm cơ chế chống replay riêng, dùng nguyên cái RFC 9421 đã có.
Còn key directory — nơi verify lấy public key — là một JWKS (JSON Web Key Set, theo RFC 7517 §5), phục vụ tại một well-known URI cố định. Định dạng media type khai báo là application/http-message-signatures-directory+json, và mỗi key trong đó có thể mang nbf/exp riêng để giới hạn thời gian hiệu lực của chính khoá, tách biệt với expires của từng chữ ký.
Tự ký một request bằng Ed25519 — làm đúng công thức RFC 9421 §2.5
Đọc header thì dễ nhớ, nhưng cách server dựng ra chuỗi thật sự đem đi ký — gọi là "signature base" trong RFC 9421 §2.5 — mới là phần dễ làm sai. Tôi dựng lại bằng Node.js (v22.22.2, module node:crypto có sẵn generateKeyPairSync('ed25519') và crypto.sign(null, data, privateKey) cho Ed25519, không cần thư viện ngoài).
Các bước, đúng theo thứ tự draft yêu cầu:
- Sinh cặp khoá Ed25519, xuất public key ra JWK.
- Tính
kidbằng RFC 7638 JWK Thumbprint: dựng JSON chỉ chứa các field bắt buộc theo đúng thứ tự chữ cái (crv,kty,x), không khoảng trắng, băm SHA-256, encode base64url. - Dựng JWKS directory document — thứ sẽ được host tại well-known path.
- Dựng signature base: mỗi component được ký nằm trên một dòng dạng
"<tên component>": <giá trị>, dòng cuối cùng luôn là"@signature-params"chứa danh sách component đã ký cộng các tham sốcreated/expires/keyid/alg/tag. - Ký chuỗi signature base đó bằng Ed25519, base64-encode kết quả, nhét vào header
Signature. - Tự verify lại bằng public key để chắc không viết sai công thức.
Output thật, chạy trên chính sandbox đang nộp bài viết này:
--- JWK thumbprint input (RFC 7638, lexicographic member order) ---
{"crv":"Ed25519","kty":"OKP","x":"4ih7KLTDgG36sO2nMOIYGx9MOG_VV0ZgsgM49sgkDWM"}
kid = 5P39kFbYHhPPrzvjlBTQwbj2D2bVYg53OUMyVgdHIDc
--- JWKS directory document ---
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"x": "4ih7KLTDgG36sO2nMOIYGx9MOG_VV0ZgsgM49sgkDWM",
"kid": "5P39kFbYHhPPrzvjlBTQwbj2D2bVYg53OUMyVgdHIDc",
"use": "sig",
"nbf": 1788398089,
"exp": 1796174089
}
]
}
--- Signature base (RFC 9421 §2.5) ---
"@authority": beru.io.vn
"signature-agent": sig1="https://example-agent.test"
"@signature-params": ("@authority" "signature-agent");created=1788398089;expires=1788398389;keyid="5P39kFbYHhPPrzvjlBTQwbj2D2bVYg53OUMyVgdHIDc";alg="ed25519";tag="web-bot-auth"
--- Resulting request headers ---
Signature-Agent: sig1="https://example-agent.test"
Signature-Input: sig1=("@authority" "signature-agent");created=1788398089;expires=1788398389;keyid="5P39kFbYHhPPrzvjlBTQwbj2D2bVYg53OUMyVgdHIDc";alg="ed25519";tag="web-bot-auth"
Signature: sig1=:/kS5OFFZmRFgHULiQaiM1pEnf6+RXtRzSrorWFtok+1MpGnjU1k1AwIbNJPLLx7jwMlq7be73wDEUPAEQYW2BQ==:
Self-verification (crypto.verify with the public key): true
Vài điểm cần nói rõ để không ai đọc xong đi triển khai production dựa nguyên bài này. Thứ nhất, đây là bản minh hoạ tối giản — tôi chỉ ký hai component (@authority và signature-agent) để giữ ví dụ ngắn, một bot thật thường ký thêm @target-uri hoặc method tuỳ chính sách server yêu cầu. Thứ hai, tôi không serialize Signature-Input qua đúng bộ quy tắc Structured Field đầy đủ của RFC 8941 (dấu cách, escaping tham số) — tôi dựng chuỗi tay để bám sát ví dụ trong draft, một thư viện production nên dùng package structured-field/http-message-signatures đã test kỹ thay vì tự ghép chuỗi như tôi làm ở đây. Thứ ba, expires tôi để 5 phút cho ví dụ chạy nhanh, không phải khuyến nghị — draft ghi SHOULD ≤ 24 giờ, không phải 5 phút.
Đem well-known path này ra test trên beru.io.vn: 404
Nói thì dễ, hạ tầng thật của beru.io.vn có sẵn sàng cho cơ chế này chưa? Tôi fetch trực tiếp:
https://beru.io.vn/.well-known/http-message-signatures-directory → 404 Not Found.
Nghĩa là: tính tới ngày viết bài, beru.io.vn không public JWKS ở địa chỉ mà spec vừa chuẩn hoá — không có gì để verify, dù có bot nào đó gửi request ký kèm Signature-Agent trỏ về đây. Đây không phải điều bất ngờ (-00 mới ra được hai ngày), nhưng đáng ghi lại như một mốc: nếu vài tháng nữa tôi hoặc ai đó quay lại kiểm tra, đây là điểm xuất phát thật, không phải suy đoán.
Ngược lại, .well-known/ không hoàn toàn trống. https://beru.io.vn/.well-known/api-catalog trả về một linkset JSON thật, trỏ tới ba tài nguyên: OpenAPI spec tại /api/openapi.json (media type application/openapi+json), trang docs HTML tại /api-docs, và endpoint health-check tại /api/health. Bản thân OpenAPI spec đó (title: "Beru Portfolio Webhook API", version 1.0.0) liệt kê 12 nhóm path — trong đó /api/webhook/blog chính là nơi tool đang nộp bản nháp bài viết này gọi vào. Tức là kênh "cho agent tự khám phá API" đã có, chỉ riêng kênh "cho agent tự chứng minh danh tính bằng chữ ký" theo chuẩn webbotauth thì chưa.
robots.txt của beru.io.vn cũng đáng nhắc: nó liệt kê rõ từng bot theo tên — ClaudeBot, Claude-User, GPTBot, ChatGPT-User, PerplexityBot, Bingbot — và cho tất cả cùng một mức quyền truy cập như crawler thường, chặn /admin/ và /api/, cho phép rõ ràng /api/openapi.json và toàn bộ /.well-known/. Đây vẫn là mô hình "biết tên là cho qua" — đúng cơ chế mà Web Bot Auth sinh ra để thay thế, vì tên trong robots.txt chỉ so khớp User-Agent, không xác minh được request có thật sự đến từ chủ sở hữu cái tên đó hay không.
Một chi tiết phụ nhưng xác nhận được công khai: trang chủ beru.io.vn có đường dẫn /cdn-cgi/l/email-protection trong HTML — đây là tính năng che email của Cloudflare, nghĩa là site đang chạy sau Cloudflare edge. Tôi không kiểm chứng được (và không thể kiểm chứng từ bên ngoài) gói dịch vụ Cloudflare đang dùng có bao gồm Bot Management / Verified Bots Program hay không — cái đó cần vào dashboard Cloudflare mới biết, tôi không có quyền truy cập đó nên không khẳng định. Điều duy nhất chắc chắn là: hạ tầng biên đã là Cloudflare, nên về mặt lý thuyết, việc bật tính năng verify chữ ký (nếu gói dịch vụ hỗ trợ) không đòi hỏi đổi nhà cung cấp CDN.
Một chỗ hai nguồn viết lệch nhau đúng một chữ
Trong lúc đối chiếu, tôi bắt được một điểm hai nguồn không khớp. Bài blog Cloudflare (1/7/2025) viết: "Host a JSON web key set containing your public key under /.well-known/http-message-signature-directory of your website" — số ít, signature. Còn mục IANA registration trong draft -00 (1/9/2026) đăng ký well-known URI suffix là http-message-signatures-directory — số nhiều, signatures — khớp với registration table ghi rõ: URI Suffix = http-message-signatures-directory, Change Controller = IETF, Reference = chính tài liệu này, Status = permanent.
Đây đúng kiểu lỗi mà một request thật sẽ dính nếu triển khai theo bài blog cũ: gõ nhầm một chữ "s" thôi là verify sẽ không tìm thấy key directory, vì domain, path, chữ hoa/thường trong URI đều so khớp chính xác từng ký tự. Tôi không đi xa hơn để khẳng định Cloudflare hiện tại (production, không phải bài blog 2025) đang dùng path nào — cần vào chính hạ tầng Cloudflare mới kiểm được, việc đó ngoài khả năng của tôi từ bên ngoài. Điều tôi kiểm chứng được và dám khẳng định: hai tài liệu công khai, tự nhận là mô tả cùng một cơ chế, hiện đang viết khác chữ nhau, và bản IETF -00 là bản mới hơn, có tư cách chuẩn hoá cao hơn (registered với IANA, thay vì một bài blog).
Ai đã chạy thật, và trễ bao lâu so với kế hoạch
Cloudflare là bên triển khai production sớm nhất, qua Verified Bots Program, với quy trình xác minh 8 bước ở edge: kiểm header bắt buộc có mặt, khớp keyid với khoá đã biết, request chưa hết hạn (chống replay), có đúng tag web-bot-auth, trích các component đã ký, dựng lại signature base để so sánh, verify bằng thuật toán Ed25519, rồi gắn nhãn traffic đã verify. Điều kiện để một bot được tham gia: tự sinh khoá ký, host JWKS ở well-known path, chứng minh sở hữu khoá qua một request đã ký, và đăng ký với Cloudflare.
Google tham gia muộn hơn và rõ ràng ở giai đoạn thử nghiệm — tài liệu dev docs Google công bố 6/5/2026 dùng nguyên câu "testing the protocol with some AI agents hosted on Google infrastructure" với danh tính thử nghiệm https://agent.bot.goog, và tự ghi thêm giới hạn: "not all Google user agents are using Web Bot Auth, and Google is not yet signing every request of agents using the protocol." Tức là ngay chính Google, một trong hai đồng tác giả của draft -00, cũng chưa ký toàn bộ traffic bot của mình theo chuẩn này — chỉ một phần, và tự nhận là experimental.
Còn về tiến độ chính thức: charter của working group webbotauth đặt mốc standards-track spec vào tháng 4/2026 và một Best Current Practice vận hành vào tháng 8/2026. Bản -00 — bản WG document đầu tiên, chưa phải bản cuối càng chưa phải RFC — tới 1/9/2026 mới xuất hiện, tức trễ khoảng năm tháng so với mốc spec ban đầu, và ngay sau cả mốc BCP tháng 8. Đây là khoảng cách giữa lịch trình một charter IETF đặt ra và tốc độ một working group thật sự đi — không phải điều gì bất thường trong quy trình chuẩn hoá, nhưng đáng để bất kỳ ai định "chờ chuẩn ổn định rồi làm" biết trước: nếu tính theo mốc charter, "ổn định" lẽ ra đã phải xảy ra từ nửa năm trước.
Web Bot Auth không thay thế MCP OAuth — chúng đứng ở hai lớp khác nhau
Dễ nhầm lẫn hai thứ này vì cả hai đều gắn với "xác thực agent". Nhưng chúng giải quyết hai bài toán khác tầng. Web Bot Auth trả lời câu hỏi ở lớp HTTP transport, khi một crawler/agent đang đọc nội dung công khai của một trang: "request GET này tới /blog/... có thật sự tới từ cái danh tính nó tự xưng không?" — không có khái niệm "đăng nhập", không có scope, không có refresh token, chỉ có chữ ký trên từng request và một well-known key directory để verify.
Còn OAuth — cơ chế mà một MCP server dùng để cấp quyền cho agent gọi tool (kiểu cái tool đang nộp chính bài viết này) — trả lời câu hỏi khác hẳn: "cái agent đang cầm access token này được phép gọi hành động ghi nào, với quyền hạn gì, do ai cấp, hết hạn khi nào?" Đây là bài toán authorization có trạng thái, có scope, có khả năng thu hồi — thứ chữ ký request đơn lẻ của Web Bot Auth không làm được và cũng không định làm.
Nói cách khác: một agent hoàn toàn có thể vừa ký request GET tới /blog/bai-viet bằng Web Bot Auth (để beru.io.vn biết "đúng là con bot X đang đọc trang này"), vừa cầm OAuth token riêng để gọi POST /api/webhook/blog (để tạo bản nháp mới) — hai cơ chế xác thực độc lập, phục vụ hai hành vi khác nhau: đọc nội dung công khai so với thực thi hành động có quyền ghi. Một trang muốn agent-friendly toàn diện, về lý thuyết, cần cả hai lớp chứ không phải chọn một.
Vậy nếu beru.io.vn muốn thử — cần gì, và cái gì tôi chưa dám nói chắc
Về mặt lý thuyết đọc từ spec, để một site tự host như beru.io.vn verify được request đã ký (khác với việc tự ký request đi crawl chỗ khác), site cần: một endpoint kiểm tra Signature-Agent/Signature-Input/Signature trên request tới, cơ chế fetch và cache JWKS từ well-known path của bên gửi (có tôn trọng nbf/exp của từng key), verify chữ ký theo đúng công thức signature base ở trên, và một cách để quyết định làm gì khi verify thất bại — chặn cứng, hay chỉ gắn nhãn "unverified" như cách robots.txt hiện tại đang phân biệt các bot theo tên.
Nhưng đây là ranh giới tôi dừng lại: tôi không biết, và không có cách nào biết từ bên ngoài, code CMS Next.js + Prisma của beru.io.vn hiện xử lý request tới theo middleware nào, có đang parse Structured Field header hay không, hay gói Cloudflare đang dùng có bật Bot Management hay không. Viết ra một khẳng định như "beru.io.vn có thể bật Verified Bots trong 10 phút" sẽ là bịa — tôi không SSH được vào server, không có quyền vào dashboard Cloudflare của domain này. Điều tôi kiểm chứng được, và chỉ dừng ở đó, là ba việc: (1) draft -00 tồn tại thật, ngày 1/9/2026, đọc được toàn văn qua IETF datatracker; (2) công thức ký/verify trong draft chạy đúng thật với Ed25519 chuẩn Node.js, tự ký tự verify ra true; (3) well-known path mà spec yêu cầu, tính tới lúc viết bài, trả về 404 trên beru.io.vn thật.
Vì sao chữ ký request lại khó giả hơn IP hay User-Agent
Đáng dừng lại phân tích kỹ hơn một chút, vì đây là lý do cả Cloudflare lẫn Google cùng đặt cược vào cơ chế này thay vì tiếp tục vá IP allowlist. Với IP allowlist, một trang blocklist/allowlist theo dải IP do bên phát hành công bố (ví dụ OpenAI công bố dải IP cho GPTBot). Vấn đề nằm ở chỗ: (a) danh sách đó phải fetch định kỳ và trang web phải tự tin nó luôn cập nhật đúng lúc; (b) một bot đứng sau proxy hoặc NAT dùng chung địa chỉ với dịch vụ khác sẽ khiến allowlist theo IP trở thành allowlist theo "cả cụm hạ tầng", không riêng gì bot đó; (c) hoàn toàn không có gì ngăn một actor khác chiếm được (hoặc thuê được) một địa chỉ IP từng nằm trong dải đã công bố, sau khi bên phát hành đã trả IP đó lại cho nhà cung cấp cloud.
Chữ ký số không có ba vấn đề đó. Verify không phụ thuộc việc "IP này có nằm trong danh sách mới nhất hay không" mà phụ thuộc việc "chữ ký này có đúng là được tạo bởi private key tương ứng với public key đang công bố hay không" — một phép toán, không phải một bảng tra cứu cần đồng bộ liên tục. Muốn giả mạo, kẻ tấn công buộc phải có được chính private key, không phải chỉ chiếm được một địa chỉ IP hay gõ đúng một chuỗi User-Agent. Đánh đổi là chi phí vận hành: bot giờ phải giữ khoá, ký từng request (tốn CPU, dù Ed25519 rất rẻ), xoay vòng khoá định kỳ, và publish JWKS đúng chuẩn — nặng hơn hẳn so với việc chỉ gắn sẵn một chuỗi User-Agent vào request.
Đọc lại đặc tả: những chỗ chưa chốt, đọc kỹ mới thấy
Một vài góc draft -00 cố tình để mở, đáng nêu vì chúng là những chỗ implementer sẽ phải tự quyết định, không có câu trả lời sẵn trong spec. Thứ nhất, spec không bắt buộc một thuật toán ký duy nhất — chỉ liệt kê Ed25519 và RSA-PSS-SHA512 làm ví dụ, cấm HMAC (khoá đối xứng) chứ không ép tất cả implementer dùng chung một thuật toán. Nghĩa là hai bot tuân thủ spec vẫn có thể dùng hai kiểu khoá khác nhau, và verifier bắt buộc phải hỗ trợ được nhiều thuật toán cùng lúc thay vì hardcode một loại.
Thứ hai, phần nonce: draft nói thẳng "this document defines no additional nonce requirements" ngoài những gì RFC 9421 §7.2.2 đã có — tức webbotauth không tự sáng chế thêm cơ chế chống replay riêng, dựa hoàn toàn vào cửa sổ created/expires cộng cơ chế nonce chung của RFC 9421. Với cửa sổ hiệu lực khuyến nghị dưới 24 giờ (SHOULD, không phải MUST), một request bị lộ có thể bị replay trong đúng khung thời gian đó nếu verifier không tự thêm cơ chế chặn trùng lặp riêng — spec để việc này tuỳ ý verifier, không bắt buộc.
Thứ ba, ba kiểu type cho Signature-Agent (directory, jwks_uri, cimd) mở ra ba cách khác nhau để một verifier tìm khoá: lấy trực tiếp từ well-known directory mặc định, trỏ tới một jwks_uri tuỳ ý không theo chuẩn well-known, hoặc dùng CIMD — Client ID Metadata Document, cùng khái niệm CIMD mà OAuth cho agent (DCR thay thế) đang hướng tới. Có nghĩa hai hướng chuẩn hoá — xác thực request theo Web Bot Auth và xác thực client theo OAuth cho agent — đang bắt đầu dùng chung một khái niệm định danh (CIMD) dù vẫn là hai spec riêng biệt, hai working group riêng biệt.
Kết luận: chuẩn còn draft, nhưng công thức đã chạy được thật
Tính tới thời điểm viết bài (3/9/2026), Web Bot Auth chưa phải RFC — nó là draft-ietf-webbotauth-httpsig-protocol-00, hết hạn 5/3/2027 nếu không có bản mới, và bản thân working group đã trễ nửa năm so với mốc charter tự đặt ra. Nhưng khác với nhiều "chuẩn tương lai" chỉ tồn tại trên giấy, cơ chế ký/verify ở đây chạy thật, chạy được bằng crypto có sẵn trong Node.js không cần thư viện ngoài, và đã có ít nhất một nhà cung cấp (Cloudflare) verify nó ở edge từ tháng 7/2025 — trước cả khi IETF có tài liệu chính thức. Với beru.io.vn, khoảng cách hiện tại rất cụ thể và đo được: một well-known path trả 404, một robots.txt vẫn xác thực bot bằng tên gọi, và một hạ tầng Cloudflare đã sẵn ở biên nhưng chưa rõ có bật tính năng cần thiết hay không. Ba việc rời rạc đó là toàn bộ những gì tôi dám khẳng định hôm nay — phần còn lại, tôi để ngỏ đúng như spec đang để ngỏ.
Tham khảo kỹ thuật liên quan: nếu bạn đang tự host một CMS như CMS webhook API của beru.io.vn hoặc vận hành server riêng, phần cấu hình reverse proxy và pipeline deploy để thử nghiệm những middleware xác thực kiểu này nằm trong phạm vi dịch vụ DevOps & Server Management mà tôi mô tả trong trang dịch vụ. Một ví dụ thực tế về site catalog sản phẩm B2B có lượng truy cập từ crawler/agent đáng kể — nơi việc phân biệt bot thật với bot giả có ý nghĩa vận hành thật — là dự án DJI Enterprise Vietnam mà tôi từng làm.
Những gì tôi KHÔNG kiểm chứng được, và vì sao:
- Gói dịch vụ Cloudflare mà beru.io.vn đang dùng có bật Bot Management / Verified Bots Program hay không — cần quyền truy cập dashboard Cloudflare của domain, tôi không có.
- CMS Next.js + Prisma của beru.io.vn hiện có middleware nào xử lý header
Signature-Agent/Signature-Input/Signaturehay không — tôi không có quyền đọc source code phía server, chỉ kiểm được qua request công khai (và request công khai cho kết quả 404 ở well-known path liên quan). - Cloudflare hiện tại (production, không phải bài blog 2025) đang thực sự dùng well-known path số ít hay số nhiều — bài blog viết số ít, spec IETF mới nhất đăng ký số nhiều; tôi không có cách kiểm chứng từ bên ngoài path nào Cloudflare thật sự đang lắng nghe.
- Có bao nhiêu phần trăm traffic AI crawler thật (GPTBot, ClaudeBot, PerplexityBot...) hiện đã hỗ trợ ký request theo chuẩn này — ngoài phát biểu "testing… some AI agents… not yet signing every request" của Google, tôi không tìm được số liệu công khai nào từ Cloudflare hay OpenAI về tỷ lệ áp dụng thật.
- Mốc thời gian working group có đạt BCP (Best Current Practice) đúng hạn hay không — charter ghi tháng 8/2026 cho BCP, nhưng chính spec chính (
-00) còn chưa xong tính tới lúc viết bài, nên khả năng BCP đúng hạn tôi đánh giá là thấp, nhưng đây là suy đoán của tôi, không phải sự kiện đã xảy ra.
Viết và kiểm chứng bởi Nguyễn Hoàng Lộc (Beru), TP. Hồ Chí Minh
