Executando verificação de segurança...
1

Como contornar as restrições de CORS em solicitações entre domínios? (com proxy)

Olá, quero desenvolver um servidor web proxy em PHP que permita aos usuários acessar outros sites (de domínios diferentes) através dele, contornando o erro de CORS (Compartilhamento de Recursos de Origem Cruzada). O objetivo é criar uma camada intermediária que faça solicitações em nome do usuário, permitindo assim o acesso a conteúdos que possam estar bloqueados em determinados domínios devido às restrições de CORS.

Segue um exemplo de código que já elaborei:

<?php
// Verifica se a URL de destino foi fornecida na query string
if (isset($_GET['url'])) {
$url = $_GET['url'];

// Configura opções de stream para o contexto de file_get_contents
$options = array(
    'http' => array(
        'header' => "User-Agent: PHP\r\n",
        'method' => $_SERVER['REQUEST_METHOD'],
        'follow_location' => false // Evita redirecionamentos no proxy
    )
);

// Cria o contexto para a requisição
$context = stream_context_create($options);

// Realiza a requisição para a URL de destino usando file_get_contents
$result = file_get_contents($url, false, $context);

// Pega os headers da resposta para enviar ao cliente
$headers = $http_response_header;

// Envia os headers para o cliente
foreach ($headers as $header) {
    header($header);
}

echo $result;

} else {
// Caso a URL de destino não tenha sido fornecida, retorna um erro
header("HTTP/1.0 400 Bad Request");
echo "URL de destino não especificada.";
}

Embora esse código aborde parte do problema, ainda há a questão pendente das solicitações de origem cruzada (CORS). Gostaria de entender como desenvolver uma solução que possa resolver completamente o problema do CORS. Agradeço antecipadamente por qualquer orientação, ajuda ou exemplo de código que possam direcionar-me para a solução adequada. Em relação ao tópico: "Como contornar as restrições de CORS em solicitações entre domínios?"

Carregando publicação patrocinada...
2

Basicamente, vc precisa adicionar alguns cabeçalhos especificos.

Antes daquele seu if, adicione isto:

$method = $_SERVER['REQUEST_METHOD'];
$origin = $_SERVER['HTTP_ORIGIN'];

if ($method === 'OPTIONS') {
  $requestMethod = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'];
  $requestHeaders = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'];
  header("Access-Control-Allow-Methods: $requestMethod");
  header("Access-Control-Allow-Headers: $requestHeaders");
}

header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');

// ...

Antes de alguns tipos de requisições o navegador faz uma requisição extra com o método OPTIONS chamada de pre-flight request.

Na pre-flight request ele espera que o servidor responda quais métodos e quais cabeçalhos ele permite. O cabeçalho "Access-Control-Allow-Headers' é importante porque seu cliente pode querer enviar cabeçalhos diferentes e vc precisa permiti-los.

O cabeçalho 'Access-Control-Allow-Origin' deve estar presente em qualquer requisição. Seu valor pode ser '*' indicando que qualquer origin é permitida, mas o asterisco é inválido quando vc está usando 'Access-Control-Allow-Credentials: true', por isso temos que usar a origin exata do cliente.

O cabeçalho 'Access-Control-Allow-Credentials: true' é importante, pois permite que o cliente envie cookies junto com a requisição.

Não tenho certeza se isso cobre todos os casos de uso, mas acredito que é um bom começo para o que vc precisa. Para entender melhor o protocolo CORS, recomendo a especificação: https://fetch.spec.whatwg.org/#http-cors-protocol

1

Não sei se entendi muito bem, mas não tenho como mudar o cabeçalho do outro domínio já que não tenho acesso a eles. O meu código acima faz a requisição do conteúdo da página de domínio diferente (no qual não tenho acesso), e assim exibe no meu domínio, por exemplo:

examplo.com.br/proxy.php?url=https://outrodominio.com.br. Mas suponhamos que o outro domínio https://outrodominio.com.br faça uma chamada para uma folha de estilo na url https://outrodominio.com.br/style.css então ainda vou ter problema com o Compartilhamento de Recursos de Origem Cruzada.

Queria saber como posso resolver isso? No entanto, já agradeço pela ajuda e referência.

0

Seguindo o exemplo para deixar mais claro, não tem como eu simplesmente fazer uma requisição para https://outrodominio.com.br/ senão teria restrição pelo CORS. Por isso faço pelo PHP e a página e exibida no meu domínio como no exemplo examplo.com.br/proxy.php?url=https://outrodominio.com.br, assim posso fazer a requisição para na minha origem examplo.com.br.

Os links apresentados são apenas ilustrativos.

1

Moço, eu entendi perfeitamente sua situação, por isso eu disse "adicine isso antes daquele seu if".

Aquele código que vc mostrou onde tem o trecho if (isset($_GET['url']))
não está no seu arquivo proxy.php? Pois então, é a esse if que eu me referia e é antes dele que vc tem que adicionar o que eu sugeri.

1
1
1

Testei no chrome, edge e firefox. Claro o código etá todo aqui:

<?php

//trecho adicionado
$method = $_SERVER['REQUEST_METHOD'];
$origin = $_SERVER['HTTP_ORIGIN'];

if ($method === 'OPTIONS') {
  $requestMethod = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'];
  $requestHeaders = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'];
  header("Access-Control-Allow-Methods: $requestMethod");
  header("Access-Control-Allow-Headers: $requestHeaders");
}

header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');
//trecho adicionado

// Verifica se a URL de destino foi fornecida na query string
if (isset($_GET['url'])) {
$url = $_GET['url'];

// Configura opções de stream para o contexto de file_get_contents
$options = array(
    'http' => array(
        'header' => "User-Agent: PHP\r\n",
        'method' => $_SERVER['REQUEST_METHOD'],
        'follow_location' => false // Evita redirecionamentos no proxy
    )
);

// Cria o contexto para a requisição
$context = stream_context_create($options);

// Realiza a requisição para a URL de destino usando file_get_contents
$result = file_get_contents($url, false, $context);

// Pega os headers da resposta para enviar ao cliente
$headers = $http_response_header;

//Envia os headers para o cliente
foreach ($headers as $header) {
    header($header);
}

echo $result;

} else {
// Caso a URL de destino não tenha sido fornecida, retorna um erro
header("HTTP/1.0 400 Bad Request");
echo "URL de destino não especificada.";
}
2
1

Testei, me retorna isso no console (No localhost:8001):

localhost/:1 Access to fetch at 'http://localhost:8003/destino.php' from origin 'http://localhost:8001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
(índice):12

Uncaught (in promise) TypeError: Failed to fetch
at doRequest ((índice):12:9)
at (índice):21:7
doRequest @ (índice):12
(anônimo) @ (índice):21
Promise.then (assíncrono)
doRequest @ (índice):18
(anônimo) @ (índice):21

(índice):18
Warning: file_get_contents(http://localhost:8003/destino.php): Failed to open stream: Cannot assign requested address in /var/www/html/proxy.php on line 28
Warning: Undefined variable $http_response_header in /var/www/html/proxy.php on line 29
Warning: foreach() argument must be of type array|object, null given in /var/www/html/proxy.php on line 31

Está usando qual navegador?

0

Coloquei online em https://inteligenciaplena.com.br/teste/proxy.php e não me retornou mais esse erro, talvez seja a verão do php local! Mas como pode ver no console ainda tenho erros relacionados a Compartilhamento de Recursos de Origem Cruzada: https://inteligenciaplena.com.br/teste/proxy.php?url=https://joveminventor.com.br/

São referentes a recursos do site, como css e js... Como poderia resolvê-los? Talvez mudar o url para mandar para o proxy, isso seria viável?

2

Agora eu entendi aquele erro HTTP_ORIGIN ser indefinido. Realmente não estávamos falando da mesma coisa.

Veja bem, se vc está acessando do seu navegador o endereço https://inteligenciaplena.com.br/teste/proxy.php?url=https://joveminventor.com.br/ vc não está sujeito ao CORS e usar seu proxy é desnecessário.

Na verdade vc está criando um problema de CORS que antes não existia como vc mesmo notou naqueles erros relacionados com CSS, JS e etc...

Pensa comigo: Por que eu iria acessar https://inteligenciaplena.com.br/teste/proxy.php?url=https://joveminventor.com.br/ se eu posso acessar https://joveminventor.com.br/ diretamente?

O CORS ocorre quando eu tento acessar https://joveminventor.com.br/ a partir de um endereço não autorizado usando o fetch, como eu fiz naquele exemplo do repositório. É nesta situação que seu proxy faz sentido.

Então eu imaginei que vc queria usar o fetch para fazer uma requisição para alguma API que retornaria um JSON.

Claro que vc pode usar o fetch para requisitar um arquivo HTML, mas aí vc vai ter que impelementar uma lógica para substituir a URL de todos os CSS, JS e qualquer outro arquivo que o HTML desejado esteja referenciando.

Veja novamente o meu repositório. Eu o atualizei com a lógica que lida com arquivos CSS. Mas é só um exemplo, pois está incompleto. Veja com atenção o README também porque eu modifiquei alguns comandos.

0
1
1
1

Ah, uma dúvida: A requisição que gerou este erro foi feita a partir de um navegador? Porque é muito estranho que o HTTP_ORIGIN não esteja presente, pois o navegador sempre envia este cabeçalho sem a gente precisar fazer nada.