Chính sách bảo mật nội dung

Joe Medley
Joe Medley

Mô hình bảo mật của web bắt nguồn từ chính sách cùng nguồn gốc. Mã từ https://mybank.com chỉ được phép truy cập vào dữ liệu của https://mybank.comhttps://evil.example.com chắc chắn không bao giờ được phép truy cập. Mỗi nguồn gốc được tách biệt với phần còn lại của web, mang đến cho nhà phát triển một hộp cát an toàn để xây dựng và chơi. Trên lý thuyết, đây là một ý tưởng tuyệt vời. Trong thực tế, kẻ tấn công đã tìm ra những cách thông minh để phá hoại hệ thống.

Ví dụ: các cuộc tấn công Tập lệnh trên nhiều trang web (XSS) bỏ qua chính sách cùng nguồn gốc bằng cách lừa một trang web phân phối mã độc hại cùng với nội dung dự kiến. Đây là một vấn đề lớn, vì trình duyệt tin tưởng tất cả mã xuất hiện trên một trang là một phần hợp pháp của nguồn gốc bảo mật của trang đó. Bảng tổng quan về XSS là một bản tóm tắt cũ nhưng khái quát về các phương thức mà kẻ tấn công có thể sử dụng để vi phạm sự tin cậy này bằng cách chèn mã độc hại. Nếu kẻ tấn công chèn thành công bất kỳ mã nào, thì trò chơi gần như đã kết thúc: dữ liệu phiên hoạt động của người dùng bị xâm phạm và thông tin cần được giữ bí mật sẽ bị rò rỉ cho kẻ xấu. Rõ ràng là chúng tôi muốn ngăn chặn điều đó nếu có thể.

Thông tin tổng quan này nêu bật một biện pháp phòng thủ có thể làm giảm đáng kể rủi ro và tác động của các cuộc tấn công XSS trong các trình duyệt hiện đại: Chính sách bảo mật nội dung (CSP).

TL;DR

  • Sử dụng danh sách cho phép để cho ứng dụng biết những nội dung được phép và không được phép.
  • Tìm hiểu xem có những lệnh nào.
  • Tìm hiểu những từ khoá mà họ sử dụng.
  • Mã cùng dòng và eval() được coi là có hại.
  • Báo cáo lỗi vi phạm chính sách cho máy chủ của bạn trước khi thực thi các lỗi đó.

Danh sách cho phép nguồn

Vấn đề mà các cuộc tấn công XSS khai thác là trình duyệt không thể phân biệt giữa tập lệnh thuộc ứng dụng của bạn và tập lệnh do bên thứ ba chèn vào một cách độc hại. Ví dụ: nút Google +1 ở cuối trang này sẽ tải và thực thi mã từ https://apis.google.com/js/plusone.js trong ngữ cảnh nguồn gốc của trang này. Chúng tôi tin tưởng mã đó, nhưng không thể mong đợi trình duyệt tự tìm ra mã từ apis.google.com là tuyệt vời, trong khi mã từ apis.evil.example.com có thể không phải vậy. Trình duyệt sẽ tải xuống và thực thi mọi mã mà trang yêu cầu, bất kể nguồn.

Thay vì tin tưởng mù quáng mọi thứ mà máy chủ phân phối, CSP sẽ xác định tiêu đề HTTP Content-Security-Policy. Tiêu đề này cho phép bạn tạo danh sách cho phép các nguồn nội dung đáng tin cậy và hướng dẫn trình duyệt chỉ thực thi hoặc hiển thị tài nguyên từ các nguồn đó. Ngay cả khi kẻ tấn công có thể tìm thấy lỗ hổng để chèn tập lệnh, tập lệnh đó sẽ không khớp với danh sách cho phép và do đó sẽ không được thực thi.

Vì chúng ta tin tưởng apis.google.com sẽ phân phối mã hợp lệ và chúng ta cũng tin tưởng mình sẽ làm được điều tương tự, hãy xác định một chính sách chỉ cho phép tập lệnh thực thi khi tập lệnh đó đến từ một trong hai nguồn đó:

Content-Security-Policy: script-src 'self' https://apis.google.com

Thật đơn giản phải không? Như bạn có thể đoán, script-src là một lệnh kiểm soát một tập hợp các đặc quyền liên quan đến tập lệnh cho một trang cụ thể. Chúng tôi đã chỉ định 'self' làm một nguồn tập lệnh hợp lệ và https://apis.google.com làm một nguồn tập lệnh khác. Trình duyệt tải xuống và thực thi JavaScript một cách hợp pháp từ apis.google.com qua HTTPS, cũng như từ nguồn gốc của trang hiện tại.

Lỗi trên bảng điều khiển: Từ chối tải tập lệnh "http://evil.example.com/evil.js" vì tập lệnh này vi phạm lệnh sau trong Chính sách bảo mật nội dung: script-src "self" https://apis.google.com

Khi xác định chính sách này, trình duyệt chỉ gửi lỗi thay vì tải tập lệnh từ bất kỳ nguồn nào khác. Khi một kẻ tấn công thông minh có thể chèn mã vào trang web của bạn, chúng sẽ nhanh chóng nhận được thông báo lỗi thay vì thành công mà họ mong đợi.

Chính sách áp dụng cho nhiều tài nguyên

Mặc dù tài nguyên tập lệnh là rủi ro bảo mật rõ ràng nhất, nhưng CSP cung cấp một bộ nhiều lệnh chính sách cho phép kiểm soát khá chi tiết đối với các tài nguyên mà một trang được phép tải. Bạn đã thấy script-src, vì vậy, khái niệm này phải rõ ràng.

Hãy cùng tìm hiểu nhanh các lệnh tài nguyên còn lại. Danh sách dưới đây trình bày trạng thái của các lệnh ở cấp 2. Thông số kỹ thuật cấp 3 đã được phát hành, nhưng chưa được triển khai rộng rãi trong các trình duyệt chính.

  • base-uri hạn chế các URL có thể xuất hiện trong phần tử <base> của trang.
  • child-src liệt kê các URL cho worker và nội dung khung được nhúng. Ví dụ: child-src https://youtube.com sẽ cho phép nhúng video từ YouTube nhưng không cho phép nhúng video từ các nguồn khác.
  • connect-src giới hạn các nguồn gốc mà bạn có thể kết nối (thông qua XHR, WebSockets và EventSource).
  • font-src chỉ định các nguồn gốc có thể phân phát phông chữ web. Bạn có thể bật phông chữ web của Google thông qua font-src https://themes.googleusercontent.com.
  • form-action liệt kê các điểm cuối hợp lệ để gửi bằng các thẻ <form>.
  • frame-ancestors chỉ định các nguồn có thể nhúng trang hiện tại. Chỉ thị này áp dụng cho các thẻ <frame>, <iframe>, <embed><applet>. Bạn không thể sử dụng lệnh này trong thẻ <meta> và chỉ áp dụng cho các tài nguyên không phải HTML.
  • frame-src không còn được dùng trong cấp 2 nhưng được khôi phục trong cấp 3. Nếu không có, thì hệ thống vẫn sẽ quay về child-src như trước đây.
  • img-src xác định nguồn gốc mà từ đó có thể tải hình ảnh.
  • media-src hạn chế các nguồn gốc được phép phân phối video và âm thanh.
  • object-src cho phép kiểm soát Flash và các trình bổ trợ khác.
  • plugin-types giới hạn các loại trình bổ trợ mà một trang có thể gọi.
  • report-uri chỉ định một URL mà trình duyệt sẽ gửi báo cáo khi có chính sách bảo mật nội dung bị vi phạm. Không thể dùng lệnh này trong các thẻ <meta>.
  • style-src là phiên bản tương đương của script-src cho biểu định kiểu.
  • upgrade-insecure-requests hướng dẫn các tác nhân người dùng ghi lại các giao thức URL, thay đổi HTTP thành HTTPS. Chỉ thị này dành cho những trang web có số lượng lớn URL cũ cần được viết lại.
  • worker-src là một lệnh CSP cấp 3 hạn chế các URL có thể được tải dưới dạng worker, worker dùng chung hoặc worker dịch vụ. Kể từ tháng 7 năm 2017, lệnh này đã có các phương thức triển khai bị hạn chế.

Theo mặc định, các lệnh sẽ mở rộng. Nếu bạn không đặt chính sách cụ thể cho một lệnh, giả sử là font-src, thì lệnh đó sẽ hoạt động theo mặc định như thể bạn đã chỉ định * làm nguồn hợp lệ (ví dụ: bạn có thể tải phông chữ từ bất kỳ đâu mà không bị hạn chế).

Bạn có thể ghi đè hành vi mặc định này bằng cách chỉ định một lệnh default-src. Lệnh này xác định các giá trị mặc định cho hầu hết các lệnh mà bạn không chỉ định. Nhìn chung, điều này áp dụng cho mọi lệnh kết thúc bằng -src. Nếu default-src được đặt thành https://example.com và bạn không chỉ định được lệnh font-src, thì bạn có thể tải phông chữ từ https://example.com và không nơi nào khác. Chúng ta chỉ chỉ định script-src trong các ví dụ trước, nghĩa là hình ảnh, phông chữ, v.v. có thể được tải từ bất kỳ nguồn gốc nào.

Các lệnh sau đây không sử dụng default-src làm phương án dự phòng. Hãy nhớ rằng việc không đặt các tuỳ chọn này cũng giống như việc cho phép bất kỳ thứ gì.

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

Bạn có thể sử dụng nhiều hoặc ít lệnh trong số này tuỳ theo ứng dụng cụ thể của mình, chỉ cần liệt kê từng lệnh trong tiêu đề HTTP, phân tách các lệnh bằng dấu chấm phẩy. Hãy nhớ liệt kê tất cả tài nguyên bắt buộc thuộc một loại cụ thể trong một lệnh. Nếu bạn viết một kiểu như script-src https://host1.com; script-src https://host2.com, lệnh thứ hai sẽ bị bỏ qua. Nội dung như sau sẽ chỉ định chính xác cả hai nguồn gốc là hợp lệ:

script-src https://host1.com https://host2.com

Ví dụ: nếu bạn có một ứng dụng tải tất cả tài nguyên của ứng dụng đó từ một mạng phân phối nội dung (ví dụ: https://cdn.example.net) và biết rằng bạn không cần bất kỳ nội dung hoặc trình bổ trợ nào được đóng khung, thì chính sách của bạn có thể trông giống như sau:

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

Chi tiết triển khai

Bạn sẽ thấy các tiêu đề X-WebKit-CSPX-Content-Security-Policy trong nhiều hướng dẫn trên web. Từ giờ trở đi, bạn nên bỏ qua các tiêu đề có tiền tố này. Các trình duyệt hiện đại (ngoại trừ IE) hỗ trợ tiêu đề Content-Security-Policy không có tiền tố. Đó là tiêu đề mà bạn nên sử dụng.

Bất kể tiêu đề bạn sử dụng là gì, chính sách được xác định trên cơ sở từng trang: bạn sẽ cần gửi tiêu đề HTTP cùng với mọi phản hồi mà bạn muốn đảm bảo được bảo vệ. Điều này mang lại nhiều sự linh hoạt vì bạn có thể điều chỉnh chính sách cho các trang cụ thể dựa trên nhu cầu cụ thể của các trang đó. Có thể một nhóm trang trong trang web của bạn có nút +1, trong khi các trang khác thì không: bạn có thể chỉ cho phép tải mã nút khi cần.

Danh sách nguồn trong mỗi lệnh rất linh hoạt. Bạn có thể chỉ định nguồn theo giao thức (data:, https:) hoặc theo mức độ cụ thể từ chỉ tên máy chủ (example.com, khớp với mọi nguồn gốc trên máy chủ đó: mọi giao thức, mọi cổng) đến URI đủ điều kiện (https://example.com:443, chỉ khớp với HTTPS, chỉ example.com và chỉ cổng 443). Chấp nhận ký tự đại diện, nhưng chỉ dưới dạng lược đồ, cổng hoặc ở vị trí ngoài cùng bên trái của tên máy chủ: *://*.example.com:* sẽ khớp với tất cả miền con của example.com (chứ không phải chính example.com) bằng cách sử dụng mọi lược đồ, trên mọi cổng.

Danh sách nguồn cũng chấp nhận 4 từ khoá:

  • 'none' không khớp với kết quả nào như bạn mong đợi.
  • 'self' khớp với nguồn gốc hiện tại, nhưng không khớp với các miền con của nguồn gốc đó.
  • 'unsafe-inline' cho phép JavaScript và CSS nội tuyến. (Chúng ta sẽ đề cập chi tiết hơn về vấn đề này sau.)
  • 'unsafe-eval' cho phép các cơ chế văn bản sang JavaScript như eval. (Chúng ta cũng sẽ đề cập đến vấn đề này.)

Những từ khoá này yêu cầu dấu ngoặc đơn. Ví dụ: script-src 'self' (có dấu ngoặc kép) uỷ quyền thực thi JavaScript từ máy chủ lưu trữ hiện tại; script-src self (không có dấu ngoặc kép) cho phép JavaScript từ một máy chủ có tên "self" (và không từ máy chủ lưu trữ hiện tại), có thể không phải là ý định của bạn.

Hộp cát

Còn một lệnh khác đáng nói đến: sandbox. Thuộc tính này hơi khác với các thuộc tính khác mà chúng ta đã xem xét, vì thuộc tính này đặt ra các hạn chế đối với các hành động mà trang có thể thực hiện thay vì các tài nguyên mà trang có thể tải. Nếu có lệnh sandbox, trang sẽ được coi là được tải bên trong <iframe> có thuộc tính sandbox. Điều này có thể gây ra nhiều ảnh hưởng trên trang: buộc trang vào một nguồn gốc duy nhất và ngăn việc gửi biểu mẫu, v.v. Nó nằm ngoài phạm vi của bài viết này một chút, nhưng bạn có thể tìm thấy đầy đủ thông tin chi tiết về các thuộc tính hộp cát hợp lệ trong phần "Hộp cát" của thông số kỹ thuật HTML5.

Thẻ meta

Cơ chế phân phối ưu tiên của CSP là tiêu đề HTTP. Tuy nhiên, bạn có thể đặt chính sách trên một trang ngay trong mã đánh dấu. Hãy thực hiện việc đó bằng cách sử dụng thẻ <meta> có thuộc tính http-equiv:

<meta
  http-equiv="Content-Security-Policy"
  content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>

Bạn không thể sử dụng tính năng này cho frame-ancestors, report-uri hoặc sandbox.

Mã cùng dòng được coi là có hại

Bạn cần phải hiểu rõ rằng CSP dựa trên nguồn gốc danh sách cho phép, vì đó là cách rõ ràng để hướng dẫn trình duyệt coi một số tài nguyên cụ thể là chấp nhận được và từ chối phần còn lại. Tuy nhiên, danh sách cho phép dựa trên nguồn gốc không giải quyết được mối đe doạ lớn nhất do các cuộc tấn công XSS gây ra: chèn tập lệnh cùng dòng. Nếu kẻ tấn công có thể chèn một thẻ tập lệnh trực tiếp chứa một số tải trọng độc hại (<script>sendMyDataToEvilDotCom();</script>), thì trình duyệt sẽ không có cơ chế để phân biệt thẻ này với một thẻ tập lệnh nội tuyến hợp lệ. CSP giải quyết vấn đề này bằng cách cấm hoàn toàn tập lệnh nội tuyến: đó là cách duy nhất để đảm bảo.

Lệnh cấm này không chỉ bao gồm các tập lệnh được nhúng trực tiếp vào thẻ script, mà còn bao gồm cả trình xử lý sự kiện nội tuyến và URL javascript:. Bạn sẽ cần di chuyển nội dung của thẻ script vào một tệp bên ngoài và thay thế URL javascript:<a ... onclick="[JAVASCRIPT]"> bằng các lệnh gọi addEventListener() thích hợp. Ví dụ: bạn có thể viết lại nội dung sau từ:

<script>
  function doAmazingThings() {
    alert('YOU AM AMAZING!');
  }
</script>
<button onclick="doAmazingThings();">Am I amazing?</button>

thành một số thứ khác như:

<!-- amazing.html -->
<script src="amazing.js"></script>
<button id="amazing">Am I amazing?</button>

<div style="clear:both;"></div>
// amazing.js
function doAmazingThings() {
  alert('YOU AM AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('amazing').addEventListener('click', doAmazingThings);
});

Mã được viết lại có một số ưu điểm vượt trội so với việc hoạt động tốt với CSP; đây là phương pháp hay nhất, bất kể bạn sử dụng CSP như thế nào. JavaScript cùng dòng kết hợp cấu trúc và hành vi theo cách bạn không nên làm. Các tài nguyên bên ngoài dễ dàng lưu vào bộ nhớ đệm hơn cho trình duyệt, dễ hiểu hơn cho nhà phát triển và thuận lợi cho việc biên dịch cũng như rút gọn. Bạn sẽ viết mã tốt hơn nếu bạn di chuyển mã vào các tài nguyên bên ngoài.

Kiểu nội tuyến được xử lý theo cách tương tự: cả thuộc tính style và thẻ style phải được hợp nhất vào các tệp biểu định kiểu bên ngoài để bảo vệ khỏi nhiều phương thức rất thông minh để lấy cắp dữ liệu mà CSS cho phép.

Nếu phải có tập lệnh và kiểu nội tuyến, bạn có thể bật tập lệnh và kiểu đó bằng cách thêm 'unsafe-inline' làm nguồn được phép trong lệnh script-src hoặc style-src. Bạn cũng có thể sử dụng số chỉ dùng một lần hoặc hàm băm (xem bên dưới), nhưng thực sự không nên làm vậy. Việc cấm tập lệnh nội tuyến là lợi ích bảo mật lớn nhất mà CSP mang lại, đồng thời việc cấm kiểu nội tuyến cũng giúp tăng cường bảo mật cho ứng dụng của bạn. Bạn cần phải nỗ lực một chút để đảm bảo mọi thứ hoạt động chính xác sau khi di chuyển tất cả mã ra khỏi dòng, nhưng đó là một sự đánh đổi đáng giá.

Nếu bạn chắc chắn phải sử dụng

CSP cấp 2 cung cấp khả năng tương thích ngược cho các tập lệnh nội tuyến bằng cách cho phép bạn thêm các tập lệnh nội tuyến cụ thể vào danh sách cho phép bằng cách sử dụng số chỉ dùng một lần (số chỉ dùng một lần) hoặc hàm băm. Mặc dù việc này có thể gây phiền toái, nhưng sẽ rất hữu ích trong trường hợp khẩn cấp.

Để sử dụng số chỉ dùng một lần, hãy cung cấp cho thẻ tập lệnh của bạn thuộc tính số chỉ dùng một lần. Giá trị của thuộc tính này phải khớp với một giá trị trong danh sách các nguồn đáng tin cậy. Ví dụ:

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
  // Some inline code I can't remove yet, but need to asap.
</script>

Bây giờ, hãy thêm số chỉ dùng một lần vào lệnh script-src được thêm vào từ khoá nonce-.

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

Hãy nhớ rằng bạn phải tạo lại số chỉ dùng một lần cho mỗi yêu cầu trang và số này phải không thể đoán được.

Hàm băm cũng hoạt động theo cách tương tự. Thay vì thêm mã vào thẻ tập lệnh, hãy tạo hàm băm SHA của chính tập lệnh rồi thêm hàm này vào lệnh script-src. Ví dụ: giả sử trang của bạn chứa nội dung sau:

<script>
  alert('Hello, world.');
</script>

Chính sách của bạn sẽ chứa nội dung sau:

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

Có một vài điều cần lưu ý ở đây. Tiền tố sha*- chỉ định thuật toán tạo hàm băm. Trong ví dụ trên, sha256- được sử dụng. CSP cũng hỗ trợ sha384-sha512-. Khi tạo hàm băm, đừng thêm thẻ <script>. Ngoài ra, cách viết hoa và khoảng trắng cũng quan trọng, bao gồm cả khoảng trắng ở đầu hoặc ở cuối.

Một lượt tìm kiếm trên Google về cách tạo hàm băm SHA sẽ đưa bạn đến các giải pháp bằng bất kỳ ngôn ngữ nào. Khi sử dụng Chrome 40 trở lên, bạn có thể mở DevTools rồi tải lại trang. Thẻ Console (Bảng điều khiển) sẽ chứa thông báo lỗi có hàm băm sha256 chính xác cho từng tập lệnh nội tuyến.

Eval cũng

Ngay cả khi không thể chèn tập lệnh trực tiếp, kẻ tấn công vẫn có thể lừa ứng dụng của bạn chuyển đổi văn bản không hoạt động thành JavaScript có thể thực thi và thay mặt chúng thực thi văn bản đó. eval(), Hàm mới() , setTimeout([string], ...)setInterval([string], ...) đều là các vectơ mà qua đó, văn bản được chèn có thể thực thi một hành động độc hại ngoài dự kiến. Phản hồi mặc định của CSP đối với rủi ro này là chặn hoàn toàn tất cả các vectơ này.

Điều này có nhiều ảnh hưởng đến cách bạn xây dựng ứng dụng:

  • Bạn phải phân tích cú pháp JSON qua JSON.parse tích hợp sẵn thay vì dựa vào eval. Các thao tác JSON gốc có sẵn trong mọi trình duyệt kể từ IE8 và hoàn toàn an toàn.
  • Viết lại mọi lệnh gọi setTimeout hoặc setInterval mà bạn đang thực hiện bằng các hàm nội tuyến thay vì chuỗi. Ví dụ:
setTimeout("document.querySelector('a').style.display = 'none';", 10);

sẽ được viết tốt hơn như sau:

setTimeout(function () {
  document.querySelector('a').style.display = 'none';
}, 10);
  • Tránh tạo mẫu cùng dòng trong thời gian chạy: Nhiều thư viện tạo mẫu sử dụng new Function() một cách tự do để tăng tốc độ tạo mẫu trong thời gian chạy. Đây là một ứng dụng linh hoạt của lập trình động, nhưng có nguy cơ đánh giá văn bản độc hại. Một số khung hỗ trợ CSP ngay từ đầu, quay lại trình phân tích cú pháp mạnh mẽ khi không có eval. Lệnh ng-csp của AngularJS là một ví dụ điển hình về điều này.

Tuy nhiên, lựa chọn tốt hơn là ngôn ngữ tạo mẫu có hỗ trợ tính năng biên dịch trước (ví dụ: Thanh công cụ chẳng hạn). Việc biên dịch trước các mẫu có thể giúp người dùng có trải nghiệm nhanh hơn cả khi triển khai thời gian chạy nhanh nhất, đồng thời an toàn hơn. Nếu eval và các thành phần chuyển văn bản sang JavaScript là thiết yếu đối với ứng dụng của bạn, bạn có thể bật các thành phần này bằng cách thêm 'unsafe-eval' làm nguồn được phép trong lệnh script-src, nhưng bạn không nên làm như vậy. Việc cấm thực thi chuỗi sẽ khiến kẻ tấn công khó thực thi mã trái phép trên trang web của bạn hơn.

Báo cáo

Khả năng chặn các tài nguyên không đáng tin cậy ở phía máy khách của CSP là một lợi ích lớn cho người dùng, nhưng việc gửi một số loại thông báo về máy chủ để bạn có thể xác định và chặn mọi lỗi cho phép chèn nội dung độc hại ngay từ đầu. Để làm việc này, bạn có thể hướng dẫn trình duyệt POST báo cáo vi phạm ở định dạng JSON đến một vị trí được chỉ định trong lệnh report-uri.

Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

Những báo cáo đó sẽ có dạng như sau:

{
  "csp-report": {
    "document-uri": "http://example.org/page.html",
    "referrer": "http://evil.example.com/",
    "blocked-uri": "http://evil.example.com/evil.js",
    "violated-directive": "script-src 'self' https://apis.google.com",
    "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
  }
}

Tệp này chứa một phần thông tin hữu ích giúp bạn theo dõi nguyên nhân cụ thể của lỗi vi phạm, bao gồm trang đã xảy ra lỗi vi phạm (document-uri), đường liên kết giới thiệu của trang đó (lưu ý rằng không giống như trường tiêu đề HTTP, khoá này không bị viết sai chính tả), tài nguyên vi phạm chính sách của trang (blocked-uri), lệnh cụ thể mà trang web vi phạm (violated-directive) và chính sách đầy đủ của trang (original-policy).

Chỉ báo cáo

Nếu mới bắt đầu sử dụng CSP, bạn nên đánh giá trạng thái hiện tại của ứng dụng trước khi triển khai một chính sách hà khắc cho người dùng. Để chuẩn bị cho việc triển khai đầy đủ, bạn có thể yêu cầu trình duyệt theo dõi một chính sách, báo cáo các lỗi vi phạm nhưng không thực thi các quy định hạn chế. Thay vì gửi tiêu đề Content-Security-Policy, hãy gửi tiêu đề Content-Security-Policy-Report-Only.

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

Chính sách được chỉ định ở chế độ chỉ báo cáo sẽ không chặn các tài nguyên bị hạn chế, nhưng sẽ gửi báo cáo vi phạm đến vị trí mà bạn chỉ định. Thậm chí, bạn có thể gửi cả hai tiêu đề, thực thi một chính sách trong khi giám sát một chính sách khác. Đây là một cách hay để đánh giá tác động của các thay đổi đối với CSP của ứng dụng: bật tính năng báo cáo cho chính sách mới, theo dõi báo cáo vi phạm và khắc phục mọi lỗi xuất hiện; khi bạn hài lòng với hiệu quả của chính sách mới, hãy bắt đầu thực thi chính sách mới.

Sử dụng thực tế

CSP 1 khá hữu dụng trong Chrome, Safari và Firefox, nhưng có hỗ trợ rất hạn chế trong IE 10. Bạn có thể xem thông tin chi tiết tại caniuse.com. CSP cấp 2 đã có trong Chrome kể từ phiên bản 40. Các trang web lớn như Twitter và Facebook đã triển khai tiêu đề này (Bạn nên đọc nghiên cứu điển hình của Twitter) và tiêu chuẩn này đã sẵn sàng để bạn bắt đầu triển khai trên trang web của riêng mình.

Bước đầu tiên để tạo chính sách cho ứng dụng là đánh giá các tài nguyên mà bạn thực sự đang tải. Khi bạn đã nắm được cách kết hợp các thành phần trong ứng dụng, hãy thiết lập chính sách dựa trên các yêu cầu đó. Hãy cùng tìm hiểu một số trường hợp sử dụng phổ biến và xác định cách tốt nhất để hỗ trợ các trường hợp đó trong phạm vi bảo vệ của CSP.

Trường hợp sử dụng 1: tiện ích mạng xã hội

  • Nút +1 của Google bao gồm một tập lệnh từ https://apis.google.com và nhúng một <iframe> từ https://plusone.google.com. Bạn cần có một chính sách bao gồm cả hai nguồn gốc này để nhúng nút. Chính sách tối thiểu sẽ là script-src https://apis.google.com; child-src https://plusone.google.com. Bạn cũng cần đảm bảo rằng đoạn mã JavaScript mà Google cung cấp được kéo vào một tệp JavaScript bên ngoài. Nếu bạn có chính sách dựa trên Cấp 1 sử dụng frame-src, thì Cấp 2 yêu cầu bạn thay đổi chính sách đó thành child-src. Điều này không còn cần thiết ở CSP cấp 3 nữa.

  • Nút Thích của Facebook có một số tuỳ chọn triển khai. Bạn nên tiếp tục sử dụng phiên bản <iframe> vì phiên bản này được chuyển sang hộp cát an toàn với phần còn lại của trang web. Phương thức này yêu cầu một lệnh child-src https://facebook.com để hoạt động đúng cách. Xin lưu ý rằng theo mặc định, mã <iframe> mà Facebook cung cấp sẽ tải một URL tương đối, //facebook.com. Thay đổi mã đó để chỉ định rõ ràng HTTPS: https://facebook.com. Chẳng có lý do gì để sử dụng HTTP nếu bạn không cần.

  • Nút Tweet của Twitter dựa vào quyền truy cập vào một tập lệnh và một khung, cả hai đều được lưu trữ tại https://platform.twitter.com. (Twitter cũng cung cấp URL tương đối theo mặc định; chỉnh sửa mã để chỉ định HTTPS khi sao chép/dán mã trên máy.) Bạn sẽ hoàn tất việc thiết lập script-src https://platform.twitter.com; child-src https://platform.twitter.com, miễn là bạn di chuyển đoạn mã JavaScript mà Twitter cung cấp vào một tệp JavaScript bên ngoài.

  • Các nền tảng khác cũng có các yêu cầu tương tự và có thể được giải quyết theo cách tương tự. Bạn chỉ nên đặt default-src thành 'none' và theo dõi bảng điều khiển để xác định những tài nguyên bạn cần bật để các tiện ích hoạt động.

Việc đưa nhiều tiện ích rất đơn giản: bạn chỉ cần kết hợp các lệnh của chính sách, hãy nhớ hợp nhất tất cả tài nguyên thuộc một loại duy nhất thành một lệnh duy nhất. Nếu bạn muốn sử dụng cả 3 tiện ích mạng xã hội, chính sách sẽ có dạng như sau:

script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com

Trường hợp sử dụng #2: khoá

Giả sử trong giây lát bạn chạy một trang web ngân hàng và muốn đảm bảo rằng chỉ những tài nguyên mà bạn tự viết mới có thể tải được. Trong trường hợp này, hãy bắt đầu bằng một chính sách mặc định chặn mọi thứ (default-src 'none') và xây dựng từ đó.

Giả sử ngân hàng tải tất cả hình ảnh, kiểu và tập lệnh từ một CDN tại https://cdn.mybank.net, đồng thời kết nối qua XHR với https://api.mybank.com/ để tải nhiều bit dữ liệu xuống. Khung được sử dụng, nhưng chỉ dành cho các trang cục bộ của trang web (không có nguồn gốc của bên thứ ba). Trang web không có Flash, không có phông chữ, không có nội dung bổ sung. Tiêu đề CSP hạn chế nhất mà chúng tôi có thể gửi là:

Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'

Trường hợp sử dụng 3: Chỉ SSL

Một quản trị viên của diễn đàn thảo luận về nhẫn cưới muốn đảm bảo rằng tất cả tài nguyên chỉ được tải qua các kênh bảo mật, nhưng không thực sự viết nhiều mã; việc viết lại các phần lớn của phần mềm diễn đàn bên thứ ba chứa đầy tập lệnh nội tuyến và kiểu vượt quá khả năng của anh ta. Chính sách sau đây sẽ có hiệu lực:

Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'

Mặc dù https: được chỉ định trong default-src, nhưng tập lệnh và các lệnh định kiểu không tự động kế thừa nguồn đó. Mỗi lệnh sẽ ghi đè hoàn toàn giá trị mặc định của loại tài nguyên cụ thể đó.

Tương lai

Chính sách bảo mật nội dung cấp 2 là một Đề xuất đề xuất. Nhóm làm việc về bảo mật ứng dụng web của W3C đã bắt đầu làm việc trên phiên bản lặp lại tiếp theo của quy cách, Chính sách bảo mật nội dung cấp 3.

Nếu bạn quan tâm đến cuộc thảo luận về các tính năng sắp ra mắt này, hãy duyệt qua bản lưu trữ danh sách gửi thư public-webappsec@ hoặc tự tham gia.

Phản hồi