Một request có cookie hợp lệ chưa chắc xuất phát từ giao diện của bạn. Trình duyệt có thể gửi request từ form, ảnh, iframe hoặc script trên website khác và tự đính kèm credential theo chính sách cookie. Fetch Metadata bổ sung các header Sec-Fetch-* để server biết request được tạo trong ngữ cảnh nào trước khi chạy logic nghiệp vụ.
Bằng một Resource Isolation Policy nhỏ ở middleware hoặc reverse proxy, ứng dụng có thể từ chối nhiều request cross-site không hợp lệ từ sớm. Đây là lớp phòng thủ bổ sung cho CSRF, XSSI và rò rỉ cross-origin; nó không thay thế CSRF token, SameSite cookie, CORS, xác thực hay phân quyền.
1. Fetch Metadata trả lời những câu hỏi nào?
Đặc tả Fetch Metadata Request Headers của W3C định nghĩa các tín hiệu do user agent gửi để server quyết định có phục vụ request hay không:
| Header | Câu hỏi | Giá trị thường gặp |
|---|---|---|
Sec-Fetch-Site | Quan hệ giữa initiator và đích? | same-origin, same-site, cross-site, none |
Sec-Fetch-Mode | Request chạy ở mode nào? | navigate, cors, no-cors, same-origin, websocket |
Sec-Fetch-Dest | Tài nguyên sẽ được dùng làm gì? | document, image, script, style, empty… |
Sec-Fetch-User | Navigation có user activation? | ?1 khi có tín hiệu phù hợp |
Các tên bắt đầu bằng Sec- thuộc nhóm header mà JavaScript phía trang không được tùy ý sửa. Vì vậy một website tấn công không thể đơn giản gọi fetch() rồi tự gắn Sec-Fetch-Site: same-origin. Tuy nhiên, client ngoài trình duyệt vẫn có thể tự tạo header; Fetch Metadata không phải cơ chế chứng thực client.
2. same-origin, same-site và none không đồng nghĩa
same-origin: scheme, host và port của initiator khớp với đích. Đây thường là tín hiệu mạnh nhất cho request nội bộ.same-site: khác origin nhưng vẫn cùng site theo quy tắc của trình duyệt, chẳng hạn một số subdomain cùng registrable domain.cross-site: request bắt nguồn từ site khác.none: navigation được user agent coi là do người dùng trực tiếp khởi tạo, như nhập URL hoặc mở bookmark.
Đừng mặc định coi mọi subdomain là đáng tin. Nếu người dùng có thể tạo nội dung ở tenant.example.com, nếu subdomain trỏ tới dịch vụ SaaS hoặc có nguy cơ takeover, cho phép toàn bộ same-site có thể mở lại đường tấn công. Với endpoint nhạy cảm, chính sách nên ưu tiên same-origin và chỉ thêm ngoại lệ có chủ đích.
3. Một Resource Isolation Policy tối thiểu
Chính sách phổ biến cho ứng dụng web hoạt động theo thứ tự:
- Cho phép request không có Fetch Metadata trong giai đoạn tương thích, nhưng đo lường chúng.
- Cho phép
same-origin; cân nhắcsame-sitetheo mô hình tin cậy subdomain. - Cho phép navigation
GEThợp lệ để liên kết từ website khác vẫn mở được trang. - Miễn trừ chính xác các endpoint vốn phải nhận traffic cross-site.
- Từ chối các trường hợp
cross-sitecòn lại trước khi chạy session và nghiệp vụ.
function allowByFetchMetadata(array $server, string $method, string $path): bool
{
$site = strtolower($server['HTTP_SEC_FETCH_SITE'] ?? '');
$mode = strtolower($server['HTTP_SEC_FETCH_MODE'] ?? '');
$dest = strtolower($server['HTTP_SEC_FETCH_DEST'] ?? '');
// Client cũ, app mobile, CLI và server-to-server có thể không gửi header.
if ($site === '') {
return true;
}
if ($site === 'same-origin') {
return true;
}
// Chỉ bật nếu toàn bộ subdomain cùng site đều đáng tin.
if ($site === 'same-site') {
return true;
}
// Giữ khả năng mở link cross-site trong tab chính.
if ($method === 'GET'
&& $mode === 'navigate'
&& !in_array($dest, ['object', 'embed'], true)) {
return true;
}
return false;
}
Middleware thực tế nên kiểm tra một tập giá trị đã biết. Đặc tả khuyến nghị bỏ qua header có giá trị không hợp lệ để tương thích với mở rộng tương lai; không nên biến một token mới thành sự cố diện rộng. Cách fail-open hay fail-closed cho giá trị lạ phải dựa trên độ nhạy endpoint và dữ liệu quan sát được.
4. Đặt middleware đúng vị trí
Kiểm tra Fetch Metadata nên chạy càng sớm càng tốt: sau lớp chuẩn hóa host/TLS cần thiết nhưng trước session, xác thực nặng, truy vấn database và tác vụ có side effect. Request bị chặn thường trả 403 Forbidden với body nhỏ, không tiết lộ endpoint có tồn tại hay trạng thái tài khoản.
if (!allowByFetchMetadata($_SERVER, $_SERVER['REQUEST_METHOD'], $path)) {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
echo '{"error":"request_context_not_allowed"}';
exit;
}
Ở kiến trúc nhiều dịch vụ, lớp edge có thể chặn phần lớn traffic, nhưng ứng dụng vẫn cần biết ngoại lệ nào thuộc route nào. Đừng dùng một regex đường dẫn rộng ở CDN rồi giả định mọi backend đều được bảo vệ. Policy nên nằm trong source control, có test và có owner.
5. Những endpoint cần ngoại lệ rõ ràng
| Loại endpoint | Vì sao khác biệt? | Cách xử lý |
|---|---|---|
| API CORS công khai | Được thiết kế để frontend origin khác gọi | Allowlist route và origin; vẫn áp dụng CORS, auth, rate limit |
| Webhook | Server bên thứ ba không gửi Fetch Metadata | Xác minh chữ ký, timestamp, replay và idempotency |
| OAuth/SAML callback | Luồng đăng nhập đi qua site ngoài | Allowlist callback; xác minh state, PKCE hoặc assertion |
| Ảnh/font nhúng công khai | Có chủ đích được tải cross-site | Miễn trừ tài nguyên tĩnh cụ thể; không để lẫn dữ liệu riêng tư |
| Payment return/callback | Navigation hoặc POST có thể đến từ cổng thanh toán | Thiết kế route riêng và xác minh giao dịch phía server |
Ngoại lệ nên theo route hoặc capability, không theo chuỗi con mơ hồ. Ví dụ, miễn trừ /public/assets/ an toàn hơn miễn trừ mọi URL chứa từ “public”. Mỗi ngoại lệ cần lý do, test và ngày rà soát.
6. Vì sao không được bỏ CSRF token và SameSite?
Fetch Metadata chỉ xuất hiện ở user agent hỗ trợ và trong ngữ cảnh phù hợp. Mobile app, bot, CLI, WebView, proxy đặc biệt hoặc browser cũ có thể không gửi header. Nếu policy cho phép request thiếu header để giữ tương thích, kẻ tấn công ngoài browser cũng có thể gửi request thiếu header.
- CSRF token chứng minh form/request có giá trị bí mật gắn với phiên hoặc hành động.
- SameSite cookie kiểm soát lúc browser đính kèm cookie trong ngữ cảnh cross-site.
- Origin/Referer validation cung cấp tín hiệu nguồn cho các request cần kiểm tra.
- Fetch Metadata mô tả ngữ cảnh request để chặn sớm và bao phủ thêm nhiều kiểu resource load.
Các lớp này bổ sung nhau. CORS cũng không phải CSRF defense tổng quát: một cross-site form submission “simple request” có thể được gửi dù JavaScript phía tấn công không đọc được response.
7. Cache và header Vary
Nếu cùng một URL trả response khác nhau dựa trên Sec-Fetch-Site, Sec-Fetch-Mode hoặc Sec-Fetch-Dest, cache phải biết các biến thể đó. Đặc tả lưu ý dùng Vary thích hợp, chẳng hạn:
Vary: Accept-Encoding, Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest
Nếu không, một response 403 cho cross-site request có thể bị cache và phục vụ nhầm cho same-origin request, hoặc ngược lại. Tuy nhiên, thêm quá nhiều chiều Vary làm giảm hit ratio. Với endpoint động có credential, thường nên cấu hình cache riêng hoặc không shared-cache thay vì dựa vào một danh sách biến thể phức tạp.
8. Quan sát trước khi enforce
Fetch Metadata không có response header “Report-Only” chuẩn như CSP. Chế độ quan sát phải được xây trong middleware: tính quyết định dự kiến, ghi metric, nhưng vẫn cho request đi tiếp.
fetch_metadata_decision_total{
decision="would_block",
site="cross-site",
route_group="account_write",
method="POST"
}
Không đưa URL đầy đủ, cookie, query chứa token hay dữ liệu cá nhân vào nhãn metric. Nhãn có cardinality cao sẽ làm hệ thống quan sát tốn kém. Nên nhóm route theo capability và log có sampling với request ID.
Hướng dẫn triển khai Fetch Metadata của web.dev đề xuất bật logic ở chế độ chỉ ghi nhận, xử lý ngoại lệ hợp lệ rồi mới enforce. Một lộ trình an toàn:
- Quan sát 100% traffic trong ít nhất một chu kỳ sử dụng bình thường.
- Phân loại client thiếu header và các route nhận cross-site hợp lệ.
- Enforce trước cho endpoint ghi nhạy cảm có traffic trình duyệt rõ ràng.
- Mở rộng theo route group; giữ kill switch có thời hạn.
- Cảnh báo khi tỷ lệ block hoặc missing-header thay đổi đột ngột.
9. Bộ test phải bao phủ ngữ cảnh, không chỉ URL
| Kịch bản | Kỳ vọng |
|---|---|
| XHR/fetch same-origin tới API | Cho phép |
| POST form từ site tấn công tới endpoint ghi | Chặn trước nghiệp vụ |
| Ảnh cross-site trỏ vào JSON riêng tư | Chặn |
| Người dùng mở link GET từ site khác | Cho phép navigation phù hợp |
| Request từ client không có Fetch Metadata | Theo policy tương thích đã công bố |
| OAuth callback/webhook được miễn trừ | Đi qua rồi được cơ chế chuyên biệt xác minh |
| Giá trị header mới hoặc không hợp lệ | Không gây lỗi 500; hành vi đúng chiến lược tương thích |
Test tích hợp nên chạy trong browser thật để browser tự sinh header. Unit test với header tự dựng chỉ kiểm tra middleware, không chứng minh hành vi end-to-end. Đồng thời thử redirect chain, vì Sec-Fetch-Site có thể chuyển thành cross-site khi chuỗi redirect đi qua site khác.
10. Checklist production
- Đã xác định subdomain nào đáng tin; không mặc định cho phép
same-site. - Đã lập danh sách hữu hạn cho CORS API, webhook, callback đăng nhập/thanh toán và tài nguyên nhúng.
- Policy chạy trước xử lý tốn kém và side effect, trả lỗi nhỏ không rò thông tin.
- CSRF token, SameSite, auth, authorization và rate limit vẫn được giữ nguyên.
- Đã quyết định rõ cách xử lý request thiếu header và client ngoài browser.
- Cache có
Varyphù hợp hoặc endpoint không shared-cache. - Đã quan sát, canary, kiểm thử browser và chuẩn bị rollback có kiểm soát.
Fetch Metadata hiệu quả nhất khi được xem là một bộ tín hiệu để thu hẹp bề mặt request, không phải bằng chứng danh tính. Một policy nhỏ, route-aware và triển khai theo dữ liệu có thể chặn traffic cross-site vô nghĩa trước khi chạm vào ứng dụng, đồng thời giữ nguyên các lớp bảo vệ nền tảng cho những trường hợp header vắng mặt hoặc ngoại lệ hợp lệ.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.