Безопасная работа с URL

Работа со ссылками в контексте плавной прокрутки требует строгого контроля над входными данными. Любой URL может содержать как безопасные, так и потенциально вредоносные элементы. Основные задачи:

  • корректное извлечение якоря (hash)
  • проверка допустимости схемы (protocol)
  • нормализация пути и параметров

Для безопасного разбора URL используется встроенный объект URL:

function parseUrl(input) {
  try {
    const url = new URL(input, window.location.origin);
    return url;
  } catch (e) {
    return null;
  }
}

Ключевой момент — обязательное указание базового URL (window.location.origin), что предотвращает ошибки при работе с относительными ссылками.


Проверка протокола

Smooth Scroll обычно работает только с якорями внутри страницы или с относительными ссылками. Внешние URL могут быть источником уязвимостей.

Допустимые протоколы:

  • http:
  • https:
  • пустой (относительные ссылки)

Фильтрация:

function isSafeProtocol(url) {
  return ['http:', 'https:'].includes(url.protocol);
}

Недопустимые протоколы:

  • jav * ascript: — выполнение произвольного кода
  • data: — внедрение закодированного контента
  • file: — доступ к локальной системе

Работа с якорями (hash)

Плавная прокрутка чаще всего привязана к якорям (#section). Однако значение hash может быть подменено.

Безопасная обработка:

function getSafeHash(url) {
  if (!url.hash) return null;

  const hash = decodeURIComponent(url.hash.substring(1));

  // Разрешены только id из букв, цифр, дефисов и подчёркиваний
  if (/^[a-zA-Z0-9\-_]+$/.test(hash)) {
    return hash;
  }

  return null;
}

Это предотвращает:

  • XSS-атаки через внедрение HTML
  • попытки доступа к нестандартным селекторам

Поиск элемента в DOM

После получения безопасного id выполняется поиск элемента:

function findTarget(hash) {
  return document.getElementById(hash);
}

Использование querySelector с непроверенными данными может привести к ошибкам или уязвимостям, особенно если строка содержит специальные CSS-символы.


Защита от XSS при работе с URL

Основные правила:

  • не вставлять URL напрямую в innerHTML
  • не использовать eval или подобные конструкции
  • не доверять данным из location.hash

Опасный пример:

document.body.innerHTML += location.hash;

Безопасная альтернатива:

const text = document.createTextNode(location.hash);
document.body.appendChild(text);

Перехват кликов по ссылкам

Smooth Scroll обычно перехватывает клики по ссылкам:

document.addEventListener('click', function (e) {
  const link = e.target.closest('a');
  if (!link) return;

  const url = parseUrl(link.href);
  if (!url || !isSafeProtocol(url)) return;

  const hash = getSafeHash(url);
  if (!hash) return;

  const target = findTarget(hash);
  if (!target) return;

  e.preventDefault();

  target.scrollIntoView({
    beh * avior: 'smooth'
  });
});

Здесь реализована полная цепочка безопасности:

  1. Проверка существования ссылки
  2. Парсинг URL
  3. Проверка протокола
  4. Валидация якоря
  5. Проверка наличия элемента

Ограничение области действия

Важно исключить обработку внешних ссылок:

if (url.origin !== window.location.origin) return;

Это предотвращает:

  • вмешательство в навигацию на сторонние сайты
  • нарушение ожидаемого поведения пользователя

Работа с динамическими URL

При изменении URL через History API:

history.pushState(null, '', '#section');

необходимо повторно валидировать значение:

window.addEventListener('popstate', () => {
  const url = new URL(window.location.href);
  const hash = getSafeHash(url);

  if (!hash) return;

  const target = findTarget(hash);
  if (target) {
    target.scrollIntoView({ beh * avior: 'smooth' });
  }
});

Защита от переполнения и DoS

Слишком длинные URL могут привести к проблемам производительности:

function isValidLength(url) {
  return url.href.length < 2048;
}

Ограничения:

  • предотвращение перегрузки парсера
  • защита от злоумышленных запросов

Декодирование и кодирование URL

Использование:

  • encodeURIComponent
  • decodeURIComponent

Пример безопасного формирования ссылки:

const id = 'section-1';
const safeHash = encodeURIComponent(id);
const link = `#${safeHash}`;

Обработка ошибок

Любая работа с URL должна учитывать возможные сбои:

try {
  const url = new URL(input);
} catch (e) {
  console.error('Некорректный URL');
}

Игнорирование ошибок приводит к:

  • остановке выполнения скрипта
  • непредсказуемому поведению прокрутки

Изоляция логики Smooth Scroll

Безопасная архитектура предполагает отделение:

  • парсинга URL
  • валидации
  • прокрутки

Пример структуры:

class SmoothScrollSafe {
  handleClick(event) {
    const link = this.getLink(event);
    const url = this.parse(link);
    const hash = this.validate(url);
    const target = this.resolve(hash);

    if (target) {
      this.scroll(target);
    }
  }
}

Такой подход:

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

Итоговые принципы безопасной работы с URL

  • строгая валидация всех входных данных
  • использование встроенных API браузера
  • запрет небезопасных протоколов
  • фильтрация якорей
  • защита от XSS и инъекций
  • контроль области действия ссылок

Эти меры обеспечивают стабильную и безопасную работу плавной прокрутки даже в условиях недоверенной среды.