Ngăn chặn tình trạng rò rỉ thông tin về CSRF, XSSI và 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 các tài nguyên trên web của mình?
Nhiều ứng dụng web dễ bị tấn công nhiều nguồn gốc như giả mạo yêu cầu trên nhiều trang web (CSRF), đưa vào tập lệnh trên nhiều trang web (XSSI), tấn công thời gian, rò rỉ thông tin nhiều nguồn gốc hoặc tấn công bên kênh thực thi đầu cơ (Spectre).
Tiêu đề của 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ế bảo vệ chuyên sâu – một chính sách tách biệt tài nguyên – nhằm bảo vệ ứng dụng khỏi những cuộc tấn công phổ biến trên nhiều nguồn gốc.
Thông thường, tài nguyên mà một ứng dụng web nhất định hiển thị chỉ được tải bởi chính ứng dụng đó chứ không phải bởi các trang web khác. Trong những trường hợp như vậy, việc triển khai Chính sách tách biệt tài nguyên dựa trên tiêu đề của yêu cầu Tìm nạp siêu dữ liệu sẽ không mất nhiều công sức mà vẫn 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ợ các tiêu đề của yêu cầu Siêu dữ liệu tìm nạp.
Thông tin khái quát
Nhiều cuộc tấn công giữa các trang web có thể diễn ra do web luôn 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ệ mình 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 điển hình trên nhiều nguồn gốc là hành vi giả mạo yêu cầu trên nhiều trang web (CSRF), trong đó kẻ tấn công dụ dỗ người dùng truy cập vào trang web mà họ kiểm soát, sau đó gửi 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 (nhiều trang web) hay không và trình duyệt có tự động đính kèm cookie vào yêu cầu giữa nhiều trang web hay không, nên máy chủ sẽ thay mặt người dùng thực thi hành động mà kẻ tấn công yêu cầu.
Các cuộc tấn công khác trên nhiều trang web như đưa vào tập lệnh trên nhiều trang web (XSSI) hoặc rò rỉ thông tin 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 bị hại trong tài liệu do kẻ tấn công kiểm soát cũng như thông tin rò rỉ về ứng dụng nạn nhân. Vì các ứng dụng không thể dễ dàng phân biệt yêu cầu đáng tin cậy với 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 về tính năng Tìm nạp siêu dữ liệu
Tiêu đề của yêu cầu tìm nạp siêu dữ liệu là một tính năng bảo mật mới trên nền tảng web được thiết kế để giúp các 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 việc cung cấp thông tin về ngữ cảnh của một yêu cầu HTTP trong một nhóm 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 nên 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 đưa ra và bối cảnh sử dụng yêu cầu đó. Nhờ đó, nhà phát triển có thể chỉ phản hồi các yêu cầu hợp lệ do chính ứng dụng của họ đưa 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 cho một trong các giá trị sau:
same-origin
, nếu yêu cầu được đưa ra bởi ứng dụng của chính bạn (ví dụ:site.example
)same-site
, nếu yêu cầu do một miền con của trang web (ví dụ:bar.site.example
) đưa ranone
, nếu yêu cầu rõ ràng là do 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ư nhấp vào dấu trang) gây racross-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. Điều này gần 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 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 tài nguyên).
Cách sử dụng tính năng Tìm nạp siêu dữ liệu để ngăn chặn 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 được gọi là Chính sách tách biệt tài nguyên, chỉ bằng một vài dòng mã.
Triển khai chính sách tách biệt tài nguyên
Chính sách tách biệt 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úp giảm thiểu các lỗ hổng bảo mật phổ biến trên web trên nhiều trang web, chẳng hạn như CSRF, XSSI, tấn công 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 mọi điểm cuối của ứng dụng và sẽ cho phép mọi yêu cầu về tài nguyên đến từ ứng dụng của 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 HTTP GET
). Bạn có thể chọn không sử dụng logic này cho các điểm cuối đáng lẽ phả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ừ 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 sec-fetch-site
có hiện diện 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 do cùng một trang web và trình duyệt khởi tạo
Chúng tôi cho phép mọi yêu cầu không bắt nguồn từ ngữ cảnh nhiều nguồn gốc (như evil.example
). Cụ thể, đây là những yêu cầu:
- Bắt nguồn từ ứng dụng của bạn (ví dụ: yêu cầu có cùng nguồn gốc trong đó
site.example
yêu cầusite.example/foo.json
sẽ luôn được cho phép). - Bắt nguồn từ miền con của bạn.
- Được gây ra một cách rõ ràng là do người dùng tương tác với tác nhân người dùng (ví dụ: điều hướng trực tiếp hoặc nhấp vào 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 đơn giản ở cấp cao nhất và lấy khung hình
Để đảm bảo trang web của bạn vẫn có thể liên kết được 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 những điểm cuối nhằm 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 tài nguyên cần được tải trên nhiều trang web. Những tài nguyên này cần được miễn trừ theo từng đường dẫn hoặc theo từng điểm cuối. Ví dụ về các điểm cuối như vậy:
- Điểm cuối dùng để 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 được bật
CORS
, thì bạn cần chọn không sử dụng các điểm cuối đó một cách rõ ràng để đảm bảo rằng vẫn có thể gửi các yêu cầu trên nhiều trang web đến các điểm cuối này. - 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 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 liên quan đến nhiều trang web và không điều hướng
Chính sách tách biệt 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ờ đó giúp 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 đủ một Chính sách tách biệt tài nguyên mạnh mẽ trên máy chủ hoặc dưới dạng một phần mềm trung gian để từ chối các yêu cầu tài nguyên có khả năng gây hại trên nhiều trang web, đồ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 nhật ký và theo dõi cách trang web của bạn hoạt động và đảm bảo các quy định hạn chế này 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 có thể xảy ra bằng cách miễn trừ các điểm cuối hợp lệ trên nhiều nguồn.
- 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 có tác dụng phụ bằng cách bật chính sách ở chế độ báo cáo trước trong mã phía máy chủ của bạn. Ngoài ra, bạn có thể triển khai logic này trong phần mềm trung gian hoặc trong 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ể gây ra khi áp dụng cho lưu lượng truy cập thực tế.
Từ kinh nghiệm của chúng tôi khi 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, theo mặc định, hầu hết các ứng dụng đều tương thích với chính sách đó 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 chính thức hợp lệ, bạn đã 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, đồng thời 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
- Quy cách của tiêu đề yêu cầu tìm nạp siêu dữ liệu W3C
- Tìm nạp siêu dữ liệu
- Buổi nói chuyện về Google I/O: Bảo mật ứng dụng web bằng các tính năng nền tảng hiện đại (Trang trình bày)