Bảo vệ tài nguyên của bạn khỏi các cuộc tấn công web bằng công cụ Tìm nạp siêu dữ liệu

Ngăn chặn việc rò rỉ thông tin về CSRF, XSSI và thông tin từ nhiều nguồn gốc.

Lukas Weichselbaum
Lukas Weichselbaum

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 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 (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ệ được ứ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.

Hỗ trợ trình duyệt

  • Chrome: 76.
  • Cạnh: 79.
  • Firefox: 90.
  • Safari: 16.4.

Nguồn

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 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 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 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 đư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 pháp do chính ứng dụng của họ đưa ra.

Cùng nguồn gốc
Các yêu cầu bắt nguồn từ các trang web do máy chủ của riêng bạn phân phát (cùng nguồn gốc) sẽ tiếp tục hoạt động. Yêu cầu tìm nạp qua https://site.example đối với tài nguyên https://site.example/foo.json trong JavaScript sẽ khiến trình duyệt gửi tiêu đề của yêu cầu HTTP "Sec Tìm nạp trang: cùng nguồn gốc".
Trên nhiều trang web
Các yêu cầu độc hại trên nhiều trang web có thể bị máy chủ từ chối vì ngữ cảnh bổ sung trong yêu cầu HTTP do các tiêu đề Sec-Fetch-* cung cấp. Hình ảnh trên https://evil.example đã đặt thuộc tính src của phần tử img thành "https://site.example/foo.json" khiến trình duyệt gửi tiêu đề yêu cầu HTTP "Sec-Fetch-Site: cross-site".

Sec-Fetch-Site

Hỗ trợ trình duyệt

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 90.
  • Safari: 16.4.

Nguồn

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 đượ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 đượ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 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 ra
  • 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

Hỗ trợ trình duyệt

  • Chrome: 76.
  • Cạnh: 79.
  • Firefox: 90.
  • Safari: 16.4.

Nguồn

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 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

Hỗ trợ trình duyệt

  • Chrome: 80.
  • Cạnh: 80.
  • Firefox: 90.
  • Safari: 16.4.

Nguồn

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 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 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 á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ừ 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 do cùng một trang web và 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:

  • 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ầu site.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 cấp cao và iframing đơn giản

Để đả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 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. 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 đượ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 liên quan đến nhiều trang web và không đ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 đủ 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 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

  1. 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.
  2. 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.
  3. 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 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ế.

Từ kinh nghiệm của chúng tôi trong việc 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 sản xuất hợp pháp, 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