Khi tải tập lệnh, biểu định kiểu, phông chữ hoặc hình ảnh từ CDN của bên thứ ba hoặc nguồn gốc bên ngoài, hãy xác minh tính toàn vẹn của mật mã bằng tính năng Tính toàn vẹn của tài nguyên phụ (integrity trong HTML và integrity() trong CSS url()), định cấu hình cách xử lý thông tin xác thực trên nhiều nguồn gốc (crossorigin và cross-origin()) và hạn chế tiêu đề Referer gửi đi (referrerpolicy và referrer-policy()). Để biết Chính sách bảo mật nội dung ở cấp tài liệu và danh sách cho phép kết nối gửi đi, hãy xem phần Bảo mật web và Hạn chế kết nối mạng gửi đi bằng Danh sách cho phép kết nối.
1. Tạo thông báo tóm tắt về tính toàn vẹn của mật mã cho các thành phần không thể thay đổi
Tính toàn vẹn của tài nguyên phụ (SRI) đảm bảo trình duyệt chỉ thực thi hoặc hiển thị một thành phần bên ngoài nếu byte của thành phần đó khớp với giá trị băm mật mã do ứng dụng của bạn khai báo.
- Tính toán chuỗi đại diện tại thời điểm tạo bản dựng hoặc phát hành, không bao giờ tính toán từ các phản hồi trực tiếp không đáng tin cậy: Tạo hàm băm toàn vẹn trong bước tạo bản dựng hoặc phát hành từ các byte tài sản cục bộ đã biết là tốt (hoặc sao chép chuỗi đại diện chính thức do tác giả gói phát hành) và cố định giá trị trong mã vào HTML, CSS hoặc JavaScript của bạn. Không bao giờ tìm nạp URL của bên thứ ba trong trình duyệt trong thời gian chạy để tính toán hàm băm
integritycủa riêng URL đó, vì việc này sẽ tin tưởng bất kỳ byte nào mà máy chủ từ xa trả về và làm mất tác dụng của SRI. - Sử dụng
sha384-theo mặc định: Định dạng siêu dữ liệu về tính toàn vẹn dưới dạng<algorithm>-<base64-digest>bằngsha384-(mặc định được đề xuất),sha512-hoặcsha256-. - Ghim tài nguyên phụ bên ngoài vào URL không thể thay đổi, có phiên bản: Luôn tham chiếu tài sản của bên thứ ba bằng URL không thể thay đổi, được ghim phiên bản hoặc được định địa chỉ nội dung (chẳng hạn như
https://cdn.example.com/ui@2.4.0/app.min.js) thay vì URL động có thể thay đổi (latest/app.min.js), khiến trang web của bạn gặp phải những thay đổi thầm lặng trong chuỗi cung ứng và làm hỏng SRI ngay khi tệp từ xa cập nhật. Nếu bạn phải tải một tập lệnh bên thứ ba có thể thay đổi và cập nhật tại chỗ (chẳng hạn như thẻ phân tích của nhà cung cấp), hãy bỏ quaintegritytrên URL đó và hạn chế URL đó bằng Chính sách bảo mật nội dung (strict-dynamic/nonce) hoặc hộp cát iframe. - Phân tách nhiều chuỗi băm bằng dấu cách khi một điểm cuối có thể phân phát nhiều tải trọng hợp lệ: Tất cả trình duyệt hỗ trợ SRI đều triển khai
sha256-,sha384-vàsha512-. Nếu một URL có thể trả về một cách hợp pháp một trong số nhiều tải trọng hợp lệ đã biết (chẳng hạn như trong quá trình thử nghiệm A/B, triển khai CDN theo giai đoạn hoặc bản dựng được thương lượng nội dung), hãy liệt kê nhiều chuỗi đại diện của cùng một thuật toán, phân tách bằng dấu cách (ví dụ:integrity="sha384-variantA... sha384-variantB..."). Trình duyệt sẽ chấp nhận phản hồi nếu phản hồi đó khớp với bất kỳ chuỗi đại diện nào được liệt kê của thuật toán mạnh nhất hiện có. - Phân phát tài nguyên phụ trên nhiều nguồn gốc bằng tiêu đề CORS: Trình duyệt chỉ có thể xác minh tính toàn vẹn của phản hồi trên nhiều nguồn gốc khi phản hồi đó có thể đọc được bằng CORS (
Access-Control-Allow-Origin: *cho các yêu cầu ẩn danh hoặc nguồn gốc yêu cầu chính xác bằngAccess-Control-Allow-Credentials: truecho các yêu cầu có thông tin xác thực).
# Generate a sha384- SRI hash at build time from a known-good local file
printf "sha384-%s\n" "$(openssl dgst -sha384 -binary app.min.js | openssl base64 -A)"
// Build-time / tooling helper (Node.js 19+, Deno, Bun) to compute an SRI string from known-good local bytes
export async function computeIntegrityMetadata(bytes) {
const rawBytes =
typeof bytes === 'string' ? new TextEncoder().encode(bytes) : bytes;
const digestBuffer = await crypto.subtle.digest('SHA-384', rawBytes);
const binary = Array.from(new Uint8Array(digestBuffer), (b) =>
String.fromCharCode(b),
).join('');
return `sha384-${btoa(binary)}`;
}
2. Bảo mật tập lệnh HTML, biểu định kiểu và tải trước
Đặt tiêu đề phản hồi HTTP Referrer-Policy: strict-origin-when-cross-origin ở cấp tài liệu (hoặc thêm <meta name="referrer" content="strict-origin-when-cross-origin"> vào <head>) làm giá trị mặc định cơ sở để mọi tài nguyên phụ hoặc url() CSS bỏ qua đối tượng sửa đổi đường liên kết giới thiệu theo yêu cầu vẫn loại bỏ các tham số truy vấn và đường dẫn nhạy cảm trong các yêu cầu khác nguồn gốc.
Sau đó, trên các phần tử <script>, <link rel="stylesheet">, <link rel="preload"> và <link rel="modulepreload"> bên ngoài, hãy luôn đặt integrity, crossorigin và referrerpolicy cùng nhau:
integrity="sha384-...": Chặn việc thực thi hoặc áp dụng tài nguyên nếu byte phản hồi không khớp với thông báo dự kiến (đối với<link rel="stylesheet">bị chặn,linkElement.sheetvẫn lànullvà sự kiệnerrorkích hoạt).crossorigin="anonymous"(mặc định được đề xuất cho các thành phần CDN công khai) hoặccrossorigin="use-credentials": Bắt buộc bất cứ khi nàointegrityđược đặt trên một tài nguyên khác nguồn gốc. Việc bỏ quacrossoriginsẽ khiến trình duyệt tìm nạp tài nguyên ở chế độno-cors(tạo ra phản hồi mờ không thể xác minh bằng hàm băm) và chặn tài nguyên.referrerpolicy="no-referrer"(mặc định được đề xuất cho CDN của bên thứ ba) hoặcreferrerpolicy="strict-origin-when-cross-origin": Ghi đè chính sách liên kết giới thiệu tài liệu để ngăn chặn việc rò rỉ bất kỳ thông tin nào về nguồn gốc hoặc đường dẫn trong tiêu đề của yêu cầuRefererđến máy chủ lưu trữ của bên thứ ba.
<!-- Document-level baseline referrer policy in <head> (or via Referrer-Policy HTTP header) -->
<meta name="referrer" content="strict-origin-when-cross-origin" />
<!-- External stylesheet with SRI, anonymous CORS, and no referrer -->
<link
rel="stylesheet"
href="https://cdn.example.com/ui@2.4.0/styles.css"
integrity="sha384-bs0EksOYBbU/g0IAHMXbSyH1Mgw83GKn+EZlSI+VE+zVchXmSbLwhVLrPzsOWnc5"
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<!-- External script with SRI, anonymous CORS, and no referrer -->
<script
src="https://cdn.example.com/ui@2.4.0/bundle.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
Khi tìm nạp tài nguyên phụ bên ngoài theo phương thức lập trình bằng fetch(), hãy truyền các tuỳ chọn integrity, mode, credentials và referrerPolicy phù hợp (nếu thông báo tóm tắt không khớp, lời hứa sẽ bị từ chối bằng TypeError):
const response = await fetch('https://cdn.example.com/ui@2.4.0/manifest.json', {
integrity: 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC',
mode: 'cors',
credentials: 'omit',
referrerPolicy: 'no-referrer',
});
3. Bảo mật hình ảnh, phông chữ và biểu định kiểu đã nhập của CSS bằng đối tượng sửa đổi url()
Đối tượng sửa đổi yêu cầu CSS url() (cross-origin(), referrer-policy() và integrity()) kiểm soát chế độ tìm nạp, tiêu đề liên kết giới thiệu và quy trình xác minh Tính toàn vẹn của tài nguyên phụ đối với các tài nguyên do CSS tải (bao gồm cả các quy tắc background-image, content, mask-image, border-image-source, filter, @font-face src và @import) ngay trong CSS:
| Hàm sửa đổi | Cú pháp đối số và giá trị được phép | Mục đích |
|---|---|---|
cross-origin(<ident>) |
Từ khoá không được đặt trong dấu ngoặc kép: anonymous (mặc định được đề xuất) hoặc use-credentials |
Tìm nạp tài nguyên CSS bằng CORS và kiểm soát việc có gửi thông tin đăng nhập hay không. Luôn thêm cùng với integrity() trên tài nguyên CSS khác nguồn gốc. |
referrer-policy(<ident>) |
Từ khoá không được đặt trong dấu ngoặc kép: no-referrer (mặc định được đề xuất cho thành phần của bên thứ ba), strict-origin-when-cross-origin, origin, same-origin, strict-origin, origin-when-cross-origin, no-referrer-when-downgrade hoặc unsafe-url |
Ghi đè chính sách liên kết giới thiệu tài liệu cho yêu cầu CSS cụ thể này. |
integrity("<string>") |
Chuỗi được trích dẫn: "sha384-<base64>" (hoặc chuỗi tóm tắt được phân tách bằng dấu cách "sha384-... sha384-...") |
Xác minh tài nguyên CSS đã tìm nạp dựa trên thông báo tóm tắt mật mã dự kiến trước khi hiển thị hoặc áp dụng tài nguyên đó. |
Hãy tuân theo các quy tắc bảo mật và cú pháp CSS sau đây khi tạo đối tượng sửa đổi url():
- Đặt chuỗi URL trong dấu ngoặc kép bên trong
url("..."): Đối tượng sửa đổi yêu cầu chỉ hợp lệ khi URL bên trongurl(...)là một<string>được đặt trong dấu ngoặc kép (URL không được đặt trong dấu ngoặc kép không thể sử dụng đối tượng sửa đổi). - Chỉ định mỗi loại đối tượng sửa đổi tối đa một lần cho mỗi
url(): Bạn có thể kết hợpcross-origin(),referrer-policy()vàintegrity()theo thứ tự bất kỳ bên trongurl("..." ...), nhưng việc lặp lại cùng một loại đối tượng sửa đổi hai lần (chẳng hạn như hai đối tượng sửa đổiintegrity()) là không hợp lệ. Để cung cấp nhiều hàm băm, hãy phân tách chúng bằng dấu cách trong một chuỗiintegrity("sha384-... sha384-...")duy nhất. - Luôn ghép nối
integrity()khác nguồn gốc vớicross-origin(anonymous): Mặc dù@font-facesrcvàmask-imageđã tìm nạp ở chế độcorsẩn danh theo mặc định, nhưngbackground-image,content,border-image-sourcevà@importmặc định ở chế độno-cors(tạo ra phản hồi mờ không thể xác minh bằng hàm băm) và SVGfilterbên ngoài mặc định ở chế độsame-origin(chặn hoàn toàn các URL khác nguồn gốc). Việc thêmcross-origin(anonymous)(hoặccross-origin(use-credentials)) đảm bảo quy trình xác minh CORS nhất quán trên mọi thuộc tính CSS. - Đặt đối tượng sửa đổi bên trong
url("...")trong quy tắc@importvà@font-face: Giữcross-origin(),referrer-policy()vàintegrity()bên trongurl("..." ...), đồng thời giữ các chỉ số mô tả phông chữ nhưformat("woff2")vàtech(...)bên ngoàiurl(...).
@import url(
"https://cdn.example.com/theme@1.2.0/tokens.css"
cross-origin(anonymous)
referrer-policy(no-referrer)
integrity("sha384-bs0EksOYBbU/g0IAHMXbSyH1Mgw83GKn+EZlSI+VE+zVchXmSbLwhVLrPzsOWnc5")
);
@font-face {
font-family: "BrandSans";
src: url(
"https://cdn.example.com/fonts@1.0.0/brand-sans.woff2"
cross-origin(anonymous)
referrer-policy(no-referrer)
integrity("sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC")
) format("woff2");
font-display: swap;
}
.verified-graphic {
background-image: url(
"https://cdn.example.com/media@1.0.0/badge.svg"
cross-origin(anonymous)
referrer-policy(no-referrer)
integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91")
);
}
Chiến lược dự phòng
Nếu mục tiêu Baseline của bạn chưa hỗ trợ các đối tượng sửa đổi yêu cầu CSS url() (cross-origin(), referrer-policy() hoặc integrity()), hãy kết hợp các chính sách cấp tài liệu với các nội dung khai báo CSS xếp tầng để các tài nguyên phụ vẫn hoạt động và được bảo vệ:
- Ưu tiên HTML
<link rel="stylesheet">hơn CSS@importkhi cần có SRI trên nhiều trình duyệt: Vìsubresource-integrityvàreferrer-policytrên<link rel="stylesheet">được cung cấp rộng rãi trên tất cả trình duyệt, hãy tải biểu định kiểu bên ngoài bằng<link rel="stylesheet" href="..." integrity="sha384-..." crossorigin="anonymous" referrerpolicy="no-referrer">thay vì@importbất cứ khi nào bạn phải thực thi quy trình xác minh tính toàn vẹn trên mọi trình duyệt. - Dựa vào tiêu đề
Referrer-Policyhoặc<meta name="referrer">ở cấp tài liệu từ Bước 2: Trong các trình duyệt không hỗ trợreferrer-policy()bên trong CSSurl(), phần tửReferrer-Policy: strict-origin-when-cross-originhoặc<meta name="referrer" content="strict-origin-when-cross-origin">ở cấp tài liệu sẽ tự động loại bỏ đường dẫn nhạy cảm và tham số truy vấn trên các yêu cầu CSS khác nguồn gốc. Xếp tầng các nội dung khai báo CSS riêng biệt để cải tiến tăng dần: Các trình duyệt không nhận dạng được đối tượng sửa đổi bên trong
url("..." ...)sẽ loại bỏ toàn bộ nội dung khai báo tại thời điểm phân tích cú pháp CSS. Trước tiên, hãy khai báo mộturl("...")đơn giản, sau đó là khai báocross-origin()vàreferrer-policy()(cho các trình duyệt hỗ trợ hai đối tượng sửa đổi đó nhưng không hỗ trợintegrity()), sau đó là khai báo đầy đủ bao gồmintegrity("sha384-..."):- Tuyệt đối không đặt
url()chưa được xác minh vào cùng một danh sách@font-facesrcđược phân tách bằng dấu phẩy: Việc viếtsrc: url("font.woff2" cross-origin(anonymous) integrity("sha384-...")), url("font.woff2");trong một chỉ số mô tảsrcduy nhất sẽ bỏ qua Tính toàn vẹn của tài nguyên phụ trong thời gian chạy. Nếu CDN phân phát một phông chữ bị can thiệp và không vượt qua được quy trình kiểm traintegrity(), thì trình duyệt sẽ coi đó là lỗi tải và tự động quay lạiurl("font.woff2")thứ hai được phân tách bằng dấu phẩy. Việc sử dụng các nội dung khai báo riêng biệtsrc:đảm bảo rằng các trình duyệt hỗ trợ sẽ ghi đè nội dung khai báo chưa được xác minh tại thời điểm phân tích cú pháp CSS và chặn các phông chữ bị can thiệp tại thời điểm chạy.
@font-face { font-family: "BrandSans"; /* 1. Parse-time fallback for browsers without url() modifier support */ src: url("https://cdn.example.com/fonts@1.0.0/brand-sans.woff2") format("woff2"); /* 2. Parse-time fallback for browsers supporting cross-origin() and referrer-policy() */ src: url( "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2" cross-origin(anonymous) referrer-policy(no-referrer) ) format("woff2"); /* 3. Full protection in browsers supporting cross-origin(), referrer-policy(), and integrity() */ src: url( "https://cdn.example.com/fonts@1.0.0/brand-sans.woff2" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC") ) format("woff2"); font-display: swap; } .verified-graphic { background-image: url("https://cdn.example.com/media@1.0.0/badge.svg"); background-image: url( "https://cdn.example.com/media@1.0.0/badge.svg" cross-origin(anonymous) referrer-policy(no-referrer) ); background-image: url( "https://cdn.example.com/media@1.0.0/badge.svg" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91") ); }- Tuyệt đối không đặt
Phát hiện tính năng hỗ trợ công cụ sửa đổi
url()CSS trong JavaScript bằngCSS.supports():
export function getCssUrlModifierSupport() {
const supportsCrossOriginAndReferrer =
typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports(
'background-image',
'url("https://example.com/a.svg" cross-origin(anonymous) referrer-policy(no-referrer))',
);
const supportsUrlIntegrity =
typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports(
'background-image',
'url("https://example.com/a.svg" cross-origin(anonymous) referrer-policy(no-referrer) integrity("sha384-Iw/0p59dP96nGTzHvf2XIfqcBwBW7RM6JfOij8uZxyg5azfNaOOUW8SQy6H/hU91"))',
);
return { supportsCrossOriginAndReferrer, supportsUrlIntegrity };
}