Bí Mật Sau Những Ký Tự Lạ Trong API Response Của Big Tech
Giới thiệu
Bạn có bao giờ mở DevTools, bắt một API response từ Google hay Messenger, rồi ngạc nhiên khi thấy phần đầu của dữ liệu trông như thế này không?
)]}'\n[{"id": 1, "name": "Alice"}, ...]
Hoặc thậm chí là một vòng lặp while(1); hay for(;;); ngay trước khi JSON bắt đầu?
Đây không phải bug. Đây là một kỹ thuật phòng thủ chủ động — được thiết kế để chống lại một lỗ hổng bảo mật có tên JSON Hijacking. Bài viết này sẽ giải thích từng bước cơ chế tấn công, cách các ông lớn công nghệ đối phó, và lý do tại sao họ vẫn duy trì kỹ thuật này cho đến ngày nay.
Nội dung chính
1. JSON Hijacking là gì?
JSON Hijacking là một kỹ thuật tấn công khai thác sự kết hợp giữa hai đặc điểm của trình duyệt:
- Same Origin Policy (SOP): Trình duyệt ngăn một trang web đọc dữ liệu từ một domain khác — nhưng chỉ áp dụng với
fetchhayXMLHttpRequest. - Thẻ
<script>là ngoại lệ: Trình duyệt luôn cho phép tải và thực thi JavaScript từ bất kỳ domain nào qua thẻ<script>, kể cả khi domain đó thuộc bên thứ ba.
Và đây chính là lỗ hổng.
2. Cơ chế tấn công — Step by Step
Bước 1: Nạn nhân đang đăng nhập Google
Người dùng đang mở sẵn tab Gmail, nghĩa là trình duyệt đang lưu cookie phiên đăng nhập Google.
Bước 2: Kẻ tấn công dụ nạn nhân vào trang mạo danh
Kẻ tấn công tạo một trang web có chứa đoạn code như sau:
<!-- Trang web của kẻ tấn công: evil.com -->
<script>
// Ghi đè hàm khởi tạo mảng trước khi JSON load
function Array() {
// "this" ở đây chính là mảng JSON vừa được parse
// Kẻ tấn công lấy được dữ liệu ngay tại đây
sendToAttacker(this);
}
</script>
<!-- Trình duyệt sẽ tự đính kèm cookie Google vào request này -->
<script src="https://mail.google.com/mail/feed/atom"></script>
Bước 3: Trình duyệt tự làm việc thay kẻ tấn công
Khi nạn nhân truy cập evil.com:
- Trình duyệt thấy thẻ
<script>trỏ đếnmail.google.com. - Vì nạn nhân đang đăng nhập Google, trình duyệt tự động đính kèm cookie vào request.
- Google trả về dữ liệu JSON — ví dụ một mảng danh sách email:
[{"subject": "...", "from": "..."}]. - Trình duyệt cố gắng thực thi mảng JSON này như JavaScript.
- Vì hàm
Array()đã bị ghi đè từ trước, kẻ tấn công nghe lén được toàn bộ dữ liệu.
Tóm lại: Kẻ tấn công không cần đánh cắp cookie. Chúng chỉ cần để trình duyệt của nạn nhân tự gọi API và tự nộp dữ liệu về.
3. Cách Google và các ông lớn phòng chống
Giải pháp rất thông minh: làm cho JSON không thể thực thi được khi tải qua thẻ <script>, trong khi trang web chính chủ vẫn đọc được bình thường.
Google — Chèn ký tự rác vào đầu
Google thêm chuỗi )]}'\n trước khi JSON bắt đầu:
)]}'\n
[{"id": 1, "email": "alice@gmail.com"}, ...]
Tại sao hiệu quả?
Khi kẻ tấn công tải URL này qua thẻ <script>, trình duyệt cố parse )]}'\n như JavaScript — và lập tức báo lỗi cú pháp (SyntaxError), dừng thực thi ngay lập tức. Dữ liệu phía sau không bao giờ được đọc.
Trang chính chủ đọc như thế nào?
// Client Google thực hiện
const response = await fetch("https://mail.google.com/mail/feed/atom");
const rawText = await response.text();
// Cắt bỏ phần "rác" ở đầu trước khi parse
const cleanJson = rawText.replace(")]}'\n", "");
const data = JSON.parse(cleanJson);
Log output ví dụ:
rawText: )]}'\n[{"id":1,"email":"alice@gmail.com"}]
cleanJson: [{"id":1,"email":"alice@gmail.com"}]
data: Array(1) [ { id: 1, email: "alice@gmail.com" } ]
Messenger / Canva — Vòng lặp vô tận
Facebook Messenger và Canva chọn cách khác: chèn for(;;); hoặc while(true); trước JSON.
for(;;);
{"t":"msg","payload":{"thread_id":"...","message":"..."}}
Tại sao hiệu quả?
Khi kẻ tấn công tải qua thẻ <script>, trình duyệt thực thi for(;;); — một vòng lặp chạy mãi mãi. Tab của kẻ tấn công bị đơ hoàn toàn và không bao giờ đọc được dữ liệu phía sau.
Trang chính chủ đọc như thế nào?
const response = await fetch("https://www.messenger.com/api/...");
const rawText = await response.text();
// Cắt bỏ "for(;;);" ở đầu
const cleanJson = rawText.replace("for(;;);", "");
const data = JSON.parse(cleanJson);
Log output ví dụ:
rawText: for(;;);{"t":"msg","payload":{"thread_id":"xyz"}}
cleanJson: {"t":"msg","payload":{"thread_id":"xyz"}}
data: { t: "msg", payload: { thread_id: "xyz" } }
4. So sánh hai kỹ thuật
| Tiêu chí | Google (ký tự rác) | Messenger/Canva (vòng lặp vô tận) |
|---|---|---|
| Cơ chế dừng | Lỗi cú pháp | Vòng lặp treo tab |
| Tốc độ phát hiện | Ngay lập tức | Ngay lập tức |
| Tác động lên kẻ tấn công | Script dừng lại | Tab bị đơ/crash |
| Độ "khó chịu" với kẻ tấn công | Trung bình | Cao hơn |
5. Tại sao vẫn dùng dù trình duyệt đã vá lỗi?
Các trình duyệt hiện đại (Chrome, Safari, Edge) đã vá lỗ hổng ghi đè Array Constructor từ hơn 10 năm trước. Vậy tại sao Google và Messenger vẫn giữ những đoạn mã này?
Câu trả lời nằm ở nguyên tắc Defense in Depth — Bảo mật nhiều lớp.
Không bao giờ đặt toàn bộ niềm tin vào một lớp bảo vệ duy nhất.
Có ba lý do thực tế:
Lý do 1: Người dùng vẫn dùng trình duyệt đời cũ
Smart TV, thiết bị IoT, trình duyệt cũ trên các thị trường đang phát triển — rất nhiều thiết bị chạy các phiên bản Chrome/WebKit từ 5–10 năm trước, chưa có bản vá này.
Lý do 2: Phòng thủ trước zero-day
Một lỗ hổng zero-day mới trong trình duyệt có thể làm sống lại kiểu tấn công tương tự. Kỹ thuật prefix này là lớp phòng thủ tồn tại độc lập với trình duyệt.
Lý do 3: Chi phí duy trì gần bằng 0
Thêm vài byte vào đầu response và một dòng replace() ở client không ảnh hưởng đến hiệu năng. Với chi phí gần bằng 0 như vậy, tại sao lại bỏ đi?
Tổng kết
JSON Hijacking là một minh chứng thú vị cho thấy cách một đặc điểm của trình duyệt (thẻ <script> luôn được phép cross-origin) có thể bị lợi dụng để phá vỡ mô hình bảo mật thông thường.
Những ký tự trông có vẻ "rác" ở đầu JSON của Google thực ra là một kỹ thuật phòng thủ tinh tế:
- Tấn công qua
<script>: Bị chặn ngay lập tức do lỗi cú pháp hoặc vòng lặp vô tận. - Truy cập hợp lệ qua Fetch API: Hoạt động bình thường sau khi cắt bỏ phần prefix.
- Nguyên tắc Defense in Depth: Không phụ thuộc hoàn toàn vào trình duyệt để bảo vệ người dùng.
Lần sau khi bạn thấy for(;;); hay )]}'\n trong một API response, hãy nhớ rằng đó không phải bug — đó là một lớp áo giáp được thiết kế rất cẩn thận.
Tài liệu tham khảo
- OWASP AJAX Security Cheat Sheet — Protect Against JSON Hijacking
- PortSwigger Research: JSON Hijacking for the Modern Web
- Phil Haack: JSON Hijacking (bài viết gốc giải thích lỗ hổng Array constructor)
- GitLab Docs: JSON Hijacking Check — định nghĩa và cách khắc phục
- Dev.to: Why Facebook's API starts with a for loop