Também disponível em: English · Español · Français · العربية
X-Frame-Options: o que o cabeçalho realmente faz hoje
Cole seu cabeçalho e os quadros ao redor da página, e veja qual passo do padrão decide.
O que é X-Frame-Options?
X-Frame-Options é um cabeçalho de resposta que diz ao navegador se a sua página pode ser exibida dentro do quadro de outra pessoa. Ele existe para conter o clickjacking: um atacante incorpora a sua página real, deixa-a invisível e faz a vítima clicar num botão que ela não vê. Só dois valores significam alguma coisa: DENY recusa qualquer enquadramento e SAMEORIGIN o permite apenas a partir da sua própria origem.
O cabeçalho é mais antigo que o processo que costuma produzir padrões web. Os navegadores o lançaram por volta de 2009, e a descrição que todo mundo cita, a RFC 7034, foi escrita quatro anos depois para documentar o que já existia. Essa RFC é marcada como Informational e diz isso de si mesma logo nas primeiras linhas: não é uma especificação da via de padrões da internet.
Ou seja, o documento para o qual a maioria das páginas aponta já não é o que rege o comportamento. É o padrão HTML que agora carrega o modelo de processamento, e ele afirma sem rodeios que sua definição substitui a da RFC. A gramática que ele traz é mais curta, o algoritmo é mais preciso, e ele resolve várias questões que a RFC deixou explicitamente em aberto.
Como usar
- Informe seu cabeçalho. Um por linha, se a resposta enviar mais de um. Um valor separado por vírgulas e vários cabeçalhos separados são equivalentes pela norma, então as duas formas dão aqui a mesma resposta.
- Descreva os quadros ao redor da página. Dê a origem do documento que envia o cabeçalho e depois as origens que o enquadram, da mais interna para a mais externa, até a aba. Isso importa mais do que parece: a verificação percorre a cadeia inteira.
- Acrescente uma Content-Security-Policy, se você enviar uma. Se uma política aplicada trouxer frame-ancestors, o X-Frame-Options é ignorado por completo. Marque a caixa de Report-Only para ver a diferença, que é maior do que quase todo mundo espera.
Os casos que nenhuma tabela registra
O comportamento interessante aparece quando uma resposta carrega mais de um valor, o que é comum na prática porque um proxy ou um framework acrescenta um cabeçalho que a aplicação já tinha definido. O padrão publica uma tabela de respostas resolvidas exatamente para esses casos, e os resultados não são os que você imaginaria. SAMEORIGIN ao lado de um valor inválido bloqueia a página. Dois valores inválidos a liberam. A regra é deliberada: um cabeçalho com um valor significativo e outro confuso parece a má configuração de algo que tentava funcionar, então é bloqueado de propósito, enquanto um cabeçalho totalmente ilegível é tratado como se nunca tivesse sido enviado.
Basta uma vírgula final para disparar isso. Separar por vírgulas transforma SAMEORIGIN, em dois valores, o segundo vazio, e isso conta como um segundo valor distinto — então a página é bloqueada. Enviar SAMEORIGIN em um cabeçalho e um X-Frame-Options vazio em outro faz a mesma coisa, exatamente pelo mesmo motivo.
ALLOWALL é o sobrevivente mais estranho. Não aparece em lugar nenhum da RFC 7034 (nunca fez parte da gramática documentada do cabeçalho) e ainda assim o padrão o menciona, unicamente para que a regra de múltiplos valores possa capturá-lo. Sozinho ele não faz absolutamente nada, e uma página que envia apenas ALLOWALL é tão incorporável quanto uma que não envia cabeçalho algum.
ALLOW-FROM é o que as pessoas ainda copiam de tutoriais. Está na gramática da RFC, e o padrão HTML diz com todas as letras que ele não deve ser implementado. Uma página que conte com ele para liberar um parceiro específico está liberando todo mundo, em silêncio, porque um valor que a gramática não admite é tratado como se não houvesse cabeçalho.
A pergunta que a RFC deixou em aberto
SAMEORIGIN soa inequívoco e não é. Pense numa página enquadrada pelo widget de um parceiro, com esse widget por sua vez enquadrado pelo seu próprio site no topo. A origem comparada é a da página que enquadra diretamente, a do documento de nível superior na aba, ou a de todos os documentos no meio? A RFC 7034 admite as três leituras no próprio texto, ao observar que os critérios podem não ser avaliados de forma unânime e listar cada possibilidade.
O padrão HTML resolve: a verificação sobe pela cadeia inteira de ancestrais, e um único documento de outra origem em qualquer ponto bloqueia a página. Portanto o exemplo acima é recusado, mesmo que a página de nível superior seja sua. Essa é a mais estrita das três leituras, e é a que a ferramenta acima implementa — razão pela qual o exemplo padrão parece que deveria ser permitido e não é.
A outra regra que vale conhecer é que uma diretiva CSP frame-ancestors não apenas tem prioridade: ela remove o X-Frame-Options da equação por completo. O algoritmo procura a diretiva antes mesmo de olhar o valor do cabeçalho, então uma página que envia DENY junto de qualquer política com frame-ancestors é regida apenas pela política. Mas isso só vale para uma política aplicada. Um cabeçalho Content-Security-Policy-Report-Only é ignorado, porque o algoritmo considera apenas políticas cuja disposição seja de aplicação — de modo que mover frame-ancestors para Report-Only enquanto você testa devolve o controle, caladamente, a um cabeçalho em que você talvez já tivesse parado de pensar.
O que isto não diz
Ele diz o que um navegador em conformidade deve fazer, não o que um navegador específico faz. A distinção é real: a própria razão de a RFC admitir uma ambiguidade é que as implementações divergiam, e um navegador pode carregar comportamento antigo por anos. Nada aqui foi medido contra um navegador real, e o apêndice de compatibilidade de um documento de 2013 não é prova sobre hoje.
Ele também não enxerga a página sobre a qual você pergunta. As origens são fornecidas por você, então a resposta vale o que valer a cadeia que você descreveu — e a cadeia é justamente a parte fácil de errar, já que uma página pode ser enquadrada por algo que você não colocou ali. Se quiser a resposta real de uma página real, as ferramentas de desenvolvimento do navegador dirão quando um quadro foi recusado, e por quê.
Por fim, o X-Frame-Options é a metade mais antiga de uma dupla. Tudo o que você expressa com ele pode ser expresso com uma diretiva CSP frame-ancestors, que ainda cobre os casos que o cabeçalho não alcança: liberar uma origem específica, liberar várias, ou comparar por esquema. O cabeçalho continua valendo a pena para clientes antigos, e os dois convivem bem, mas uma implantação nova deveria ser conduzida pela política, não pelo cabeçalho.
Por que é grátis?
Rodar um algoritmo de uma especificação é aritmética sobre algumas cadeias de texto, e isso acontece no seu navegador. Não há servidor no meio, então não há o que cobrar nem conta a criar.
Nada é enviado. O cabeçalho e as origens que você digita não saem da aba.