Tính toàn vẹn của tài nguyên con
Subresource Integrity (SRI) là một tính năng bảo mật cho phép trình duyệt xác minh rằng các tài nguyên mà chúng tìm nạp, ví dụ từ một CDN, được phân phối mà không có sự can thiệp ngoài mong muốn. Nó hoạt động bằng cách cho phép bạn cung cấp một giá trị băm mật mã mà tài nguyên được tìm nạp phải khớp.
Note: Đối với việc xác minh subresource-integrity của một tài nguyên được phục vụ từ một origin khác với tài liệu mà nó được nhúng vào, trình duyệt còn kiểm tra tài nguyên bằng Cross-Origin Resource Sharing (CORS), để đảm bảo origin đang phục vụ tài nguyên cho phép tài nguyên đó được chia sẻ với origin yêu cầu.
Cách Subresource Integrity hỗ trợ
Các website đôi khi chọn phụ thuộc vào một bên thứ ba như một Content Delivery Network (CDN) để lưu trữ một số tài nguyên của họ, thay vì tự lưu trữ tất cả. Ví dụ, một tài liệu được phục vụ từ https://example.com có thể bao gồm một tài nguyên từ một vị trí khác:
<script src="https://not-example.com/script.js"></script>
Điều này đi kèm rủi ro, vì nếu kẻ tấn công giành được quyền kiểm soát host bên thứ ba, kẻ tấn công có thể chèn nội dung độc hại tùy ý vào các tệp của nó (hoặc thay thế hoàn toàn các tệp) và do đó cũng có thể tấn công các site tìm nạp tệp từ đó.
Subresource Integrity cho phép bạn giảm thiểu một phần rủi ro của những cuộc tấn công như vậy, bằng cách đảm bảo rằng các tệp mà ứng dụng web hoặc tài liệu web của bạn tìm nạp đã được phân phối mà không có việc kẻ tấn công chèn thêm bất kỳ nội dung nào vào các tệp đó - và không có bất kỳ thay đổi nào khác dưới bất kỳ hình thức nào đã được thực hiện đối với các tệp đó.
Sử dụng Subresource Integrity
Bạn sử dụng tính năng Subresource Integrity bằng cách chỉ định một giá trị băm mật mã được mã hóa base64 của một tài nguyên (tệp) mà bạn yêu cầu trình duyệt tìm nạp, trong giá trị của thuộc tính integrity của một phần tử <script> hoặc một phần tử <link> với rel="stylesheet", rel="preload", hoặc rel="modulepreload".
Giá trị integrity bắt đầu bằng ít nhất một chuỗi, trong đó mỗi chuỗi bao gồm một tiền tố chỉ ra một thuật toán băm cụ thể (hiện tại các tiền tố hợp lệ là sha256, sha384, và sha512), theo sau là dấu gạch ngang, và kết thúc bằng giá trị băm base64 thực tế.
Note: Một giá trị integrity có thể chứa nhiều giá trị băm được phân tách bằng khoảng trắng. Một tài nguyên sẽ được tải nếu nó khớp với một trong các giá trị băm đó.
Ví dụ chuỗi integrity với giá trị băm sha384 được mã hóa base64:
sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC
Vì vậy oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC là phần "hash", còn tiền tố sha384 cho biết đây là một hash sha384.
Note:
Phần "hash" của một giá trị integrity, nói chính xác hơn, là một digest mật mã được tạo bằng cách áp dụng một hàm băm cụ thể lên một đầu vào nào đó (ví dụ, một tệp script hoặc stylesheet). Nhưng người ta thường dùng cách gọi tắt "hash" để chỉ cryptographic digest, nên đó là cách dùng trong bài viết này.
Công cụ tạo SRI hash
SRI Hash Generator
SRI Hash Generator là một công cụ trực tuyến mà bạn có thể dùng để tạo SRI hash.
Dùng OpenSSL
Bạn có thể tạo SRI hash từ dòng lệnh bằng OpenSSL với một lệnh như sau:
cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A
Trong môi trường Windows, bạn có thể tạo một công cụ để sinh SRI hash bằng đoạn mã sau:
@echo off
set bits=384
openssl dgst -sha%bits% -binary %1% | openssl base64 -A > tmp
set /p a= < tmp
del tmp
echo sha%bits%-%a%
pause
Để dùng đoạn mã đó:
- Lưu đoạn mã đó vào một tệp tên
sri-hash.battrong thư mục Windows SendTo của môi trường bạn (ví dụ:C:\Users\USER\AppData\Roaming\Microsoft\Windows\SendTo). - Nhấp chuột phải vào một tệp trong File Explorer, chọn Send to…, rồi chọn
sri-hash. Bạn sẽ thấy giá trị integrity trong một hộp lệnh. - Chọn giá trị integrity và nhấp chuột phải để sao chép nó vào Clipboard.
- Nhấn bất kỳ phím nào để đóng hộp lệnh.
Note: Nếu OpenSSL chưa được cài đặt trên hệ thống của bạn, hãy truy cập trang dự án OpenSSL để biết thông tin về cách tải xuống và cài đặt nó. Bản thân dự án OpenSSL không lưu trữ các bản phân phối nhị phân của OpenSSL, nhưng có duy trì một danh sách không chính thức các bản phân phối từ bên thứ ba: https://github.com/openssl/openssl/wiki/Binaries.
Dùng shasum
Bạn có thể tạo SRI hash bằng shasum với một lệnh như sau:
shasum -b -a 384 FILENAME.js | awk '{ print $1 }' | xxd -r -p | base64
- Bước pipe qua
xxdlấy đầu ra dạng thập lục phân từshasumvà chuyển nó thành nhị phân. - Bước pipe qua
awklà cần thiết vìshasumsẽ đưa tên tệp đã băm vào đầu ra của nó choxxd. Điều này có thể gây hậu quả nghiêm trọng nếu tên tệp tình cờ chứa các ký tự hex hợp lệ - vìxxdcũng sẽ giải mã phần đó và truyền nó tớibase64.
Chia sẻ tài nguyên chéo origin và Subresource Integrity
Đối với việc xác minh subresource-integrity của một tài nguyên được phục vụ từ một origin khác với tài liệu mà nó được nhúng vào, trình duyệt còn kiểm tra tài nguyên bằng Cross-Origin Resource Sharing (CORS), để đảm bảo origin đang phục vụ tài nguyên cho phép tài nguyên đó được chia sẻ với origin yêu cầu. Vì vậy, tài nguyên phải được phục vụ với một tiêu đề Access-Control-Allow-Origin cho phép tài nguyên đó được chia sẻ với origin yêu cầu; ví dụ:
Access-Control-Allow-Origin: *
Cách trình duyệt xử lý Subresource Integrity
Trình duyệt xử lý SRI bằng cách thực hiện như sau:
-
Khi trình duyệt gặp một phần tử
<script>hoặc<link>có thuộc tínhintegrity, trước khi thực thi script hoặc trước khi áp dụng bất kỳ stylesheet nào được phần tử<link>chỉ định, trình duyệt phải trước tiên so sánh script hoặc stylesheet với giá trị băm mong đợi được cung cấp trong giá trịintegrity.Đối với việc xác minh subresource-integrity của một tài nguyên được phục vụ từ một origin khác với tài liệu mà nó được nhúng vào, trình duyệt còn kiểm tra tài nguyên bằng Cross-Origin Resource Sharing (CORS), để đảm bảo origin đang phục vụ tài nguyên cho phép tài nguyên đó được chia sẻ với origin yêu cầu.
-
Nếu script hoặc stylesheet không khớp với giá trị
integritycủa nó, trình duyệt phải từ chối thực thi script hoặc áp dụng stylesheet, và thay vào đó phải trả về một lỗi mạng cho biết việc tìm nạp script hoặc stylesheet đó thất bại.
Chính sách toàn vẹn
Các tiêu đề HTTP Integrity-Policy và Integrity-Policy-Report-Only cho phép một tài liệu thực thi chính sách về các yêu cầu siêu dữ liệu toàn vẹn đối với các subresource script và stylesheet được tải.
Khi tiêu đề Integrity-Policy được chỉ định, trình duyệt sẽ chặn các yêu cầu có chế độ no-cors hoặc không có thuộc tính integrity, và cũng sẽ báo cáo vi phạm nếu có chỉ định một reporting endpoint hợp lệ.
Khi tiêu đề Integrity-Policy-Report-Only được chỉ định, trình duyệt cho phép các yêu cầu vi phạm chính sách, nhưng sẽ báo cáo vi phạm tới reporting endpoint (nếu có chỉ định một reporting endpoint hợp lệ).
Thông thường, các nhà phát triển sẽ dùng Integrity-Policy-Report-Only như bước triển khai đầu tiên trong hành trình áp dụng Integrity Policy, để đảm bảo rằng tất cả script và stylesheet được tải trong tài liệu của họ đều có siêu dữ liệu toàn vẹn phù hợp. Khi thấy rằng không còn nhận được báo cáo vi phạm nào, họ sẽ biết rằng có thể bật chặn bằng tiêu đề Integrity-Policy mà không lo gây hỏng trải nghiệm của người dùng.
Giá trị tiêu đề được định nghĩa như các structured field dictionary với các khóa sau:
blocked-destinations-
Xác định danh sách các request destinations sẽ bị chặn. Các giá trị duy nhất được phép là
scriptvàstyle. sourcesOptional-
Xác định danh sách các integrity source. Giá trị mặc định và duy nhất hiện được hỗ trợ là
inline. Vì vậy, thêmsources=(inline)vào tiêu đề có tác dụng tương tự như bỏ quasources. endpointsOptional-
Xác định danh sách các reporting endpoints. Các reporting endpoint cần được định nghĩa trong tiêu đề
Reporting-Endpoints.
Trong các trường hợp một yêu cầu bị chặn bởi integrity policy, một báo cáo vi phạm Reporting API sẽ được tạo ra với thuộc tính type là integrity-violation và cấu trúc được định nghĩa bởi IntegrityViolationReport, bao gồm các thông tin như URL của tài liệu và tài nguyên bị chặn.
Một báo cáo điển hình có thể trông như sau
{
"type": "integrity-violation",
"url": "https://example.com",
"body": {
"documentURL": "https://example.com",
"blockedURL": "https://example.com/main.js",
"destination": "script",
"reportOnly": false
}
}
Ví dụ
Trong các ví dụ sau, giả sử oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC đã được biết là giá trị băm SHA-384 mong đợi (digest) của một script cụ thể example-framework.js, và có một bản sao của script đó được lưu trữ tại https://example.com/example-framework.js.
Subresource Integrity với phần tử <script>
Bạn có thể dùng phần tử <script> sau để báo cho trình duyệt rằng trước khi thực thi script https://example.com/example-framework.js, trình duyệt phải trước tiên so sánh script với giá trị băm mong đợi và xác minh rằng có khớp.
<script
src="https://example.com/example-framework.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
Note:
Để biết thêm chi tiết về mục đích của thuộc tính crossorigin, xem CORS settings attributes.
Thực thi integrity với tiêu đề Integrity-Policy
Bạn có thể thêm tiêu đề Integrity-Policy vào tài liệu của mình để đảm bảo rằng các tài nguyên bên ngoài mà nó tải (trong trường hợp này là scripts) được tải kèm integrity (và không được tải ở chế độ no-cors)
Integrity-Policy: blocked-destinations=(script), endpoints=(integrity-endpoint, some-other-integrity-endpoint)
Nếu bạn không chắc rằng tất cả các script bên ngoài đều có siêu dữ liệu integrity, bạn có thể bật phiên bản chỉ báo cáo của tính năng và bắt đầu nhận báo cáo vi phạm.
Bạn có thể làm điều đó bằng tiêu đề Integrity-Policy-Report-Only.
Integrity-Policy-Report-Only: blocked-destinations=(script), endpoints=(integrity-endpoint, some-other-integrity-endpoint)
Đặc tả
| Specification |
|---|
| HTML> # attr-link-integrity> |
| HTML> # attr-script-integrity> |
| Subresource Integrity> # the-integrity-attribute> |