Open Redirect

Open Redirect — уязвимость, при которой веб-приложение перенаправляет пользователя на произвольный URL из параметра запроса без валидации, что позволяет злоумышленникам создавать фишинговые ссылки с доверенным доменом.

Зачем нужно

Ссылка https://bank.com/login?next=https://evil.com выглядит надёжно, потому что начинается с домена банка. После клика пользователь попадает на фишинговый сайт. Open Redirect используется в фишинге, bypass OAuth-flow и обходе Content-Security-Policy.

Где используется

  • Параметры редиректа после логина (?next=, ?redirect=, ?returnUrl=)
  • OAuth callback URL
  • Страницы "вы покидаете сайт"

Основной контент

Уязвимый код

// УЯЗВИМО — перенаправляет куда угодно
app.get('/login', (req, res) => {
  // После авторизации
  res.redirect(req.query.next()); // ?next=https://evil.com
});

Атака через URL-трюки

# Обходы наивной проверки "начинается с /"
https://trusted.com/redirect?to=//evil.com
https://trusted.com/redirect?to=https://evil.com
https://trusted.com/redirect?to=%2F%2Fevil.com     (URL-encoded)
https://trusted.com/redirect?to=\/evil.com          (backslash)

Безопасный редирект — whitelist подхода

const ALLOWED_PATHS = new Set(['/dashboard', '/profile', '/orders']);

function safeRedirect(res, url) {
  try {
    // Только относительные пути к нашему домену
    const parsed = new URL(url, 'https://example.com');
    if (parsed.origin !== 'https://example.com') {
      return res.redirect('/');
    }
    if (!ALLOWED_PATHS.has(parsed.pathname)) {
      return res.redirect('/');
    }
    res.redirect(parsed.pathname + parsed.search);
  } catch {
    res.redirect('/');
  }
}

app.get('/login', authenticate, (req, res) => {
  safeRedirect(res, req.query.next() || '/dashboard');
});

Валидация относительных URL

function isRelativeUrl(url) {
  // Разрешаем только пути начинающиеся с /
  // Отклоняем // (protocol-relative), http://, и т.д.
  return /^\/[^/\\]/.test(url);
}

const next = req.query.next();
const redirectTo = isRelativeUrl(next) ? next : '/dashboard';
res.redirect(redirectTo);

Частые ошибки

  • Проверка только начала строки: if (url.startsWith('/'))//evil.com пройдёт проверку
  • Использование url.includes(hostname)evil.com?trusted.com обойдёт
  • Доверие заголовку Referer для редиректа — легко подделать
  • Отсутствие fallback на / при невалидном URL

Связанные темы

Ресурсы