Ao carregar scripts, folhas de estilo, fontes ou imagens de CDNs de terceiros ou origens externas, verifique a integridade criptográfica deles com a integridade de subrecursos (integrity em HTML e integrity() em CSS url()), configure o processamento de credenciais entre origens (crossorigin e cross-origin()) e restrinja os cabeçalhos Referer de saída (referrerpolicy e referrer-policy()). Para conferir a política de segurança de conteúdo no nível do documento e as listas de permissões de conexão de saída, consulte Segurança da Web e Restringir conexões de rede de saída com listas de permissões de conexão.
1. Gerar resumos de integridade criptográfica para recursos imutáveis
A integridade de sub-recursos (SRI, na sigla em inglês) garante que o navegador só execute ou renderize um recurso externo se os bytes dele corresponderem ao hash criptográfico declarado pelo aplicativo.
- Calcule resumos no momento do build ou da versão, nunca de respostas dinâmicas não confiáveis: gere hashes de integridade durante a etapa de build ou versão usando bytes de recursos locais conhecidos (ou copie o resumo oficial publicado pelo autor do pacote) e codifique-os no HTML, CSS ou JavaScript. Nunca busque um URL de terceiros no navegador no momento da execução para calcular o próprio hash
integrity, que confia em qualquer byte que o servidor remoto retornar e anula a SRI. - Usar
sha384-por padrão: formate os metadados de integridade como<algorithm>-<base64-digest>usandosha384-(padrão recomendado),sha512-ousha256-. - Fixe subrecursos externos a URLs imutáveis e com controle de versões: sempre faça referência a recursos de terceiros usando URLs imutáveis, com controle de versões ou endereçados por conteúdo (como
https://cdn.example.com/ui@2.4.0/app.min.js), em vez de URLs flutuantes mutáveis (latest/app.min.js), que expõem seu site a mudanças silenciosas na cadeia de suprimentos e interrompem a SRI assim que o arquivo remoto é atualizado. Se você precisar carregar um script mutável de terceiros que é atualizado no local (como uma tag de análise de fornecedor), omitaintegritynesse URL e restrinja-o com a Política de Segurança de Conteúdo (strict-dynamic/ nonces) ou o sandbox de iframe. - Separe vários resumos com espaços quando um endpoint puder exibir vários payloads válidos: todos os navegadores compatíveis com SRI implementam
sha256-,sha384-esha512-. Se um URL puder retornar legitimamente um dos vários payloads conhecidos (como durante um teste A/B, um lançamento de CDN em etapas ou um build negociado por conteúdo), liste vários resumos do mesmo algoritmo separados por espaços em branco (por exemplo,integrity="sha384-variantA... sha384-variantB..."). O navegador vai aceitar a resposta se ela corresponder a qualquer resumo listado do algoritmo mais forte presente. - Disponibilizar sub-recursos entre origens com cabeçalhos CORS: os navegadores só podem verificar a integridade de uma resposta entre origens quando ela é legível por CORS (
Access-Control-Allow-Origin: *para solicitações anônimas ou a origem exata da solicitação comAccess-Control-Allow-Credentials: truepara solicitações com credenciais).
# 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. Scripts, folhas de estilo e pré-carregamentos HTML seguros
Defina um cabeçalho de resposta HTTP Referrer-Policy: strict-origin-when-cross-origin no nível do documento (ou inclua <meta name="referrer" content="strict-origin-when-cross-origin"> em <head>) como um padrão de referência para que qualquer subrecurso ou url() de CSS que omita um modificador de referenciador por solicitação ainda remova o caminho sensível e os parâmetros de consulta em solicitações entre origens.
Em seguida, nos elementos externos <script>, <link rel="stylesheet">, <link rel="preload"> e <link rel="modulepreload">, sempre defina integrity, crossorigin e referrerpolicy juntos:
integrity="sha384-...": bloqueia a execução ou o aplicativo do recurso se os bytes de resposta não corresponderem ao resumo esperado (para um<link rel="stylesheet">bloqueado,linkElement.sheetpermanecenulle um eventoerroré acionado).crossorigin="anonymous"(padrão recomendado para recursos de CDN públicos) oucrossorigin="use-credentials": obrigatório sempre queintegrityé definido em um recurso entre origens. Omitircrossoriginfaz com que o navegador busque o recurso no modono-cors(produzindo uma resposta opaca que não pode ser verificada por hash) e bloqueie o recurso.referrerpolicy="no-referrer"(padrão recomendado para CDNs de terceiros) oureferrerpolicy="strict-origin-when-cross-origin": substitui a política de referenciador do documento para evitar o vazamento de informações de origem ou caminho no cabeçalho da solicitaçãoRefererpara o host de terceiros.
<!-- 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>
Ao buscar subrecursos externos de forma programática com fetch(), transmita as opções integrity, mode, credentials e referrerPolicy correspondentes (uma incompatibilidade de resumo rejeita a promessa com 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. Proteger imagens, fontes e folhas de estilo importadas de CSS com modificadores url()
Os modificadores de solicitação CSS url() (cross-origin(), referrer-policy() e integrity()) controlam o modo de busca, o cabeçalho do referenciador e a verificação de integridade de subrecursos de recursos carregados por CSS, incluindo as regras background-image, content, mask-image, border-image-source, filter, @font-face src e @import, diretamente do CSS:
| Função modificadora | Sintaxe de argumentos e valores permitidos | Finalidade |
|---|---|---|
cross-origin(<ident>) |
Palavra-chave sem aspas: anonymous (padrão recomendado) ou use-credentials |
Busca o recurso CSS usando CORS e controla se as credenciais são enviadas. Sempre inclua ao lado de integrity() em recursos CSS entre origens. |
referrer-policy(<ident>) |
Palavra-chave sem aspas: no-referrer (padrão recomendado para recursos de terceiros), strict-origin-when-cross-origin, origin, same-origin, strict-origin, origin-when-cross-origin, no-referrer-when-downgrade ou unsafe-url |
Substitui a política de referenciador de documentos para essa solicitação de CSS específica. |
integrity("<string>") |
String entre aspas: "sha384-<base64>" (ou resumos separados por espaço "sha384-... sha384-...") |
Verifica o recurso CSS buscado em relação ao resumo criptográfico esperado antes de renderizá-lo ou aplicá-lo. |
Siga estas regras de sintaxe e segurança de CSS ao criar modificadores url():
- Coloque a string do URL entre aspas em
url("..."): os modificadores de solicitação só são válidos quando o URL emurl(...)é uma<string>entre aspas (URLs sem aspas não podem usar modificadores). - Especifique cada tipo de modificador no máximo uma vez por
url(): é possível combinarcross-origin(),referrer-policy()eintegrity()em qualquer ordem dentro deurl("..." ...), mas repetir o mesmo tipo de modificador duas vezes (como dois modificadoresintegrity()) é inválido. Para fornecer vários hashes, separe-os com espaços dentro de uma única stringintegrity("sha384-... sha384-..."). - Sempre use
integrity()entre origens comcross-origin(anonymous): embora@font-facesrcemask-imagejá busquem no modo anônimocorspor padrão,background-image,content,border-image-sourcee@importusam o modono-corspor padrão (produzindo uma resposta opaca que não pode ser verificada por hash), e o SVG externofilterusa o modosame-originpor padrão (bloqueando URLs entre origens imediatamente). Incluircross-origin(anonymous)(oucross-origin(use-credentials)) garante uma verificação de CORS consistente em todas as propriedades CSS. - Coloque modificadores dentro de
url("...")nas regras@importe@font-face: mantenhacross-origin(),referrer-policy()eintegrity()dentro deurl("..." ...), mas mantenha descritores de fonte comoformat("woff2")etech(...)fora deurl(...).
@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")
);
}
Estratégias de fallback
Se o destino do Baseline ainda não for compatível com os modificadores de solicitação url() do CSS (cross-origin(), referrer-policy() ou integrity()), combine as políticas no nível do documento com declarações CSS em cascata para que os subrecursos permaneçam funcionais e protegidos:
- Dê preferência ao HTML
<link rel="stylesheet">em vez do CSS@importquando a SRI entre navegadores for necessária: comosubresource-integrityereferrer-policyem<link rel="stylesheet">estão amplamente disponíveis em todos os navegadores, carregue folhas de estilo externas com<link rel="stylesheet" href="..." integrity="sha384-..." crossorigin="anonymous" referrerpolicy="no-referrer">em vez de@importsempre que a verificação de integridade precisar ser aplicada em todos os navegadores. - Confie no cabeçalho
Referrer-Policyno nível do documento ou em<meta name="referrer">da Etapa 2: em navegadores que não oferecem suporte areferrer-policy()dentro de CSSurl(), o cabeçalhoReferrer-Policy: strict-origin-when-cross-originno nível do documento ou o elemento<meta name="referrer" content="strict-origin-when-cross-origin">remove automaticamente o caminho sensível e os parâmetros de consulta em solicitações de CSS entre origens. Cascatear declarações CSS separadas para aprimoramento progressivo: os navegadores que não reconhecem um modificador dentro de
url("..." ...)descartam toda a declaração no momento da análise do CSS. Declare umurl("...")simples primeiro, seguido por uma declaraçãocross-origin()ereferrer-policy()(para navegadores que oferecem suporte a esses dois modificadores, mas não aintegrity()), seguido pela declaração completa, incluindointegrity("sha384-..."):- Nunca coloque um
url()não verificado na mesma lista@font-facesrcseparada por vírgulas: escreversrc: url("font.woff2" cross-origin(anonymous) integrity("sha384-...")), url("font.woff2");em um único descritorsrcignora a integridade de sub-recursos no momento da execução. Se uma CDN exibir uma fonte adulterada que não passe na verificaçãointegrity(), o navegador vai tratar isso como uma falha de carregamento e vai usar automaticamente o segundourl("font.woff2")separado por vírgulas. O uso de declaraçõessrc:separadas garante que os navegadores compatíveis substituam a declaração não verificada no momento da análise do CSS e bloqueiem fontes adulteradas no momento da execução.
@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") ); }- Nunca coloque um
Detecte o suporte ao modificador
url()do CSS no JavaScript comCSS.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 };
}