Nền tảng An toàn Web & Cơ chế Trình duyệt
8/20
Bài 8 / 20~13 phútMô hình Bảo mật Trình duyệtMiễn phí… lượt xem

Same-Origin Policy: Trụ cột bảo mật trình duyệt web

Giải phẫu ranh giới Origin tuple (Protocol, Host, Port) và cách Same-Origin Policy kiểm soát đọc, ghi, nhúng tài nguyên giữa các trang web.

TL;DR: Same-Origin Policy (SOP) là cơ chế bảo mật quan trọng nhất của mọi trình duyệt web hiện đại, quy định rằng script chạy trong một origin chỉ được phép truy cập tài nguyên của chính origin đó. Một origin được xác định nghiêm ngặt bởi bộ ba: giao thức (protocol), tên miền (host) và cổng mạng (port). SOP phân biệt rạch ròi giữa việc gửi request (cross-origin write) và việc đọc nội dung phản hồi (cross-origin read). Hiểu đúng quy tắc này giúp bạn giải thích được vì sao kẻ tấn công có thể kích hoạt hành động nhưng không thể đọc trộm dữ liệu trừ khi hệ thống có cấu hình sai lệch.

Hãy tưởng tượng bạn đang đăng nhập vào trang ngân hàng điện tử https://bank.example.com trên một tab trình duyệt, và cùng lúc đó mở một trang web đọc truyện lạ http://truyen-hay.xyz ở tab bên cạnh. Nếu không có Same-Origin Policy, mã JavaScript chạy trên tab đọc truyện hoàn toàn có thể gửi một câu lệnh fetch('https://bank.example.com/api/so-du'), đọc sạch số dư tài khoản của bạn, và thậm chí điều khiển DOM của tab ngân hàng để tự động chuyển tiền. SOP chính là bức tường thép cô lập hai thế giới đó trên cùng một trình duyệt.

1. Ranh giới Origin Tuple được định nghĩa chính xác ra sao?

Trình duyệt xác định nguồn gốc của một tài nguyên dựa trên Origin Tuple gồm 3 phần: Origin = (Protocol, Host, Port)

Chỉ cần một trong ba thành phần này có sự sai khác, hai trang web sẽ bị coi là khác nguồn gốc (Cross-Origin) và chịu sự kiểm soát nghiêm ngặt của SOP.

Giả sử trang web hiện tại của bạn là https://example.com:443:

URL ĐíchTrạng tháiLý do
https://example.com/dashboardCùng OriginTrùng Protocol, Host và Port (443 ngầm định)
https://example.com:8443/apiKhác OriginKhác Port (443 vs 8443)
http://example.comKhác OriginKhác Protocol (HTTPS vs HTTP)
https://api.example.comKhác OriginKhác Host (Subdomain khác nhau)
https://example.orgKhác OriginKhác Tên miền (Top-Level Domain)

Ranh giới Origin Tuple: Protocol + Host + Port

Sau đây là câu hỏi dự đoán phản xạ nhanh về ranh giới Origin:

Dự đoán hành vi (Predict): So sánh Origin

Một script chạy tại 'https://mybank.com' thực hiện lệnh AJAX fetch() tới 'https://mybank.com:443/api/profile'. Trình duyệt có áp dụng kiểm tra Same-Origin Policy chặn request này không?

2. Same-Origin Policy cho phép ghi nhưng cấm đọc như thế nào?

Một trong những hiểu lầm tai hại nhất của lập trình viên là nghĩ rằng SOP sẽ ngăn cản trình duyệt gửi request tới domain khác.

Quy tắc thực tế của SOP:

  1. Cross-Origin Writes thường được cho phép: Trình duyệt sẵn sàng gửi một HTTP request tới bất kỳ domain nào (qua form submission, thẻ link hoặc AJAX call).
  2. Cross-Origin Reads bị chặn hoàn toàn: Trình duyệt cấm mã JavaScript của trang web hiện tại đọc được phản hồi trả về từ server của domain khác, trừ khi server đó bật CORS cho phép.
JavaScript
// Executed on attacker.com domain
fetch("https://bank.example.com/api/transfers", {
  method: "POST",
  body: JSON.stringify({ to: "attacker", amount: 1000 }),
  credentials: "include"
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.log("SOP blocked read access:", err));

Trong ví dụ trên, lệnh chuyển tiền vẫn bay tới server ngân hàng và server vẫn có thể xử lý thành công. Nhưng khi server trả về dữ liệu JSON, trình duyệt sẽ giấu biến kết quả đi và ném ra lỗi trong JavaScript của trang attacker.

Thử ngẫmNếu server xử lý hành động trước khi trình duyệt nhận được kết quả, thì việc trình duyệt giấu kết quả đi có bảo vệ được người dùng khỏi mất tiền hay không?

3. Vì sao nhúng tài nguyên thẻ img hay script lại không vi phạm SOP?

Web được xây dựng dựa trên sự liên kết mở. Nếu một trang web không thể tải ảnh từ CDN hay nhúng font từ Google Fonts, toàn bộ hệ sinh thái web sẽ sụp đổ. Do đó, SOP cho phép Cross-Origin Embedding:

  • Thẻ <img src="https://other.com/photo.jpg">: Cho phép hiển thị ảnh lên màn hình, nhưng JavaScript không được đọc trực tiếp mảng pixel của ảnh qua Canvas (trừ khi có thuộc tính crossorigin).
  • Thẻ <script src="https://cdn.com/lib.js">: Cho phép tải và thực thi mã JavaScript trong ngữ cảnh của trang hiện tại, nhưng không thể đọc mã nguồn thô của file như văn bản.
  • Thẻ <link rel="stylesheet" href="...">: Cho phép nạp CSS định kiểu giao diện.

Quy tắc này tạo ra ranh giới: Hiển thị thì được, nhưng đọc nội dung thô bên trong thì cấm.

4. Tấn công Cross-Site Request Forgery (CSRF) lợi dụng kẽ hở nào của SOP?

Chính vì Same-Origin Policy cho phép gửi request đi (Cross-Origin Write) kèm theo cookie tự động của trình duyệt mà không ngăn cản ở tầng mạng, kẻ tấn công có thể xây dựng một trang web độc hại chứa form tự động submit tới ngân hàng:

HTML
<!-- Trang độc hại tại evil.com -->
<form id="csrfForm" action="https://bank.example.com/api/transfer" method="POST">
  <input type="hidden" name="recipient" value="hacker" />
  <input type="hidden" name="amount" value="50000000" />
</form>
<script>
  document.getElementById("csrfForm").submit();
</script>

Khi nạn nhân đang đăng nhập ngân hàng mà vô tình bấm vào link của kẻ tấn công:

  1. Trình duyệt tự động mở form và gửi POST request tới bank.example.com.
  2. Trình duyệt tự động đính kèm Cookie phiên đăng nhập của nạn nhân.
  3. Server ngân hàng không kiểm tra token chống CSRF nên thực thi lệnh chuyển tiền thành công.
  4. Kẻ tấn công không cần đọc kết quả response; mục đích phá hoại đã hoàn thành.
🐛 Tìm bug trong code này
// Subdomain data-sharing configuration
// Runs on admin.corp.internal
document.domain = "corp.internal";

// Runs on public-forum.corp.internal
document.domain = "corp.internal";

5. Ranh giới DOM giữa các window, iframe và tab được cô lập ra sao?

Không chỉ bảo vệ ở tầng mạng (Network Requests), Same-Origin Policy còn bảo vệ cây DOM (Document Object Model):

  • Khi bạn dùng JavaScript mở một cửa sổ mới: const popup = window.open('https://other.com').
  • Đối tượng popup được tạo ra trong bộ nhớ trình duyệt.
  • Bạn có thể điều khiển các hàm điều hướng cơ bản như popup.close() hoặc popup.location.href = '...'.
  • Nhưng nếu bạn thử đọc popup.document.body.innerHTML hoặc truy cập form đăng nhập của trang đó, trình duyệt sẽ ném ngay biệt lệ DOMException: Blocked a frame with origin from accessing a cross-origin frame.

Cơ chế này bảo vệ người dùng không bị đánh cắp thông tin thẻ tín dụng khi một website nhúng cổng thanh toán bên thứ ba vào thẻ <iframe>.

6. Tổng kết & Mô hình tư duy đúng về SOP

Same-Origin Policy là bức tường rào bảo vệ vững chãi nhất nhưng cũng bị hiểu nhầm nhiều nhất:

  • SOP là cơ chế phòng vệ phía Client, do trình duyệt thực thi để bảo vệ người dùng, không phải để bảo vệ server.
  • SOP cấm đọc lén dữ liệu (Read), nhưng vẫn cho phép gửi dữ liệu phá hoại (Write) nếu không có các biện pháp phòng chống CSRF.
  • Khi cần chia sẻ dữ liệu có kiểm soát giữa các origin khác nhau, Cross-Origin Resource Sharing (CORS) là giải pháp tiêu chuẩn được thiết kế để mở rộng ranh giới của SOP một cách an toàn.

7. Tự kiểm tra (Self-check)

Hãy củng cố các nguyên lý bảo mật trình duyệt cốt lõi của bạn qua các câu hỏi truy xuất chủ động dưới đây:

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Bộ ba Origin Tuple gồm những thành phần nào? Cho ví dụ hai URL khác origin chỉ vì một thành phần.
  2. Q2
    Tại sao Same-Origin Policy lại không ngăn chặn được các cuộc tấn công CSRF thông thường?
  3. Q3
    Cross-Origin Embedding cho phép những loại thẻ HTML nào hoạt động và giới hạn bảo mật của chúng là gì?
  4. Q4
    Điều gì xảy ra khi mã JavaScript trên trang A cố tình đọc document.cookie của trang B chạy trong iframe khác origin?
  5. Q5
    Vì sao việc hạ cấp thuộc tính document.domain ngày nay bị coi là một thói quen nguy hiểm?
  6. Q6
    Same-Origin Policy có bảo vệ được API server nếu một kẻ tấn công dùng cURL hoặc Python gửi request không?

Bài tiếp theo: CORS và những hiểu lầm tai hại

Bài này có giúp bạn hiểu bản chất không?

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Thảo luận & góp ý

Hỏi đáp về bài này

Bài tiếp theo

CORS và những hiểu lầm tai hại trong an toàn ứng dụng web