O que é Cross-Origin?
O conceito de cross-origin refere-se a situações em que um documento ou script carregado de uma origem tenta interagir com um recurso de outra origem. No contexto web, uma "origem" é definida pela combinação de protocolo, domínio e porta. A restrição fundamental é imposta pela Política de Mesma Origem (Same-Origin Policy - SOP). Introduzida originalmente pela Netscape em 1995, essa política é uma medida crítica de segurança que impede que scripts maliciosos acessem dados confidenciasi de outros domínios, prevenindo ataques como XSS (Cross-Site Scripting) e CSRF (Cross-Site Request Forgery). A SOP restringe as seguintes interações entre origens diferentes: - Acesso ao DOM ou objetos JavaScript de outras janelas/iframes.
- Leitura de cookies, LocalStorage ou IndexedDB.
- Envio de requisições via XMLHttpRequest ou Fetch API (AJAX).
Cenários Comuns de Bloqueio
| URL de Origem (A) | URL de Destino (B) | Resultado | Motivo |
|---|---|---|---|
| http://meusite.com/inicio.js | http://meusite.com/dados.json | Permitido | Mesma origem |
| http://meusite.com/app.js | https://meusite.com/api | Bloqueado | Protocolos diferentes (http vs https) |
| http://meusite.com:8080/js | http://meusite.com:9000/api | Bloqueado | Portas diferentes |
| http://vendas.meusite.com | http://blog.meusite.com | Bloqueado | Subdomínios diferentes |
Soluções para Comunicação entre Origens
1. JSONP (JSON with Padding)
Embora antigo, o JSONP aproveita o fato de que tags <script> não são bloqueadas pela SOP. Ele funciona injetando um script dinamicamente que executa uma função de retorno predefinida. Implementação no Cliente:```
const scriptElement = document.createElement('script'); scriptElement.type = 'text/javascript';
// Define o callback e passa parâmetros na URL scriptElement.src = 'https://api.externa.com/get-dados?id=123&callback=processarResposta'; document.head.appendChild(scriptElement);
function processarResposta(data) { console.log('Dados recebidos:', data); }
**Exemplo de Resposta do Servidor:**```
processarResposta({"status": "sucesso", "valor": 1500});
Nota: O JSONP suporta apenas o método GET.#### 2. CORS (Cross-Origin Resource Sharing)
É o padrão moderno e recomendado. O servidor define cabeçalhos HTTP que informam ao navegador quais origens têm permissão para acessar os recursos. Configuração no Cliente (com suporte a Cookies):```
const requisicao = new XMLHttpRequest(); // Habilita o envio de cookies para o domínio cruzado requisicao.withCredentials = true;
requisicao.open('POST', 'https://api.servico.com/v1/auth', true); requisicao.setRequestHeader('Content-Type', 'application/json'); requisicao.send(JSON.stringify({ user: 'admin' }));
requisicao.onreadystatechange = function() { if (requisicao.readyState === 4 && requisicao.status === 200) { console.log(requisicao.responseText); } };
**Configuração no Servidor (Exemplo Node.js/Express):**```
res.writeHead(200, {
'Access-Control-Allow-Origin': 'https://meu-frontend.com',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Content-Type': 'application/json'
});
3. postMessage API
Ideal para comunicação entre janelas, abas ou iframes de origens diferentes. Envio (Página Principal):```
const frame = document.getElementById('meuIframe'); const payload = { acao: 'atualizar', id: 50 };
frame.contentWindow.postMessage(JSON.stringify(payload), 'https://dominio-do-iframe.com');
**Recepção (Dentro do Iframe):**```
window.addEventListener('message', function(event) {
// Verificação de segurança da origem
if (event.origin !== 'https://dominio-esperado.com') return;
const dados = JSON.parse(event.data);
console.log('Mensagem recebida:', dados);
// Opcional: Responder de volta
event.source.postMessage('Recebido!', event.origin);
}, false);
4. Proxy Reverso com Nginx
O Nginx atua como um intermediário. O navegador faz a requisição para o mesmo domínio do front-end, e o Nginx a encaminha para o backend real, mascarando a diferença de origem. Configuração do Nginx:```
server { listen 80; server_name meu-app.com;
location /api/ {
# Encaminha as chamadas de /api/ para o backend real
proxy_pass http://backend-interno:8080/;
# Reescreve o domínio do cookie para o domínio do front
proxy_cookie_domain backend-interno meu-app.com;
# Adiciona cabeçalhos CORS se necessário
add_header Access-Control-Allow-Origin "http://meu-app.com";
add_header Access-Control-Allow-Credentials true;
}
}
#### 5. Proxy em Ambiente de Desenvolvimento (Webpack/Vite)
Ferramentas de build modernas como Webpack e Vite possuem servidores de desenvolvimento que podem atuar como proxies locais. **Configuração Webpack (webpack.config.js):**```
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://api.servidor-remoto.com',
changeOrigin: true,
pathRewrite: { '^/api': '' },
secure: false
}
}
}
};
6. document.domain
Esta técnica é limitada apenas a domínios que compartilham o mesmo domínio base (ex: vendas.site.com e loja.site.com). Em ambas as páginas:```
// Força o domínio base comum document.domain = 'site.com';
Isso permite que scripts de uma página acessem o DOM da outra via `window.parent` ou `window.frames`. #### 7. WebSockets
Diferente das requisições HTTP tradicionais, o protocolo WebSocket (ws:// ou wss://) não impõe a Política de Mesma Origem por padrão. O servidor WebSocket deve verificar o cabeçalho `Origin` durante o handshake para decidir se aceita a conexão. ```
const socket = new WebSocket('ws://api.realtime.com:8080');
socket.onopen = () => {
socket.send('Iniciando comunicação cross-domain');
};
socket.onmessage = (event) => {
console.log('Mensagem do servidor:', event.data);
};