Безопасные редиректы

В контексте веб-приложений редиректы являются одной из ключевых операций маршрутизации. Неправильная организация перенаправлений может привести к уязвимостям, включая открытые редиректы, которые позволяют злоумышленнику перенаправлять пользователей на сторонние, потенциально вредоносные ресурсы. Universal Router предоставляет гибкий механизм для управления редиректами, но требует правильного подхода для обеспечения безопасности.

Механизм редиректа в Universal Router

Universal Router использует концепцию объектов маршрутов с поддержкой колбэков и асинхронных обработчиков. Редирект реализуется через возврат объекта с ключом redirect:

import UniversalRouter from 'universal-router';

const routes = [
  {
    path: '/old-page',
    action: () => ({ redirect: '/new-page' })
  }
];

const router = new UniversalRouter(routes);

В этом примере любое обращение к /old-page будет безопасно перенаправлено на /new-page. Основная задача — убедиться, что значение redirect контролируется сервером или приложением, а не формируется динамически из внешних источников.

Опасность динамических URL

Ошибочной практикой является использование значений, получаемых из параметров запроса, например:

{
  path: '/login',
  action: ({ query }) => ({ redirect: query.next }) // опасно
}

Если query.next содержит сторонний URL, пользователь может быть перенаправлен на вредоносный сайт. Для предотвращения этой уязвимости следует:

  • Ограничивать список допустимых URL, например, только внутренние пути приложения.
  • Проверять формат URL с помощью регулярных выражений или библиотек для парсинга URL.
  • Использовать абсолютные проверки хоста при редиректах.

Пример безопасной фильтрации:

const allowedPaths = ['/dashboard', '/profile', '/settings'];

{
  path: '/login',
  action: ({ query }) => {
    const target = allowedPaths.includes(query.next) ? query.next : '/dashboard';
    return { redirect: target };
  }
}

Асинхронные редиректы

Universal Router поддерживает асинхронные обработчики, что позволяет реализовать редиректы на основе результатов API-запросов или данных авторизации:

{
  path: '/admin',
  async action(context) {
    const user = await context.getUser();
    if (!user.isAdmin) {
      return { redirect: '/unauthorized' };
    }
    return 'Welcome, admin';
  }
}

Здесь редирект выполняется только после проверки прав пользователя, что предотвращает неавторизованный доступ к закрытым страницам.

Защита от циклических редиректов

При построении сложной схемы маршрутов необходимо следить за тем, чтобы редиректы не образовывали циклы:

{
  path: '/a',
  action: () => ({ redirect: '/b' })
},
{
  path: '/b',
  action: () => ({ redirect: '/a' })
}

Для предотвращения бесконечных переходов Universal Router позволяет передавать контекст с историей маршрутов и проверять повторяющиеся редиректы:

async function safeAction(context) {
  if (context.history.includes('/target')) {
    return 'Ошибка: циклический редирект';
  }
  return { redirect: '/target' };
}

Использование функции resolveRedirect

Для централизации логики безопасных редиректов можно определить функцию resolveRedirect, которая проверяет допустимость URL перед возвратом:

function resolveRedirect(target) {
  const allowed = ['/home', '/profile', '/dashboard'];
  return allowed.includes(target) ? target : '/home';
}

{
  path: '/login',
  action: ({ query }) => ({ redirect: resolveRedirect(query.next) })
}

Это упрощает поддержку и обеспечивает консистентность правил редиректа во всем приложении.

Контроль редиректов в SPA

В приложениях типа SPA (Single Page Application) важно разделять внутренние маршруты и полные внешние URL. Universal Router позволяет отличать переходы по внутренним путям от переходов на внешний сайт. В качестве дополнительной защиты:

  • Всегда использовать router.resolve для внутренних редиректов.
  • Никогда не передавать значение window.location.href напрямую в редирект без проверки.

Итоговые рекомендации по безопасным редиректам

  1. Фильтровать входные URL — разрешать только внутренние пути.
  2. Использовать функцию проверки для динамических редиректов.
  3. Проверять права доступа перед редиректом.
  4. Отслеживать историю маршрутов, чтобы избежать циклов.
  5. Разделять внутренние и внешние переходы в SPA-приложениях.

Эти методы позволяют использовать редиректы в Universal Router эффективно и безопасно, минимизируя риск атак через открытые перенаправления.