В контексте веб-приложений редиректы являются одной из ключевых операций маршрутизации. Неправильная организация перенаправлений может привести к уязвимостям, включая открытые редиректы, которые позволяют злоумышленнику перенаправлять пользователей на сторонние, потенциально вредоносные ресурсы. 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 контролируется
сервером или приложением, а не формируется динамически из
внешних источников.
Ошибочной практикой является использование значений, получаемых из параметров запроса, например:
{
path: '/login',
action: ({ query }) => ({ redirect: query.next }) // опасно
}
Если query.next содержит сторонний 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 (Single Page Application) важно разделять внутренние маршруты и полные внешние URL. Universal Router позволяет отличать переходы по внутренним путям от переходов на внешний сайт. В качестве дополнительной защиты:
router.resolve для внутренних
редиректов.window.location.href
напрямую в редирект без проверки.Эти методы позволяют использовать редиректы в Universal Router эффективно и безопасно, минимизируя риск атак через открытые перенаправления.