Ngăn chặn CSRF, XSSI và rò rỉ thông tin trên nhiều nguồn gốc.
Tại sao bạn nên quan tâm đến việc tách biệt tài nguyên web?
Nhiều ứng dụng web dễ bị tấn công trên nhiều nguồn gốc như giả mạo yêu cầu trên nhiều trang web (CSRF), đưa tập lệnh trên nhiều trang web vào (XSSI), tấn công theo thời gian, rò rỉ thông tin trên nhiều nguồn gốc hoặc tấn công kênh bên thực thi dự đoán (Spectre).
Các tiêu đề yêu cầu Tìm nạp siêu dữ liệu cho phép bạn triển khai một cơ chế phòng thủ chuyên sâu mạnh mẽ – Chính sách cô lập tài nguyên – để bảo vệ ứng dụng của bạn khỏi những cuộc tấn công phổ biến trên nhiều nguồn gốc này.
Thông thường, các tài nguyên do một ứng dụng web nhất định hiển thị chỉ được chính ứng dụng đó tải chứ không phải các trang web khác. Trong những trường hợp như vậy, bạn không cần tốn nhiều công sức để triển khai Chính sách cô lập tài nguyên dựa trên tiêu đề yêu cầu Tìm nạp siêu dữ liệu, đồng thời bảo vệ ứng dụng khỏi các cuộc tấn công trên nhiều trang web.
Khả năng tương thích với trình duyệt
Tất cả công cụ trình duyệt hiện đại đều hỗ trợ tiêu đề yêu cầu Tìm nạp siêu dữ liệu.
Thông tin khái quát
Có thể xảy ra nhiều cuộc tấn công trên nhiều trang web vì web mở theo mặc định và máy chủ ứng dụng của bạn không thể dễ dàng tự bảo vệ khỏi hoạt động giao tiếp bắt nguồn từ các ứng dụng bên ngoài. Một cuộc tấn công xuyên nguồn gốc điển hình là giả mạo yêu cầu trên nhiều trang web (CSRF), trong đó kẻ tấn công lừa người dùng truy cập vào một trang web mà chúng kiểm soát, sau đó gửi một biểu mẫu đến máy chủ mà người dùng đã đăng nhập. Vì máy chủ không thể biết liệu yêu cầu có bắt nguồn từ một miền khác (trên nhiều trang web) hay không và trình duyệt tự động đính kèm cookie vào các yêu cầu trên nhiều trang web, nên máy chủ sẽ thay mặt người dùng thực hiện hành động mà kẻ tấn công yêu cầu.
Các cuộc tấn công trên nhiều trang web khác như chèn tập lệnh trên nhiều trang web (XSSI) hoặc rò rỉ thông tin trên nhiều nguồn gốc có bản chất tương tự như CSRF và dựa vào việc tải tài nguyên từ ứng dụng nạn nhân trong một tài liệu do kẻ tấn công kiểm soát và rò rỉ thông tin về ứng dụng nạn nhân. Vì các ứng dụng không thể dễ dàng phân biệt các yêu cầu đáng tin cậy với các yêu cầu không đáng tin cậy, nên chúng không thể loại bỏ lưu lượng truy cập độc hại trên nhiều trang web.
Giới thiệu tính năng Tìm nạp siêu dữ liệu
Tiêu đề yêu cầu siêu dữ liệu tìm nạp là một tính năng bảo mật mới của nền tảng web, được thiết kế để giúp máy chủ tự bảo vệ khỏi các cuộc tấn công trên nhiều nguồn gốc. Bằng cách cung cấp thông tin về ngữ cảnh của một yêu cầu HTTP trong một tập hợp tiêu đề Sec-Fetch-*
, các tiêu đề này cho phép máy chủ phản hồi áp dụng các chính sách bảo mật trước khi xử lý yêu cầu. Điều này cho phép nhà phát triển quyết định chấp nhận hay từ chối một yêu cầu dựa trên cách yêu cầu được tạo và bối cảnh sử dụng yêu cầu đó, nhờ đó chỉ có thể phản hồi các yêu cầu hợp lệ do ứng dụng của họ tạo ra.
Sec-Fetch-Site
Sec-Fetch-Site
cho máy chủ biết trang web nào đã gửi yêu cầu. Trình duyệt đặt giá trị này thành một trong những giá trị sau:
same-origin
, nếu yêu cầu do ứng dụng của bạn đưa ra (ví dụ:site.example
)same-site
, nếu yêu cầu được thực hiện bởi một miền con của trang web (ví dụ:bar.site.example
)none
, nếu yêu cầu được gây ra rõ ràng bởi hoạt động tương tác của người dùng với tác nhân người dùng (ví dụ: nhấp vào một dấu trang)cross-site
, nếu yêu cầu được gửi bởi một trang web khác (ví dụ:evil.example
)
Sec-Fetch-Mode
Sec-Fetch-Mode
cho biết chế độ của yêu cầu. Giá trị này tương ứng với loại yêu cầu và cho phép bạn phân biệt tải tài nguyên với yêu cầu điều hướng. Ví dụ: đích đến của navigate
cho biết một yêu cầu điều hướng cấp cao nhất, trong khi no-cors
cho biết các yêu cầu tài nguyên như tải hình ảnh.
Sec-Fetch-Dest
Sec-Fetch-Dest
hiển thị đích đến của yêu cầu (ví dụ: nếu thẻ script
hoặc img
khiến trình duyệt yêu cầu một tài nguyên).
Cách sử dụng tính năng Tìm nạp siêu dữ liệu để bảo vệ khỏi các cuộc tấn công trên nhiều nguồn gốc
Thông tin bổ sung mà các tiêu đề yêu cầu này cung cấp khá đơn giản, nhưng ngữ cảnh bổ sung cho phép bạn xây dựng logic bảo mật mạnh mẽ ở phía máy chủ, còn gọi là Chính sách cô lập tài nguyên, chỉ với vài dòng mã.
Triển khai Chính sách tách biệt tài nguyên
Chính sách cô lập tài nguyên ngăn các trang web bên ngoài yêu cầu tài nguyên của bạn. Việc chặn lưu lượng truy cập như vậy sẽ giảm thiểu các lỗ hổng web phổ biến trên nhiều trang web, chẳng hạn như CSRF, XSSI, các cuộc tấn công theo thời gian và rò rỉ thông tin trên nhiều nguồn gốc. Bạn có thể bật chính sách này cho tất cả các điểm cuối của ứng dụng và cho phép tất cả các yêu cầu tài nguyên đến từ ứng dụng của riêng bạn cũng như các thao tác điều hướng trực tiếp (thông qua yêu cầu GET
HTTP). Bạn có thể chọn không áp dụng logic này cho các điểm cuối được tải trong ngữ cảnh trên nhiều trang web (ví dụ: các điểm cuối được tải bằng CORS).
Bước 1: Cho phép các yêu cầu từ những trình duyệt không gửi siêu dữ liệu Tìm nạp
Vì không phải trình duyệt nào cũng hỗ trợ tính năng Tìm nạp siêu dữ liệu, nên bạn cần cho phép các yêu cầu không đặt tiêu đề Sec-Fetch-*
bằng cách kiểm tra xem có sec-fetch-site
hay không.
if not req['sec-fetch-site']:
return True # Allow this request
Bước 2: Cho phép các yêu cầu trên cùng một trang web và do trình duyệt khởi tạo
Mọi yêu cầu không bắt nguồn từ ngữ cảnh trên nhiều nguồn gốc (như evil.example
) đều được cho phép. Cụ thể, đây là những yêu cầu:
- Xuất phát từ ứng dụng của riêng bạn (ví dụ: yêu cầu cùng nguồn gốc, trong đó
site.example
yêu cầusite.example/foo.json
sẽ luôn được cho phép). - Xuất phát từ miền con của bạn.
- Do hoạt động tương tác của người dùng với tác nhân người dùng gây ra một cách rõ ràng (ví dụ: điều hướng trực tiếp hoặc bằng cách nhấp vào một dấu trang, v.v.).
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True # Allow this request
Bước 3: Cho phép điều hướng cấp cao và iframing đơn giản
Để đảm bảo rằng trang web của bạn vẫn có thể được liên kết từ các trang web khác, bạn phải cho phép điều hướng cấp cao nhất đơn giản (HTTP GET
).
if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
# <object> and <embed> send navigation requests, which we disallow.
and req['sec-fetch-dest'] not in ('object', 'embed'):
return True # Allow this request
Bước 4: Chọn không sử dụng các điểm cuối dùng để phân phát lưu lượng truy cập trên nhiều trang web (Không bắt buộc)
Trong một số trường hợp, ứng dụng của bạn có thể cung cấp các tài nguyên được tải trên nhiều trang web. Bạn cần miễn trừ các tài nguyên này theo từng đường dẫn hoặc từng điểm cuối. Sau đây là ví dụ về các điểm cuối như vậy:
- Điểm cuối được truy cập trên nhiều nguồn gốc: Nếu ứng dụng của bạn đang phân phát các điểm cuối đã bật
CORS
, bạn cần chọn rõ ràng để các điểm cuối này không được tách biệt tài nguyên để đảm bảo rằng các yêu cầu trên nhiều trang web đến các điểm cuối này vẫn có thể thực hiện được. - Tài nguyên công khai (ví dụ: hình ảnh, kiểu, v.v.): Mọi tài nguyên công khai và chưa được xác thực có thể tải được trên nhiều nguồn gốc từ các trang web khác cũng có thể được miễn trừ.
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
return True
Bước 5: Từ chối tất cả các yêu cầu khác trên nhiều trang web và không phải là yêu cầu điều hướng
Chính sách cô lập tài nguyên này sẽ từ chối mọi yêu cầu trên nhiều trang web khác, nhờ đó bảo vệ ứng dụng của bạn khỏi các cuộc tấn công phổ biến trên nhiều trang web.
Ví dụ: Mã sau đây minh hoạ cách triển khai đầy đủ Chính sách cô lập tài nguyên mạnh mẽ trên máy chủ hoặc dưới dạng phần mềm trung gian để từ chối các yêu cầu tài nguyên trên nhiều trang web có thể độc hại, đồng thời cho phép các yêu cầu điều hướng đơn giản:
# Reject cross-origin requests to protect from CSRF, XSSI, and other bugs
def allow_request(req):
# Allow requests from browsers which don't send Fetch Metadata
if not req['sec-fetch-site']:
return True
# Allow same-site and browser-initiated requests
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
return True
# Allow simple top-level navigations except <object> and <embed>
if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
and req['sec-fetch-dest'] not in ('object', 'embed'):
return True
# [OPTIONAL] Exempt paths/endpoints meant to be served cross-origin.
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
return True
# Reject all other requests that are cross-site and not navigational
return False
Triển khai Chính sách tách biệt tài nguyên
- Cài đặt một mô-đun như đoạn mã ở trên để ghi lại và theo dõi hoạt động của trang web, đồng thời đảm bảo các quy định hạn chế không ảnh hưởng đến bất kỳ lưu lượng truy cập hợp lệ nào.
- Khắc phục các lỗi vi phạm tiềm ẩn bằng cách miễn trừ các điểm cuối hợp lệ trên nhiều nguồn gốc.
- Thực thi chính sách bằng cách loại bỏ các yêu cầu không tuân thủ.
Xác định và khắc phục lỗi vi phạm chính sách
Bạn nên thử nghiệm chính sách của mình theo cách không gây ra tác dụng phụ bằng cách bật chính sách đó ở chế độ báo cáo trong mã phía máy chủ. Ngoài ra, bạn có thể triển khai logic này trong phần mềm trung gian hoặc trong một proxy đảo ngược để ghi lại mọi lỗi vi phạm mà chính sách của bạn có thể tạo ra khi áp dụng cho lưu lượng truy cập thực tế.
Theo kinh nghiệm triển khai Chính sách tách biệt tài nguyên siêu dữ liệu tìm nạp tại Google, hầu hết các ứng dụng đều tương thích với chính sách như vậy theo mặc định và hiếm khi yêu cầu miễn trừ các điểm cuối để cho phép lưu lượng truy cập trên nhiều trang web.
Thực thi Chính sách tách biệt tài nguyên
Sau khi kiểm tra để đảm bảo chính sách của bạn không ảnh hưởng đến lưu lượng truy cập hợp lệ, bạn có thể sẵn sàng thực thi các quy định hạn chế, đảm bảo rằng các trang web khác sẽ không thể yêu cầu tài nguyên của bạn và bảo vệ người dùng khỏi các cuộc tấn công trên nhiều trang web.
Tài liệu đọc thêm
- Thông số kỹ thuật về tiêu đề yêu cầu tìm nạp siêu dữ liệu của W3C
- Playground Tìm nạp siêu dữ liệu
- Buổi nói chuyện tại Google I/O: Bảo mật ứng dụng web bằng các tính năng hiện đại của nền tảng (Bản trình bày)