Работа со ссылками в контексте плавной прокрутки требует строгого контроля над входными данными. Любой 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: — доступ к локальной системеПлавная прокрутка чаще всего привязана к якорям
(#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;
}
Это предотвращает:
После получения безопасного id выполняется поиск
элемента:
function findTarget(hash) {
return document.getElementById(hash);
}
Использование querySelector с непроверенными данными
может привести к ошибкам или уязвимостям, особенно если строка содержит
специальные CSS-символы.
Основные правила:
innerHTMLeval или подобные конструкции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'
});
});
Здесь реализована полная цепочка безопасности:
Важно исключить обработку внешних ссылок:
if (url.origin !== window.location.origin) return;
Это предотвращает:
При изменении 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' });
}
});
Слишком длинные URL могут привести к проблемам производительности:
function isValidLength(url) {
return url.href.length < 2048;
}
Ограничения:
Использование:
encodeURIComponentdecodeURIComponentПример безопасного формирования ссылки:
const id = 'section-1';
const safeHash = encodeURIComponent(id);
const link = `#${safeHash}`;
Любая работа с URL должна учитывать возможные сбои:
try {
const url = new URL(input);
} catch (e) {
console.error('Некорректный 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);
}
}
}
Такой подход:
Эти меры обеспечивают стабильную и безопасную работу плавной прокрутки даже в условиях недоверенной среды.