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

Mike West
Liên khúc Joe
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ỉ nên có quyền truy cập vào dữ liệu của https://mybank.comhttps://evil.example.com chắc chắn không đượ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, cung cấp cho các nhà phát triển một hộp cát an toàn để tạo và chơi. Về lý thuyết, đây là mức giá hoàn hảo. Trên thực tế, những kẻ tấn công đã tìm được những cách tinh vi để phá vỡ hệ thống.

Tấn công tập lệnh trên nhiều trang web (XSS), chẳng hạn như bỏ qua cùng một chính sách 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 mong muốn. Đây là một vấn đề lớn vì các trình duyệt tin tưởng rằng tất cả mã xuất hiện trên trang là một phần hợp pháp của nguồn gốc bảo mật của trang đó. Bản tóm tắt về XSS là một mặt cắt cũ nhưng mang tính đại diện cho các phương thức mà kẻ tấn công có thể sử dụng để vi phạm niềm tin 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ì mọi việc đã 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ị đánh cắp vào The BadGuys. Hiển nhiên, chúng tôi muốn ngăn điều này nếu có thể.

Phần tổng quan này nêu bật một biện pháp bảo vệ có thể làm giảm đáng kể nguy cơ 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 khách hà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 các từ khoá mà họ dùng.
  • Mã cùng dòng và eval() bị coi là có hại.
  • Hãy báo cáo các lỗi vi phạm chính sách cho máy chủ của bạn trước khi thực thi.

Danh sách nguồn được cho phép

Vấn đề bị các cuộc tấn công XSS khai thác là trình duyệt không thể phân biệt được tập lệnh thuộc ứng dụng của bạn và tập lệnh đã bị bên thứ ba chèn độ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 bối cảnh nguồn gốc của trang này. Chúng tôi tin tưởng mã đó, nhưng chúng tôi không thể kỳ vọng 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 thì có lẽ không. Trình duyệt sẵn sàng tải xuống và thực thi bất kỳ mã nào mà trang yêu cầu, bất kể nguồn là nguồn.

Thay vì tin tưởng một cách mù quáng vào 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, đồng thời 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 một 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 nên sẽ không được thực thi.

Vì chúng ta tin tưởng apis.google.com sẽ cung cấp mã hợp lệ và chúng ta tin tưởng chính mình cũng làm như vậy, 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

Đơ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ột nguồn khác. Trình duyệt sẽ tải xuống và thực thi JavaScript nghiêm ngặt từ apis.google.com qua HTTPS, cũng như từ nguồn gốc của trang hiện tại.

Lỗi bảng điều khiển: Bị 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

Với chính sách này được xác định, trình duyệt chỉ cần báo 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 chèn mã vào trang web của bạn, chúng sẽ gặp phải thông báo lỗi thay vì thành công như mong đợi.

Chính sách áp dụng cho nhiều loại 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 cũng cung cấp một bộ lệnh chính sách phong phú cho phép kiểm soát tương đối chi tiết các tài nguyên mà một trang được phép tải. Bạn đã thấy script-src nên khái niệm này phải rõ ràng.

Hãy xem nhanh các lệnh còn lại về tài nguyên. Danh sách dưới đây thể hiện 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 phần lớn chưa được triển khai trong các trình duyệt chính.

  • base-uri hạn chế những URL có thể xuất hiện trong phần tử <base> của trang.
  • child-src liệt kê các URL của trình thực thi và nội dung khung được nhúng. Ví dụ: child-src https://youtube.com sẽ cho phép nhúng video từ YouTube chứ 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 những 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 từ thẻ <form>.
  • frame-ancestors chỉ định các nguồn có thể nhúng trang hiện tại. Lệnh 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 ở cấp 2 nhưng được khôi phục ở cấp 3. Nếu không, giá trị này vẫn sẽ quay về child-src như trước.
  • img-src xác định nguồn gốc mà hình ảnh có thể được tải.
  • 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 vi phạm chính sách bảo mật nội dung. Bạn không thể sử dụng lệnh này trong các thẻ <meta>.
  • style-src là đối tác 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. Lệnh này dành cho các 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 nhằm hạn chế những URL có thể được tải dưới dạng một trình thực thi, một trình thực thi dùng chung hoặc trình thực thi dịch vụ. Kể từ tháng 7 năm 2017, lệnh này đã được triển khai có giới hạn.

Theo mặc định, các lệnh được mở rộng. Nếu bạn không đặt một chính sách cụ thể cho một lệnh, giả sử 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 cứ đâ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 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, nguyên tắc 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 chứ không phải ở nơi nào khác. Chúng tôi chỉ xác định script-src trong các ví dụ trước đó, tức là hình ảnh, phông chữ, v.v. có thể tải từ mọi nguồn.

Các lệnh sau đây không dùng default-src làm phương án dự phòng. Hãy nhớ rằng việc không đặt chúng cũng giống như việc cho phép bất cứ điều 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) sao cho phù hợp với ứng dụng cụ thể, 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ả các tài nguyên cần thiết thuộc một loại cụ thể trong một lệnh duy nhất. Nếu bạn đã viết nội dung như script-src https://host1.com; script-src https://host2.com, lệnh thứ hai sẽ bị bỏ qua. Ví dụ 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

Chẳng hạn, 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 (chẳng hạn như 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ợ khung nào, thì chính sách của bạn có thể sẽ có dạng như sau:

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

Thông tin 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 chưa có tiền tố. Đó là tiêu đề mà bạn nên sử dụng.

Bất kể bạn dùng tiêu đề nào, chính sách đều được xác định theo từng trang: bạn cần phải 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 tính linh hoạt cao vì bạn có thể tinh 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 các trang trong trang web của bạn có nút +1, trong khi một số trang khác thì không: bạn chỉ có thể cho phép mã nút được tải khi cần.

Danh sách nguồn trong mỗi lệnh có thể linh hoạt. Bạn có thể chỉ định các nguồn theo giao thức (data:, https:) hoặc 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ủ lưu trữ đó: giao thức bất kỳ, cổng bất kỳ) đến URI đủ điều kiện (https://example.com:443, chỉ khớp 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 mọi miền con của example.com (nhưng không phải example.com), bằng cách sử dụng bất kỳ lược đồ nào trên bất kỳ cổng nào.

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

  • 'none', như bạn có thể mong đợi, không khớp gì.
  • '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 miền đó.
  • 'unsafe-inline' cho phép JavaScript và CSS cùng dòng. (Chúng ta sẽ đề cập đến điều này chi tiết hơn một chút.)
  • 'unsafe-eval' cho phép các cơ chế chuyển văn bản sang JavaScript như eval. (Chúng ta cũng sẽ xem xét mục này.)

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

Hộp cát

Có một lệnh nữa đáng nói, đó là sandbox. Báo cáo này hơi khác với những mục khác mà chúng tôi đã xem xét, vì nó đặt ra hạn chế về các thao tác 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 xử lý như thể trang được tải bên trong <iframe> bằng thuộc tính sandbox. Điều này có thể có nhiều hiệu quả trên trang: buộc trang xác định một nguồn gốc duy nhất và ngăn việc gửi biểu mẫu, v.v. Nội dung này 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à một tiêu đề HTTP. Tuy nhiên, bạn nên đặ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 thuộc tính này cho frame-ancestors, report-uri hoặc sandbox.

Mã cùng dòng bị coi là có hại

Rõ ràng là CSP được dựa trên nguồn gốc của danh sách cho phép, vì đó là một cách rõ ràng để hướng dẫn trình duyệt xử lý các nhóm tài nguyên cụ thể là có thể 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: chèn tập lệnh cùng dòng. Nếu kẻ tấn công có thể chèn 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 không có cơ chế nào để phân biệt thẻ này với thẻ tập lệnh nội tuyến hợp pháp. CSP giải quyết vấn đề này bằng cách cấm hoàn toàn tập lệnh cùng dòng: đây là cách duy nhất để đảm bảo an toàn.

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 trong thẻ script mà còn áp dụng cho cả các trình xử lý sự kiện nội tuyến và URL javascript:. Bạn cần di chuyển nội dung của các thẻ script vào một tệp bên ngoài và thay thế các 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 các thông tin chi tiết hơn 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, cả trên và ngoài việc kết hợp hiệu quả với CSP. Đây là phương pháp hay nhất, bất kể bạn sử dụng CSP nào. JavaScript nội tuyến kết hợp cấu trúc và hành vi theo chính xác cách bạn không nên làm. Tài nguyên bên ngoài giúp trình duyệt dễ dàng lưu vào bộ nhớ đệm, dễ hiểu hơn đối với nhà phát triển, đồng thời tạo điều kiện cho quá trình biên dịch và giảm thiểu. Bạn sẽ viết mã tốt hơn nếu bạn xử lý xong việc di chuyển mã vào tài nguyên bên ngoài.

Kiểu cùng dòng đượ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 biểu định kiểu bên ngoài để ngăn chặn các phương thức đánh cắp dữ liệu thông minh một cách đáng kinh ngạc mà CSS cho phép.

Nếu phải có tập lệnh và kiểu cùng dòng, bạn có thể bật tập lệnh này bằng cách thêm 'unsafe-inline' làm nguồn được phép theo 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 dùng. Cấm tập lệnh cùng dòng là cách bảo mật lớn nhất mà CSP cung cấp, đồng thời, việc cấm kiểu cùng dòng cũng sẽ làm cứng ứng dụng của bạn. Bạn cần 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 ngoài dòng, nhưng đó là một sự đánh đổi cũng đáng giá.

Nếu bạn bắt buộc 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 cùng dòng bằng cách cho phép bạn thêm các tập lệnh cùng dòng 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 mật mã (số dùng một lần) hoặc hàm băm. Mặc dù việc này có thể rườm rà, nhưng sẽ hữu ích khi cần.

Để sử dụng số chỉ dùng một lần, hãy cung cấp thuộc tính số chỉ dùng một lần cho thẻ tập lệnh của bạn. Giá trị của nguồn 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 nonces phải được tạo lại cho mọi yêu cầu trang và phải không xác định được.

Hàm băm 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 mã đó vào lệnh script-src. Ví dụ: giả sử trang của bạn chứa:

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

Chính sách của bạn sẽ bao gồm:

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

Khi tìm kiếm trên Google về cách tạo hàm băm SHA, bạn sẽ tìm thấy các giải pháp bằng nhiều ngôn ngữ. Khi sử dụng Chrome 40 trở lên, bạn có thể mở Công cụ cho nhà phát triển rồi tải lại trang của mình. Thẻ Console sẽ chứa các thông báo lỗi có hàm băm sha256 chính xác cho từng tập lệnh cùng dòng.

Eval nữa

Ngay cả khi kẻ tấn công không thể chèn tập lệnh trực tiếp, chúng vẫn có thể lừa ứng dụng của bạn chuyển đổi văn bản thô thành JavaScript có thể thực thi và thay mặt chúng thực thi tập lệnh đó. eval(), new Function() , 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 nội dung độc hại không mong muố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.

Vấn đề này có nhiều tác độ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 thông qua JSON.parse tích hợp sẵn, thay vì dựa vào eval. Thao tác JSON gốc có sẵn trên mọi trình duyệt kể từ IE8 và hoàn toàn an toàn.
  • Viết lại bất kỳ lệnh gọi setTimeout hoặc setInterval nào mà bạn đang thực hiện bằng các hàm cùng dòng thay vì chuỗi. Ví dụ:
setTimeout("document.querySelector('a').style.display = 'none';", 10);

sẽ được viết tốt hơn là:

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() thoải mái để tăng tốc độ tạo mẫu trong thời gian chạy. Đây là một ứng dụng lập trình động tiện lợi, nhưng có rủi ro về việc đánh giá văn bản độc hại. Một số khung hỗ trợ CSP ngay từ đầu, sẽ quay lại sử dụng 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ề trường hợp này.

Tuy nhiên, lựa chọn tốt hơn là ngôn ngữ mẫu có khả năng tổng hợp (ví dụ: Thanh điều khiển có). Việc biên dịch trước các mẫu có thể giúp trải nghiệm người dùng thậm chí nhanh hơn so với cách triển khai nhanh nhất trong thời gian chạy và cách này cũng an toàn hơn. Nếu eval và các anh em chuyển văn bản sang JavaScript cần thiết cho ứng dụng của mình, thì bạn có thể bật chúng bằng cách thêm 'unsafe-eval' làm nguồn được phép theo lệnh script-src, nhưng bạn không nên làm như vậy. Việc cấm khả năng thực thi các 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 to lớn cho người dùng. Tuy nhiên, sẽ rất hữu ích nếu bạn gửi lại một số loại thông báo về máy chủ để có thể xác định và loại bỏ mọi lỗi cho phép chèn độc hại ngay từ đầu. Để đạt được mục tiêu này, bạn có thể hướng dẫn trình duyệt gửi các báo cáo vi phạm theo định dạng JSON POST đế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;

Các báo cáo đó sẽ trông giố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 nhiều 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 cả 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á 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 đó vi phạm (violated-directive) và chính sách hoàn chỉnh 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à nội cho người dùng. Để là bước đệm cho việc triển khai hoàn chỉnh, bạn có thể yêu cầu trình duyệt giám sát một chính sách, báo cáo các trường hợp 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 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. Bạn thậm chí có thể gửi cả hai tiêu đề, thực thi một chính sách trong khi theo dõi một chính sách khác. Đây là một cách hiệu quả để đá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 về một chính sách mới, theo dõi các báo cáo về lỗi 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 này, hãy bắt đầu thực thi chính sách mới.

Sử dụng trong thế giới thực

CSP 1 khá hữu ích trên Chrome, Safari và Firefox, nhưng hỗ trợ rất hạn chế trong IE 10. Bạn có thể xem thông tin cụ thể 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 đề (nghiên cứu điển hình của Twitter rất đáng để đọc) và tiêu chuẩn này đã sẵn sàng để bạn bắt đầu triển khai trên các trang web của 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ự tải. Khi bạn nghĩ mình đã hiểu rõ cách mọi thứ được tổng hợp trong ứng dụng, hãy thiết lập một chính sách dựa trên các yêu cầu đó. Hãy cùng xem qua một số trường hợp sử dụng phổ biến và xác định cách chúng ta có thể hỗ trợ tốt nhất các trường hợp này 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 <iframe> từ https://plusone.google.com. Bạn cần có một chính sách bao gồm cả hai nguồn 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 ở Cấp 1 sử dụng frame-src Cấp 2 yêu cầu bạn phải thay đổi chính sách đó thành child-src. Điều này không còn cần thiết trong CSP cấp 3 nữa.

  • Nút Thích của Facebook có một số cách triển khai. Bạn nên sử dụng phiên bản <iframe> vì phiên bản này được tạo hộp cát an toàn từ phần còn lại của trang web. Phương thức này cần có 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 là //facebook.com. Hãy thay đổi chế độ cài đặt đó để chỉ định rõ HTTPS: https://facebook.com. Không có lý do gì để sử dụng HTTP nếu bạn không cần phải làm vậy.

  • Nút Tweet của Twitter dựa vào quyền truy cập vào 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; hãy chỉnh sửa mã để chỉ định HTTPS khi sao chép/dán mã cục bộ). Bạn đã hoàn tất 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ó yêu cầu tương tự và có thể được giải quyết tương tự. Bạn chỉ nên đặt default-src với giá trị 'none' và xem bảng điều khiển để xác định những tài nguyên cần bật để giúp các tiện ích hoạt động.

Việc bao gồm nhiều tiện ích rất đơn giản: chỉ cần kết hợp các lệnh chính sách, nhớ hợp nhất tất cả các tài nguyên của một loại thành một lệnh duy nhất. Nếu bạn muốn 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 điều hành một trang web về 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 hoàn toàn mọi thứ (default-src 'none') và thiết lập từ đó.

Giả sử ngân hàng tải tất cả hình ảnh, kiểu và tập lệnh từ CDN tại https://cdn.mybank.net và kết nối qua XHR với https://api.mybank.com/ để kéo xuống nhiều bit dữ liệu khác nhau. Khung được sử dụng, nhưng chỉ cho các trang cục bộ trên 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ó 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

Quản trị viên của diễn đàn thảo luận về đám 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 mà không thực sự viết nhiều mã; việc viết lại phần lớn phần mềm diễn đàn của bên thứ ba chứa đầy tập lệnh và kiểu nội tuyến vượt quá khả năng của anh ấy. 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à lệ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 cho 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à Đề xuất cho ứng viên. 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 với phiên bản 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 muốn thảo luận về các tính năng sắp ra mắt này, hãy xem qua bản lưu trữ danh sách gửi thư public-webappsec@ hoặc tự tham gia.

Ý kiến phản hồi