Introdução
Section titled “Introdução”Open Redirect é uma falha de validação de input que existe quando uma aplicação aceita inputs controlados pelo usuário, sem a sanitização adequada, que fornecem uma URL de um outro site ou aplicação externa ao contexto da aplicação original, que pode ser maliciosa ou controlada por um atacante.
Cenário Vulnerável
Section titled “Cenário Vulnerável”Digamos que em nossa aplicação, definirmos que quando um usuário entrar em uma area que é restringida para usuários logados e ele não tiver uma sessão ativa, vamos redirecioná-lo para o Login e, depois de fornecer corretamente as credenciais, deixamos ele acessar a página.
Bem, a tela de login precisa saber para onde redirecionar depois de o login finalizar, para isso, vamos usar um queryParam chamado redirectUrl. Ficaria mais ou menos assim:
O cliente vai em
https://company.com.br/perfilpara visualizar seu perfil.A aplicação verifica que ele não está logado, então o redireciona para o login, com o parâmetro para onde deve retornar depois do login:
https://company.com.br/login?redirectUrl=https://company.com.br/perfilO usuário insere suas credencias e efetua o login.
A aplicação redireciona o usuário para a URL do parâmetro, no caso,
https://company.com.br/perfil.
Agora o que aconteceria se um atacante enviasse o seguinte link para uma vítima:
https://company.com.br/login?redirectUrl=https://company2.com.br/perfilPercebeu a sutil diferença?
O atacante agora está direcionando a vítima para um site com company2.com.br e não o original com company.com.br, sendo este um site falso.
Com isso, ele poderia se passar pelo site oficial, onde coletaria informações adicionais, como cartões de crédito, credenciais, etc.
Este tipo de problema pode estar em diversos lugares:
// no backend depois de efetuar uma ação.return new RedirectView(targetUrl);// no front depois de executar alguma request ou operaçãoconst queryString = window.location.search;const params = new URLSearchParams(queryString);
const redirectUrl = params.get('redirectUrl');
window.location.href = redirectUrl// no backend depois de efetuar uma ação.$redirectUrl = $_GET['redirectUrl'];header("Location: " . $redirectUrl);exit();// no backend depois de efetuar uma ação.string redirectUrl = Request.Query["redirectUrl"];return Redirect(redirectUrl);Riscos
Section titled “Riscos”Ao permitir que nossas aplicações estejam vulneráveis a Open Redirect, tornamos possível que:
- Usuários da aplicação serem vítimas de ataques de phishing.
- Usuários da aplicação terem suas credenciais de acesso roubadas.
- Exposição de informações sensíveis de usuários da aplicação.
- A reputação da empresa ser danificada por estar associada a ataques de phishing aos clientes.
- Aplicação de medidas legais em decorrência da exposição de dados pessoais sensíveis dos usuários.
Como Mitigar
Section titled “Como Mitigar”Qualquer parâmetro que nossa aplicação recebe como um input ou variável e realiza uma navegação para ela deve possuir as seguintes validações:
URL Interna
Section titled “URL Interna”Validar se o domínio para a qual vamos redirecionar é permitido por nossa allowlist.
Exemplo: se apenas podemos navegar para links de company.com.br ou company2.com.br, devemos validar o domínio da URL antes de realizar o redirecionamento.
// Valida se uma url string pertence a um domínio em específico.// Exemplo:// urlString = "https://subdomain.example.com/"// targetDomain = "example.com"public static boolean isValidDomain(String urlString, String targetDomain) { try { URI uri = new URI(urlString); String host = uri.getHost();
if (host.startsWith("www.")) { host = host.substring(4); // Remove 'www.' }
return host.equalsIgnoreCase(targetDomain);
} catch (URISyntaxException e) { return false; }}// Valida se uma url string pertence a um domínio em específico.// Exemplo:// urlString = "https://subdomain.example.com/"// targetDomain = "example.com"function isValidDomain(urlString, targetDomain) { try { const url = new URL(urlString); let host = url.hostname;
if (host.startsWith('www.')) { host = host.substring(4); // Remove 'www.' }
return host.toLowerCase() === targetDomain.toLowerCase(); } catch { return false; }}// Valida se uma url string pertence a um domínio em específico.// Exemplo:// urlString = "https://subdomain.example.com/"// targetDomain = "example.com"function isValidDomain(string $urlString, string $targetDomain): bool { $host = parse_url($urlString, PHP_URL_HOST);
if ($host === null || $host === false) { return false; }
if (str_starts_with($host, 'www.')) { $host = substr($host, 4); // Remove 'www.' }
return strcasecmp($host, $targetDomain) === 0;}// Valida se uma url string pertence a um domínio em específico.// Exemplo:// urlString = "https://subdomain.example.com/"// targetDomain = "example.com"public static bool IsValidDomain(string urlString, string targetDomain){ if (!Uri.TryCreate(urlString, UriKind.Absolute, out Uri? uri)) return false;
string host = uri.Host;
if (host.StartsWith("www.", StringComparison.OrdinalIgnoreCase)) host = host.Substring(4); // Remove 'www.'
return host.Equals(targetDomain, StringComparison.OrdinalIgnoreCase);}URL Externa
Section titled “URL Externa”Caso a URL em questão possa, por regra de negócio, ser externa, avisar o usuário em uma modal de confirmação que ele está sendo redirecionado para uma URL externa.