هنگام بار کردن دستورگانها، برگههای سبک نگارش، قلمها، یا تصاویر از CDNهای طرف سوم یا مبدأهای خارجی، یکپارچگی رمزنگاری آنها را با «یکپارچگی زیرمنبع» (integrity در HTML و integrity() در CSS url()) تأیید کنید، مدیریت اطلاعات کاربری میانمبدئی (crossorigin و cross-origin()) را پیکربندی کنید، و سرصفحههای Referer خروجی (referrerpolicy و referrer-policy()) را محدود کنید. برای «خطمشی امنیتی محتوا» در سطح سند و فهرستهای مجاز اتصال خروجی، به امنیت وب و محدود کردن اتصالهای شبکه خروجی با فهرستهای مجاز اتصال مراجعه کنید.
۱. تولید خلاصه یکپارچگی رمزنگاری برای داراییهای تغییرناپذیر
«یکپارچگی زیرمنبع» (SRI) تضمین میکند که مرورگر فقط درصورتی دارایی خارجی را اجرا یا ارائه میکند که بایتهای آن با درهمساز رمزنگاری اعلامشده توسط برنامه شما مطابقت داشته باشد.
- خلاصهها را در زمان ساخت یا انتشار محاسبه کنید، هرگز از پاسخهای غیرقابلاعتماد زنده استفاده نکنید: درطول مرحله ساخت یا انتشار، درهمسازیهای یکپارچگی را از بایتهای دارایی محلی شناختهشده و خوب تولید کنید (یا خلاصه رسمی منتشرشده توسط نویسنده بسته را کپی کنید) و آنها را در HTML، CSS، یا JavaScript خود بهصورت کد ثابت قرار دهید. هرگز نشانی وب طرف سوم را در مرورگر در زمان اجرا واکشی نکنید تا درهمسازی
integrityخودش را محاسبه کند، که به هر بایتی که سرور راه دور برمیگرداند اعتماد میکند و SRI را شکست میدهد. - استفاده از
sha384-بهطور پیشفرض: فراداده یکپارچگی را بااستفاده ازsha384-(پیشفرض توصیهشده)،sha512-، یاsha256-بهصورت<algorithm>-<base64-digest>قالببندی کنید. - سنجاق کردن زیرمنابع خارجی به نشانیهای وب تغییرناپذیر و نسخهدار: همیشه بااستفاده از نشانیهای وب تغییرناپذیر، نسخهسنجاقشده، یا محتواآدرسدهیشده (مثل
https://cdn.example.com/ui@2.4.0/app.min.js) به داراییهای طرف سوم ارجاع دهید، نه نشانیهای وب شناور تغییرپذیر (latest/app.min.js) که سایت شما را درمعرض تغییرات زنجیره تأمین بیصدا قرار میدهند و بهمحض بهروزرسانی فایل راه دور، «یکپارچگی منبع فرعی» را میشکنند. اگر باید دستورگان طرف سوم تغییرپذیری را بار کنید که در محل بهروز میشود (مانند برچسب تجزیهوتحلیل فروشنده)،integrityرا در آن نشانی وب حذف کنید و بهجای آن، آن را با «خطمشی امنیتی محتوا» (strict-dynamic/ nonces) یا جعبه شنی iframe محدود کنید. - وقتی یک نقطه پایانی ممکن است چندین بار داده معتبر ارائه دهد، چندین خلاصه را با فاصله از هم جدا کنید: همه مرورگرهایی که از SRI پشتیبانی میکنند،
sha256-،sha384-، وsha512-را پیادهسازی میکنند. اگر نشانی وب ممکن است بهطور قانونی یکی از چندین محموله شناختهشده خوب را برگرداند (مثلاً درطول آزمایش A/B، عرضه CDN مرحلهای، یا ساخت مذاکرهشده محتوا)، چندین خلاصه از الگوریتم یکسان را که با فاصله از هم جدا شدهاند فهرست کنید (برای مثال،integrity="sha384-variantA... sha384-variantB..."). اگر پاسخ با هر خلاصه فهرستشده از قویترین الگوریتم موجود مطابقت داشته باشد، مرورگر آن را میپذیرد. - ارائه زیرمنابع میانمبدئی با سرصفحههای CORS: مرورگرها فقط زمانی میتوانند یکپارچگی پاسخ میانمبدئی را تأیید کنند که CORS-خوانا باشد (
Access-Control-Allow-Origin: *برای درخواستهای ناشناس، یا مبدأ دقیق درخواستکننده باAccess-Control-Allow-Credentials: trueبرای درخواستهای دارای اعتبارنامه).
# 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)}`;
}
۲. اسکریپتها، برگههای سبک نگارش، و پیشبارگیریهای HTML ایمن
یک سرایند پاسخ HTTP Referrer-Policy: strict-origin-when-cross-origin در سطح سند تنظیم کنید (یا <meta name="referrer" content="strict-origin-when-cross-origin"> را در <head> بگنجانید) تا بهعنوان خط مبنای پیشفرض عمل کند، بهطوریکه هر منبع فرعی یا CSS url() که اصلاحکننده ارجاعدهنده برای هر درخواست را حذف میکند، همچنان پارامترهای پُرسمان و مسیر حساس را در درخواستهای میانمبدئی حذف کند.
سپس، در عناصر خارجی <script>، <link rel="stylesheet">، <link rel="preload">، و <link rel="modulepreload">، همیشه integrity، crossorigin، و referrerpolicy را باهم تنظیم کنید:
integrity="sha384-...": اگر بایتهای پاسخ با خلاصه موردانتظار مطابقت نداشته باشد، اجرای منبع یا برنامه را مسدود میکند (برای<link rel="stylesheet">مسدودشده،linkElement.sheetبهصورتnullباقی میماند و رویدادerrorفعال میشود).crossorigin="anonymous"(پیشفرض توصیهشده برای داراییهای CDN عمومی) یاcrossorigin="use-credentials": هرگاهintegrityدر منبع میانمبدئی تنظیم شده باشد، الزامی است. حذفcrossoriginباعث میشود مرورگر منبع را در حالتno-corsواکشی کند (پاسخی مبهم تولید میکند که نمیتواند با درهمسازی درستیسنجی شود) و منبع را مسدود کند.referrerpolicy="no-referrer"(پیشفرض توصیهشده برای CDNهای طرف سوم) یاreferrerpolicy="strict-origin-when-cross-origin": خطمشی ارجاعدهنده سند را لغو میکند تا از نشت اطلاعات مبدأ یا مسیر در سرایند درخواستRefererبه میزبان طرف سوم جلوگیری شود.
<!-- 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>
هنگام واکشی برنامهای زیرمنابع خارجی با fetch()، گزینههای منطبق integrity، mode، credentials، و referrerPolicy را ارسال کنید (عدم تطابق خلاصه، وعده را با 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',
});
۳. تصاویر CSS، قلمها، و برگههای سبک نگارش واردشده را با اصلاحکنندههای url() ایمن کنید
اصلاحکنندههای درخواست CSS url() (cross-origin()، referrer-policy()، و integrity()) حالت واکشی، سرصفحه ارجاعدهنده، و درستیسنجی «یکپارچگی زیرمنبع» منابع بارشده با CSS را (ازجمله قوانین background-image، content، mask-image، border-image-source، filter، @font-face src، و @import) مستقیماً از CSS کنترل میکنند:
| تابع اصلاحگر | نحو آرگومان و مقادیر مجاز | هدف |
|---|---|---|
cross-origin(<ident>) |
کلیدواژه بدون نقلقول: anonymous (پیشفرض توصیهشده) یا use-credentials |
منبع CSS را بااستفاده از CORS واکشی میکند و ارسال شدن یا نشدن اعتبارنامهها را کنترل میکند. همیشه در کنار integrity() در منابع CSS میانمبدئی گنجانده شود. |
referrer-policy(<ident>) |
کلیدواژه بدون نقلقول: no-referrer (پیشفرض توصیهشده برای داراییهای شخص ثالث)، strict-origin-when-cross-origin، origin، same-origin، strict-origin، origin-when-cross-origin، no-referrer-when-downgrade، یا unsafe-url |
خطمشی ارجاعدهنده سند را برای این درخواست CSS خاص لغو میکند. |
integrity("<string>") |
رشته نقلشده: "sha384-<base64>" (یا خلاصههای جداشده با فاصله "sha384-... sha384-...") |
منبع CSS واکشیشده را قبلاز ارائه یا اعمال آن، با خلاصه رمزنگاری موردانتظار درستیسنجی میکند. |
هنگام نوشتن اصلاحکنندههای url()، این قوانین امنیتی و نحو CSS را دنبال کنید:
- رشته نشانی وب را در
url("...")نقلقول کنید: اصلاحکنندههای درخواست فقط زمانی معتبر هستند که نشانی وب داخلurl(...)نقلقولشده<string>باشد (نشانیهای وب نقلقولنشده نمیتوانند از اصلاحکنندهها استفاده کنند). - هر نوع اصلاحکننده را حداکثر یکبار در هر
url()مشخص کنید: میتوانیدcross-origin()،referrer-policy()، وintegrity()را به هر ترتیبی درurl("..." ...)ترکیب کنید، اما تکرار دو بار یک نوع اصلاحکننده (مانند دو اصلاحکنندهintegrity()) نامعتبر است. برای ارائه چند درهمسازی، آنها را با فاصله در یک رشتهintegrity("sha384-... sha384-...")از هم جدا کنید. - همیشه
integrity()میانمبدئی را باcross-origin(anonymous)جفت کنید: درحالیکه@font-facesrcوmask-imageازقبل بهطور پیشفرض در حالت ناشناسcorsواکشی میکنند،background-image،content،border-image-source، و@importبهطور پیشفرض در حالتno-corsهستند (که پاسخی مبهم تولید میکند که نمیتواند درستیسنجی درهمسازی شود) و SVG خارجیfilterبهطور پیشفرض در حالتsame-originاست (که نشانیهای وب میانمبدئی را کاملاً مسدود میکند). گنجاندنcross-origin(anonymous)(یاcross-origin(use-credentials)) درستیسنجی CORS را در همه داراییهای CSS تضمین میکند. - اصلاحکنندهها را در
url("...")در قوانین@importو@font-faceقرار دهید:cross-origin()،referrer-policy()، وintegrity()را درurl("..." ...)نگه دارید، درحالیکه توصیفکنندههای قلم مانندformat("woff2")وtech(...)را خارج ازurl(...)نگه میدارید.
@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")
);
}
راهبردهای جایگزین
اگر هدف «خط پایه» شما هنوز از اصلاحکنندههای درخواست CSS url() (cross-origin()، referrer-policy()، یا integrity()) پشتیبانی نمیکند، خطمشیهای سطح سند را با اعلامیههای CSS آبشاری ترکیب کنید تا زیرمنابع همچنان کاربردی و محافظتشده باقی بمانند:
- وقتی SRI بینمرورگری لازم است، HTML
<link rel="stylesheet">را به CSS@importترجیح دهید: چونsubresource-integrityوreferrer-policyدر<link rel="stylesheet">بهطور گسترده در همه مرورگرها دردسترس هستند، هرگاه درستیسنجی یکپارچگی باید در همه مرورگرها اجرا شود، برگه سبک نگارش خارجی را با<link rel="stylesheet" href="..." integrity="sha384-..." crossorigin="anonymous" referrerpolicy="no-referrer">بهجای@importبار کنید. - به سرصفحه
Referrer-Policyدر سطح سند یا<meta name="referrer">از مرحله ۲ تکیه کنید: در مرورگرهایی که ازreferrer-policy()در CSSurl()پشتیبانی نمیکنند، سرصفحهReferrer-Policy: strict-origin-when-cross-originدر سطح سند یا عنصر<meta name="referrer" content="strict-origin-when-cross-origin">بهطور خودکار مسیر حساس و پارامترهای پرسمان را در درخواستهای CSS میانمبدئی حذف میکند. اعلامیههای CSS جداگانه را برای ارتقای پیشرو آبشاری کنید: مرورگرهایی که اصلاحکننده را در
url("..." ...)تشخیص نمیدهند، کل اعلامیه را در زمان تجزیه CSS کنار میگذارند. ابتدا یکurl("...")ساده را اعلام کنید، سپس یک اعلامیهcross-origin()وreferrer-policy()(برای مرورگرهایی که از این دو اصلاحکننده پشتیبانی میکنند اما ازintegrity()پشتیبانی نمیکنند)، و سپس اعلامیه کامل شاملintegrity("sha384-..."):- هرگز
url()درستیسنجینشده را در همان فهرست@font-facesrcجداشده با کاما قرار ندهید: نوشتنsrc: url("font.woff2" cross-origin(anonymous) integrity("sha384-...")), url("font.woff2");در یک توصیفگرsrcواحد، «یکپارچگی زیرمنبع» را در زمان اجرا دور میزند—اگر CDN قلم دستکاریشدهای را ارائه دهد که درستیسنجیintegrity()را رد کند، مرورگر آن را بهعنوان خطای بارگیری درنظر میگیرد و بهطور خودکار بهurl("font.woff2")جداشده با کاما دوم برمیگردد. استفاده از اعلامیههای جداگانهsrc:تضمین میکند که مرورگرهای پشتیبانیکننده، اعلامیه درستیسنجینشده را در زمان تجزیه CSS بازنویسی کنند و قلمهای دستکاریشده را در زمان اجرا مسدود کنند.
@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") ); }- هرگز
تشخیص ویژگی پشتیبانی از اصلاحکننده CSS
url()در جاوا اسکریپت باCSS.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 };
}