Uma exploração aprofundada do Cross-Origin Resource Sharing (CORS) e das solicitações preflight. Aprenda a lidar com problemas de CORS e a proteger suas aplicações web para um público global.
Desmistificando o CORS: Um Mergulho Profundo no Tratamento de Solicitações Preflight em JavaScript
No mundo em constante expansão do desenvolvimento web, a segurança é primordial. O Cross-Origin Resource Sharing (CORS) é um mecanismo de segurança crucial implementado pelos navegadores web para restringir que páginas web façam solicitações a um domínio diferente daquele que serviu a página web. Este é um recurso de segurança fundamental projetado para impedir que sites maliciosos acessem dados sensíveis. Este guia abrangente aprofundará as complexidades do CORS, focando especificamente no tratamento de solicitações preflight. Exploraremos o 'porquê', o 'o quê' e o 'como' do CORS, fornecendo exemplos práticos e soluções para problemas comuns encontrados por desenvolvedores em todo o mundo.
Entendendo a Política de Mesma Origem
No cerne do CORS está a Política de Mesma Origem (SOP). Essa política é um mecanismo de segurança no nível do navegador que restringe scripts em execução em uma origem de acessar recursos de uma origem diferente. Uma origem é definida pelo protocolo (ex., HTTP ou HTTPS), domínio (ex., example.com) e porta (ex., 80 ou 443). Duas URLs têm a mesma origem se esses três componentes corresponderem exatamente.
Por exemplo:
https://www.example.com/app1/index.htmlehttps://www.example.com/app2/index.htmltêm a mesma origem (mesmo protocolo, domínio e porta).https://www.example.com/index.htmlehttp://www.example.com/index.htmltêm origens diferentes (protocolos diferentes).https://www.example.com/index.htmlehttps://api.example.com/index.htmltêm origens diferentes (subdomínios diferentes são considerados domínios diferentes).https://www.example.com:8080/index.htmlehttps://www.example.com/index.htmltêm origens diferentes (portas diferentes).
A SOP é projetada para impedir que scripts maliciosos em um site acessem dados sensíveis, como cookies ou informações de autenticação do usuário, em outro site. Embora essencial para a segurança, a SOP também pode ser restritiva, especialmente quando são necessárias solicitações legítimas de origem cruzada.
O que é o Compartilhamento de Recursos de Origem Cruzada (CORS)?
O CORS é um mecanismo que permite aos servidores especificar quais origens (domínios, esquemas ou portas) têm permissão para acessar seus recursos. Ele essencialmente flexibiliza a SOP, permitindo acesso controlado de origem cruzada. O CORS é implementado usando cabeçalhos HTTP que são trocados entre o cliente (normalmente um navegador web) e o servidor.
Quando um navegador faz uma solicitação de origem cruzada (ou seja, uma solicitação para uma origem diferente da página atual), ele primeiro verifica se o servidor permite a solicitação. Isso é feito examinando o cabeçalho Access-Control-Allow-Origin na resposta do servidor. Se a origem da solicitação estiver listada neste cabeçalho (ou se o cabeçalho estiver definido como *, permitindo todas as origens), o navegador permite que a solicitação prossiga. Caso contrário, o navegador bloqueia a solicitação, impedindo que o código JavaScript acesse os dados da resposta.
O Papel das Solicitações Preflight
Para certos tipos de solicitações de origem cruzada, o navegador inicia uma solicitação preflight. Esta é uma solicitação OPTIONS enviada ao servidor antes da solicitação real. O objetivo da solicitação preflight é determinar se o servidor está disposto a aceitar a solicitação real. O servidor responde à solicitação preflight com informações sobre os métodos, cabeçalhos e outras restrições permitidas.
As solicitações preflight são acionadas quando a solicitação de origem cruzada atende a qualquer uma das seguintes condições:
- O método da solicitação não é
GET,HEADouPOST. - A solicitação inclui cabeçalhos personalizados (ou seja, cabeçalhos diferentes daqueles adicionados automaticamente pelo navegador).
- O cabeçalho
Content-Typeestá definido para algo diferente deapplication/x-www-form-urlencoded,multipart/form-dataoutext/plain. - A solicitação usa objetos
ReadableStreamno corpo.
Por exemplo, uma solicitação PUT com um Content-Type de application/json acionará uma solicitação preflight porque usa um método diferente dos permitidos e um tipo de conteúdo potencialmente não permitido.
Por que Solicitações Preflight?
As solicitações preflight são essenciais para a segurança porque fornecem ao servidor a oportunidade de rejeitar solicitações de origem cruzada potencialmente prejudiciais antes que sejam executadas. Sem as solicitações preflight, um site malicioso poderia potencialmente enviar solicitações arbitrárias a um servidor sem o consentimento explícito do servidor. Uma solicitação preflight permite que o servidor valide se a solicitação é aceitável e impede operações potencialmente prejudiciais.
Tratando Solicitações Preflight no Lado do Servidor
Tratar adequadamente as solicitações preflight é crucial para garantir que sua aplicação web funcione corretamente e de forma segura. O servidor deve responder à solicitação OPTIONS com os cabeçalhos CORS apropriados para indicar se a solicitação real é permitida.
Aqui está uma análise dos principais cabeçalhos CORS que são usados em respostas preflight:
Access-Control-Allow-Origin: Este cabeçalho especifica a(s) origem(ns) que têm permissão para acessar o recurso. Pode ser definido para uma origem específica (ex.,https://www.example.com) ou para*para permitir todas as origens. No entanto, o uso de*é geralmente desaconselhado por razões de segurança, especialmente se o servidor lida com dados sensíveis.Access-Control-Allow-Methods: Este cabeçalho especifica os métodos HTTP permitidos para a solicitação de origem cruzada (ex.,GET,POST,PUT,DELETE).Access-Control-Allow-Headers: Este cabeçalho especifica a lista de cabeçalhos HTTP não padrão que são permitidos na solicitação real. Isso é necessário se o cliente estiver enviando cabeçalhos personalizados, comoX-Custom-HeaderouAuthorization.Access-Control-Allow-Credentials: Este cabeçalho indica se a solicitação real pode incluir credenciais, como cookies ou cabeçalhos de autorização. Deve ser definido comotruese o código do lado do cliente estiver enviando credenciais e o servidor deve aceitá-las. Nota: quando este cabeçalho é definido como `true`, `Access-Control-Allow-Origin` *não pode* ser definido como `*`. Você deve especificar a origem.Access-Control-Max-Age: Este cabeçalho especifica a quantidade máxima de tempo (em segundos) que o navegador pode armazenar em cache a resposta preflight. Isso pode ajudar a melhorar o desempenho, reduzindo o número de solicitações preflight que são enviadas.
Exemplo: Tratando Solicitações Preflight em Node.js com Express
Aqui está um exemplo de como tratar solicitações preflight em uma aplicação Node.js usando o framework Express:
const express = require('express');
const cors = require('cors');
const app = express();
// Habilita o CORS para todas as origens (apenas para fins de desenvolvimento!)
// Em produção, especifique as origens permitidas para maior segurança.
app.use(cors()); //ou app.use(cors({origin: 'https://www.example.com'}));
// Rota para tratar solicitações OPTIONS (preflight)
app.options('/data', cors()); // Habilita o CORS para uma única rota. Ou especifique a origem: cors({origin: 'https://www.example.com'})
// Rota para tratar solicitações GET
app.get('/data', (req, res) => {
res.json({ message: 'Estes são dados de origem cruzada!' });
});
// Rota para tratar uma solicitação preflight e uma de post
app.options('/resource', cors()); // habilita a solicitação pre-flight para a solicitação DELETE
app.delete('/resource', cors(), (req, res, next) => {
res.send('recurso deletado')
})
const port = 3000;
app.listen(port, () => {
console.log(`Servidor escutando na porta ${port}`);
});
Neste exemplo, usamos o middleware cors para tratar as solicitações CORS. Para um controle mais granular, o CORS pode ser habilitado por rota. Nota: em produção, é altamente recomendável especificar as origens permitidas usando a opção origin em vez de permitir todas as origens. Permitir todas as origens usando * pode expor sua aplicação a vulnerabilidades de segurança.
Exemplo: Tratando Solicitações Preflight em Python com Flask
Aqui está um exemplo de como tratar solicitações preflight em uma aplicação Python usando o framework Flask e a extensão flask_cors:
from flask import Flask, jsonify
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app) # Habilita o CORS para todas as rotas
@app.route('/data')
@cross_origin()
def get_data():
data = {"message": "Estes são dados de origem cruzada!"}
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
Este é o uso mais simples. Como antes, as origens podem ser restringidas. Consulte a documentação do flask-cors para mais detalhes.
Exemplo: Tratando Solicitações Preflight em Java com Spring Boot
Aqui está um exemplo de como tratar solicitações preflight em uma aplicação Java usando Spring Boot:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class CorsApplication {
public static void main(String[] args) {
SpringApplication.run(CorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/data").allowedOrigins("http://localhost:8080");
}
};
}
}
E o controller correspondente:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataController {
@GetMapping("/data")
public String getData() {
return "Estes são dados de origem cruzada!";
}
}
Problemas Comuns de CORS e Soluções
Apesar de sua importância, o CORS pode ser frequentemente uma fonte de frustração para desenvolvedores. Aqui estão alguns problemas comuns de CORS e suas soluções:
-
Erro: "No 'Access-Control-Allow-Origin' header is present on the requested resource."
Este erro indica que o servidor não está retornando o cabeçalho
Access-Control-Allow-Originem sua resposta. Para corrigir isso, certifique-se de que o servidor esteja configurado para incluir o cabeçalho e que ele esteja definido para a origem correta ou para*(se apropriado).Solução: Configure o servidor para incluir o cabeçalho `Access-Control-Allow-Origin` em sua resposta, definindo-o para a origem do site solicitante ou para `*` para permitir todas as origens (use com cautela).
-
Erro: "Response to preflight request doesn't pass access control check: Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers in preflight response."
Este erro indica que o servidor não está permitindo o cabeçalho personalizado (
X-Custom-Headerneste exemplo) na solicitação de origem cruzada. Para corrigir isso, certifique-se de que o servidor inclua o cabeçalho no cabeçalhoAccess-Control-Allow-Headersna resposta preflight.Solução: Adicione o cabeçalho personalizado (ex., `X-Custom-Header`) ao cabeçalho `Access-Control-Allow-Headers` na resposta preflight do servidor.
-
Erro: "Credentials flag is 'true', but the 'Access-Control-Allow-Origin' header is '*'."
Quando o cabeçalho
Access-Control-Allow-Credentialsé definido comotrue, o cabeçalhoAccess-Control-Allow-Origindeve ser definido para uma origem específica, não*. Isso ocorre porque permitir credenciais de todas as origens seria um risco de segurança.Solução: Ao usar credenciais, defina `Access-Control-Allow-Origin` para uma origem específica em vez de `*`.
-
A solicitação preflight não está sendo enviada.
Verifique novamente se o seu código JavaScript inclui a propriedade `credentials: 'include'`. Verifique também se o seu servidor permite `Access-Control-Allow-Credentials: true`.
-
Configurações conflitantes entre servidor e cliente.
Verifique cuidadosamente sua configuração de CORS no lado do servidor junto com as configurações do lado do cliente. Desacordos (ex., servidor permitindo apenas solicitações GET, mas o cliente enviando POST) causarão erros de CORS.
CORS e Melhores Práticas de Segurança
Embora o CORS permita acesso controlado de origem cruzada, é essencial seguir as melhores práticas de segurança para evitar vulnerabilidades:
- Evite usar
*no cabeçalhoAccess-Control-Allow-Originem produção. Isso permite que todas as origens acessem seus recursos, o que pode ser um risco de segurança. Em vez disso, especifique as origens exatas que são permitidas. - Considere cuidadosamente quais métodos e cabeçalhos permitir. Permita apenas os métodos e cabeçalhos que são estritamente necessários para o funcionamento correto de sua aplicação.
- Implemente mecanismos adequados de autenticação e autorização. O CORS não substitui a autenticação e a autorização. Garanta que sua API esteja protegida por medidas de segurança apropriadas.
- Valide e sanitize toda a entrada do usuário. Isso ajuda a prevenir ataques de cross-site scripting (XSS) e outras vulnerabilidades.
- Mantenha sua configuração de CORS do lado do servidor atualizada. Revise e atualize regularmente sua configuração de CORS para garantir que ela esteja alinhada com os requisitos de segurança de sua aplicação.
CORS em Diferentes Ambientes de Desenvolvimento
Problemas de CORS podem se manifestar de maneiras diferentes em vários ambientes de desenvolvimento e tecnologias. Aqui está uma olhada em como abordar o CORS em alguns cenários comuns:
Ambientes de Desenvolvimento Local
Durante o desenvolvimento local, os problemas de CORS podem ser particularmente irritantes. Os navegadores frequentemente bloqueiam solicitações do seu servidor de desenvolvimento local (ex., localhost:3000) para uma API remota. Várias técnicas podem aliviar essa dor:
- Extensões de Navegador: Extensões como "Allow CORS: Access-Control-Allow-Origin" podem desativar temporariamente as restrições de CORS para fins de teste. No entanto, *nunca* use-as em produção.
- Servidores Proxy: Configure um servidor proxy que encaminha as solicitações do seu servidor de desenvolvimento local para a API remota. Isso efetivamente torna as solicitações de "mesma origem" da perspectiva do navegador. Ferramentas como
http-proxy-middleware(para Node.js) são úteis para isso. - Configurar o CORS do Servidor: Mesmo durante o desenvolvimento, é uma boa prática configurar seu servidor de API para permitir explicitamente solicitações da sua origem de desenvolvimento local (ex.,
http://localhost:3000). Isso simula uma configuração de CORS do mundo real e ajuda a detectar problemas precocemente.
Ambientes Serverless (ex., AWS Lambda, Google Cloud Functions)
Funções serverless geralmente requerem uma configuração cuidadosa de CORS. Muitas plataformas serverless fornecem suporte CORS integrado, mas é crucial configurá-lo corretamente:
- Configurações Específicas da Plataforma: Use as opções de configuração CORS integradas da plataforma. O AWS Lambda, por exemplo, permite especificar origens, métodos e cabeçalhos permitidos diretamente nas configurações do API Gateway.
- Middleware/Bibliotecas: Para maior flexibilidade, você pode usar middleware ou bibliotecas para lidar com o CORS dentro do código da sua função serverless. Isso é semelhante às abordagens usadas em ambientes de servidor tradicionais (ex., usando o pacote `cors` em funções Lambda Node.js).
- Considere o Método
OPTIONS: Garanta que sua função serverless trate as solicitaçõesOPTIONScorretamente. Isso geralmente envolve a criação de uma rota separada que retorna os cabeçalhos CORS apropriados.
Desenvolvimento de Aplicativos Móveis (ex., React Native, Flutter)
O CORS é uma preocupação menos direta para aplicativos móveis nativos (Android, iOS), pois eles normalmente não aplicam a política de mesma origem da mesma forma que os navegadores web. No entanto, o CORS ainda pode ser relevante se seu aplicativo móvel usar uma web view para exibir conteúdo da web ou se você estiver usando frameworks como React Native ou Flutter que utilizam JavaScript:
- Web Views: Se seu aplicativo móvel usa uma web view para exibir conteúdo da web, as mesmas regras de CORS se aplicam como em um navegador web. Configure seu servidor para permitir solicitações da origem do conteúdo da web.
- React Native/Flutter: Esses frameworks usam JavaScript para fazer solicitações de API. Embora o ambiente nativo possa não aplicar o CORS diretamente, os clientes HTTP subjacentes (ex.,
fetch) ainda podem exibir um comportamento semelhante ao CORS em certas situações. - Clientes HTTP Nativos: Ao fazer solicitações de API diretamente do código nativo (ex., usando OkHttp no Android ou URLSession no iOS), o CORS geralmente não é um fator. No entanto, você ainda precisa considerar as melhores práticas de segurança, como autenticação e autorização adequadas.
Considerações Globais para a Configuração do CORS
Ao configurar o CORS para uma aplicação acessível globalmente, é crucial considerar fatores como:
- Soberania de Dados: Regulamentações em algumas regiões exigem que os dados residam dentro da região. O CORS pode estar envolvido ao acessar recursos através de fronteiras, potencialmente infringindo as leis de residência de dados.
- Políticas de Segurança Regionais: Diferentes países podem ter regulamentações e diretrizes de cibersegurança distintas que influenciam como o CORS deve ser implementado e protegido.
- Redes de Distribuição de Conteúdo (CDNs): Certifique-se de que sua CDN esteja configurada corretamente para repassar os cabeçalhos CORS necessários. CDNs configuradas incorretamente podem remover os cabeçalhos CORS, levando a erros inesperados.
- Balanceadores de Carga e Proxies: Verifique se quaisquer balanceadores de carga ou proxies reversos em sua infraestrutura estão tratando corretamente as solicitações preflight e repassando os cabeçalhos CORS.
- Suporte Multilíngue: Considere como o CORS interage com as estratégias de internacionalização (i18n) e localização (l10n) de sua aplicação. Garanta que as políticas de CORS sejam consistentes em diferentes versões de idioma de sua aplicação.
Testando e Depurando o CORS
Testar e depurar o CORS de forma eficaz é vital. Aqui estão algumas técnicas:
- Ferramentas de Desenvolvedor do Navegador: O console de desenvolvedor do navegador é sua primeira parada. A aba "Rede" mostrará as solicitações preflight e as respostas, revelando se os cabeçalhos CORS estão presentes e configurados corretamente.
- Ferramenta de Linha de Comando `curl`: Use `curl -v -X OPTIONS
` para enviar manualmente solicitações preflight e inspecionar os cabeçalhos de resposta do servidor. - Verificadores de CORS Online: Numerosas ferramentas online podem ajudar a validar sua configuração de CORS. Basta procurar por "verificador de CORS".
- Testes Unitários e de Integração: Escreva testes automatizados para verificar se sua configuração de CORS está funcionando como esperado. Esses testes devem cobrir tanto solicitações de origem cruzada bem-sucedidas quanto cenários onde o CORS deve bloquear o acesso.
- Logging e Monitoramento: Implemente logging para rastrear eventos relacionados ao CORS, como solicitações preflight e solicitações bloqueadas. Monitore seus logs em busca de atividades suspeitas ou erros de configuração.
Conclusão
O Compartilhamento de Recursos de Origem Cruzada (CORS) é um mecanismo de segurança vital que permite o acesso controlado de origem cruzada a recursos da web. Entender como o CORS funciona, especialmente as solicitações preflight, é crucial para construir aplicações web seguras e confiáveis. Seguindo as melhores práticas descritas neste guia, você pode lidar eficazmente com problemas de CORS e proteger sua aplicação contra vulnerabilidades potenciais. Lembre-se de sempre priorizar a segurança e considerar cuidadosamente as implicações de sua configuração de CORS.
À medida que o desenvolvimento web evolui, o CORS continuará a ser um aspecto crítico da segurança na web. Manter-se informado sobre as últimas melhores práticas e técnicas de CORS é essencial para construir aplicações web seguras e acessíveis globalmente.