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 có quyền truy cập vào dữ liệu của https://mybank.com
và https://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 lựa chọn hoàn hảo. Trên thực tế, những kẻ tấn công đã tìm ra những cách khéo léo để phá vỡ hệ thống.
Ví dụ: tấn công Tập lệnh trên nhiều trang web (XSS) nhằm 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 dự định. Đâ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 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ản tóm tắt về XSS là một mặt cắt cũ nhưng tiêu biểu về những phương thức mà kẻ tấn công có thể sử dụng để xâm 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ì trò chơi đã kết thúc: dữ liệu phiên của người dùng sẽ bị xâm phạm và thông tin cần được giữ bí mật sẽ bị đánh cắp và bị đánh cắp. Hiển nhiên là chúng tôi sẽ ngăn chặn điều đó 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ể 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 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á họ sử dụng.
- Mã cùng dòng và
eval()
được coi là gây hại. - Hãy 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.
Danh sách nguồn đã 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
giữa 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 vào một cách độc hại. Ví dụ: nút Google +1 ở cuối trang này 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ể kỳ vọng trình duyệt sẽ tự tìm ra mã
từ apis.google.com
thật tuyệt vời, trong khi mã từ apis.evil.example.com
có lẽ thì không. Trình duyệt rất thoải mái tải xuống và thực thi bất kỳ mã nào mà một trang yêu cầu, bất kể nguồn nào.
Thay vì tin tưởng một cách 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 gồm 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 kết xuất 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 và do đó sẽ không được thực thi.
Vì chúng ta tin tưởng apis.google.com
phân phối mã hợp lệ và chúng ta tin tưởng
sẽ làm đ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, đúng không? Như bạn có thể đoán, script-src
là một lệnh kiểm soát một nhóm 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 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 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 đây trong Chính sách bảo mật nội dung: script-src "self" của https://apis.google.com](https://developer.chrome.com/static/docs/privacy-security/csp/image/console-error-refused-l-524026adbb83a.png?authuser=5&hl=vi)
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 một thông báo lỗi chứ không phải là thành công như chúng ta 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 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 phần còn lại của các lệnh tài nguyên. 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 xuất bản, 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ế các URL có thể xuất hiện trong phần tử<base>
của trang.child-src
liệt kê URL của worker và nội dung khung được nhúng. Ví dụ:child-src https://youtube.com
sẽ bật tính năng 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ữ trên web. Bạn có thể bật phông chữ trên web của Google thông quafont-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. Lệnh này áp dụng cho các thẻ<frame>
,<iframe>
,<embed>
và<applet>
. Không thể 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
đã ngừng hoạt động trong cấp độ 2, nhưng sẽ đượ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 các nguồn gốc 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 chính sách bảo mật nội dung bị vi phạm. Không thể sử dụng lệnh này trong các thẻ<meta>
.style-src
là phiên bản tương đương củascript-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 giao thức URL, thay đổi HTTP thành HTTPS. Lệnh này dành cho các trang web có nhiều 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, chỉ thị này có giới hạn số lượng triển khai.
Theo mặc định, các lệnh sẽ mở rộng. Nếu bạn không đặt một chính sách cụ thể cho một lệnh, chẳng hạn như font-src
, thì lệnh đó sẽ hoạt động theo mặc định như mặc dù 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 chế độ mặc định cho hầu hết các lệnh mà bạn không chỉ định. Nhìn chung, lệnh 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 lệnh font-src
, thì bạn chỉ có thể tải phông chữ từ https://example.com
mà không tải phông chữ từ nơi nào khác. Chúng tôi chỉ chỉ định script-src
trong các ví dụ trước đây, nghĩa là hình ảnh, phông chữ, v.v. có thể tải từ mọi nguồn gốc.
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 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 tuỳ ý số lượng hoặc số lượng lệnh trong số này cho phù hợp với ứng dụng cụ thể của mình. Bạn 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 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
một kiểu như script-src https://host1.com; script-src https://host2.com
,
lệnh thứ hai sẽ bị bỏ qua. Thông số tương tự 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 (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ợ nào có khung, thì chính sách của bạn có thể có dạ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-CSP
và X-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) đều 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ể tiêu đề bạn sử dụng là gì, chính sách đều đượ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 rằng sẽ đượ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 trang trong trang web của bạn có nút +1, trong khi các trang khác thì không: bạn chỉ có thể 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 các nguồn theo giao thức (data:
, https:
) hoặc trong phạm vi cụ thể từ chỉ tên máy chủ (example.com
, khớp với mọi điểm gốc trên máy chủ đó: giao thức, cổng bất kỳ) đến URI đủ điều kiện (https://example.com:443
, chỉ khớp với HTTPS, chỉ example.com
và chỉ khớp với 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 bất kỳ lược đồ nào, trên mọi cổng.
Danh sách nguồn cũng chấp nhận 4 từ khoá:
'none'
, như bạn dự kiến, không khớp với giá trị nào.'self'
khớp với nguồn gốc hiện tại, nhưng không khớp với miền con của nguồn này.'unsafe-inline'
cho phép CSS và JavaScript cùng dòng. (Chúng ta sẽ nói về vấn đề 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ẽ thảo luận về vấn đề này.)
Những từ khoá này cần có 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ủ 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 là "self
" (và không phải từ máy chủ hiện tại) và có thể đây không phải là ý bạn muốn nói.
Cơ chế hộp cát
Còn một lệnh nữa đáng nói: sandbox
. Kết quả này hơi khác so với những nội dung khác mà chúng tôi xem xét vì nó đặt ra các hạn chế đối với hành động mà trang có thể thực hiện thay vì tài nguyên có thể tải trang. Nếu có lệnh sandbox
, trang sẽ được xử lý như thể đã được tải bên trong <iframe>
bằng thuộc tính sandbox
. Cách này có thể gây ra nhiều ảnh hưởng trên trang: buộc trang thành 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 nên đặt chính sách trên một trang ngay trong mã đánh dấu. Bạn có thể làm 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à gây hại
Cần làm rõ rằng CSP 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 xem các nhóm tài nguyên cụ thể là có thể chấp nhận và từ chối những nhóm tài nguyê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 cùng dòng: đó là cách duy nhất để chắc chắn.
Lệnh cấm này không chỉ áp dụng cho các tập lệnh được nhúng trực tiếp trong 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 cần di chuyển nội dung của các thẻ script
vào tệp bên ngoài, đồng thời thay thế các URL javascript:
và <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 như 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ã viết lại có một số ưu điểm vượt trội hơn và ngoài việc hoạt động 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 đúng cách bạn không nên. Các tài nguyên bên ngoài giúp trình duyệt lưu vào bộ nhớ đệm dễ dàng hơn, dễ hiểu hơn đối với nhà phát triển, đồng thời giúp biên dịch và giảm kích thước. 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 cùng dòng được xử lý theo cùng cách: cả thuộc tính style
và thẻ style
đều phải được hợp nhất vào các biểu định kiểu bên ngoài để ngăn chặn nhiều phương thức đánh cắp dữ liệu thông minh đến mức đáng ngạc nhiên mà CSS hỗ trợ.
Nếu phải có kiểu và tập lệnh cùng dòng, 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 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 làm vậy.
Cấm tập lệnh cùng dòng là biện pháp bảo mật hiệu quả nhất mà CSP mang lại. Việc cấm kiểu cùng dòng cũng giúp tăng cường bảo mật cho ứ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 chuyển toàn bộ mã ra ngoài luồng, nhưng đó cũng là một sự đánh đổi rất đáng để thực hiện.
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 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 một số chỉ dùng một lần mã hoá (số được dùng một lần) hoặc một hàm băm. Mặc dù thao tác này có thể rườm rà, nhưng sẽ hữu ích trong một khoảng thời gian ngắn.
Để 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 một thuộc tính số chỉ dùng một lần. Giá trị của lớp này phải khớp với 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 số chỉ dùng một lần phải được tạo lại cho mọi yêu cầu trang và chúng phải là không thể sử dụng.
Hàm băm cũng hoạt động tương tự như vậy. 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 có chứa:
<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-
và sha512-
. Khi tạo hàm băm, đừng sử dụng các 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 sẽ giúp bạn tìm ra các giải pháp bằng vô số 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 với hàm băm sha256 chính xác cho từng tập lệnh cùng dòng.
Cả Eval
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 trơ thành JavaScript thực thi và thay mặt kẻ tấn công thực thi tệp đó. eval()
, Function() mới, setTimeout([string], ...)
và setInterval([string], ...)
đều là các vectơ mà qua đó văn bản được chèn có thể thực thi điều gì đó độ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.
Điều này có một vài 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 qua
JSON.parse
tích hợp sẵn thay vì dựa vàoeval
. Hoạt động 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ặcsetInterval
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);
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()
một cách thoải mái để tăng tốc việc tạo mẫu trong thời gian chạy. Đây là một ứng dụng lập trình động hữu ích, 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 sử dụng một 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 cho trường hợp 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 (chẳng hạn như Thanh công cụ chẳng hạn). Việc biên dịch trước các mẫu có thể giúp trải nghiệm người dùng nhanh hơn so với tốc độ triển khai trong thời gian chạy nhanh nhất, đồng thời 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 bạn, 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 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 nhiều.
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. Để đạt được mục tiêu này, bạn có thể hướng dẫn trình duyệt báo cáo lỗi 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 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.
Để tạo bước đệm cho quá trình 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 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. 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 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 cho một chính sách mới, theo dõi các 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 đó, 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 chỉ hỗ trợ rất hạn chế trong IE 10. Bạn có thể xem thông tin cụ thể tại caniuse.com. Chính sách bảo mật nội dung (CSP) cấp 2 đã có trong Chrome kể từ phiên bản 40. Các trang web hàng loạt 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 rất sẵn sàng để bạn bắt đầu triển khai trên các 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 đang thực sự tải. Khi bạn cho rằng mình đã nắm được cách tổng hợp các thành phần 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 tốt nhất chúng tôi có thể hỗ trợ các trường hợp này trong giới hạn 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 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ụngframe-src
Cấp 2, bạn cần thay đổi chính sách này thànhchild-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ố cách 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. Hàm này cần có lệnhchild-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ế độ đó để chỉ định rõ 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 tập lệnh và 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ẽ thiết lập xong vớiscript-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 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
là'none'
và xem bảng điều khiển để xác định những tài nguyên bạn sẽ cần bật để giúp 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 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 với một chính sách mặc định chặn hoàn toà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ừ CDN tại https://cdn.mybank.net
và kết nối qua XHR với https://api.mybank.com/
để kéo các bit dữ liệu xuống. Khung được sử dụng, nhưng chỉ cho các trang cục bộ đến trang web (không có nguồn gốc của bên thứ ba). Không có Flash trên trang web, không có phông chữ, không có phần bổ sung. Tiêu đề CSP có tính 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 diễn đàn thảo luận đá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ết lại phần lớn phần mềm diễn đàn bên thứ ba đã được lấp đầy bằng tập lệnh và kiểu nội tuyến vượt quá khả năng của anh. 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 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 dành 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 nghiên cứu 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 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 lướt qua kho lưu trữ danh sách gửi thư Public-webappsec@ hoặc tự mình tham gia.