При работе с удалёнными источниками данных в Tom Select ключевым аспектом становится разделение доверенных и недоверенных зон. Любой внешний API, REST-эндпоинт или прокси-сервис следует рассматривать как потенциально недостоверный источник, даже если он находится в пределах внутренней инфраструктуры.
Удалённые данные могут поступать в следующих формах:
Основная проблема заключается в том, что Tom Select по умолчанию ориентирован на гибкость отображения, а не на жёсткую валидацию входящих данных. Это создаёт поверхность для XSS, инъекций HTML и подмены логики отображения.
Типичная интеграция с удалённым источником строится вокруг
load или loadThrottle:
new TomSelect("#select", {
valueField: "id",
labelField: "text",
searchField: "text",
load: function(query, callback) {
fetch(`/api/search?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(callback)
.catch(() => callback());
}
});
На первый взгляд такая схема безопасна, однако фактически она доверяет:
textidЛюбое из этих предположений может быть нарушено сервером или промежуточным прокси.
Tom Select по умолчанию может отображать значения как HTML в зависимости от настроек рендеринга. Если сервер возвращает:
{
"id": 1,
"text": "<img src=x oner ror=alert(1)>"
}
и отсутствует экранирование, результатом становится выполнение скрипта в браузере.
Базовый принцип безопасной конфигурации — отказ от HTML в данных:
new TomSelect("#select", {
valueField: "id",
labelField: "text",
searchField: "text",
render: {
option: function(data, escape) {
return `<div>${escape(data.text)}</div>`;
},
item: function(data, escape) {
return `<div>${escape(data.text)}</div>`;
}
}
});
Функция escape() является критическим элементом защиты,
так как она предотвращает интерпретацию входящих данных как HTML.
Клиентская защита не может считаться достаточной. Любая система с удалёнными данными должна реализовывать фильтрацию на сервере.
Пример серверной логики:
function sanitizeText(input) {
return input
.replace(/<[^>]*>/g, "")
.slice(0, 200);
}
Однако даже такая фильтрация не гарантирует защиту от сложных обходов через сущности HTML или Unicode-эквиваленты.
Tom Select ожидает строго определённую структуру объектов. Нарушение структуры может привести к:
[
{
"id": "123",
"text": "Name",
"disabled": false
}
]
Любые дополнительные поля должны игнорироваться клиентом.
valueОсобую опасность представляет поле valueField. Если
сервер позволяет клиенту влиять на идентификатор без проверки, возможны
сценарии:
idid без проверкиПолный контроль над отображением данных достигается через
render:
render: {
option: function(data, escape) {
return `
<div class="option">
<span class="title">${escape(data.text)}</span>
</div>
`;
}
}
Ключевой принцип: никакие данные не вставляются в DOM без экранирования.
Функция create в Tom Select позволяет пользователю
добавлять новые элементы. В связке с удалённым API это создаёт
дополнительный риск:
create: true
Проблема заключается в том, что пользователь может:
create: function(input, callback) {
fetch("/api/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: input })
})
.then(res => res.json())
.then(data => callback(data))
.catch(() => callback(null));
}
При этом сервер обязан повторно валидировать input, не
доверяя клиенту.
Удалённый поиск через load может быть использован
для:
new TomSelect("#select", {
loadThrottle: 300
});
Дополнительно рекомендуется:
Любой ответ сервера должен обрабатываться через защитный слой:
.then(data => {
if (!Array.isArray(data)) return callback([]);
const safe = data.filter(item =>
item && typeof item.id === "string" && typeof item.text === "string"
);
callback(safe);
});
Такой подход снижает риск:
Поле searchField влияет на то, как Tom Select фильтрует
результаты. При удалённой загрузке важно помнить, что сервер уже
выполняет поиск, а клиентская фильтрация может:
Рекомендуется отключать лишнюю локальную фильтрацию:
new TomSelect("#select", {
score: function() {
return function() { return 1; };
}
});
Архитектурно важно разделять:
Любая логика, смешивающая эти уровни, увеличивает вероятность уязвимостей.
Часто данные проходят через:
Каждый промежуточный слой может:
Поэтому финальная проверка должна быть ближе всего к клиенту, а критическая валидация — на сервере источника.
Устойчивый подход включает несколько обязательных уровней:
textКаждый из этих уровней компенсирует слабости других, создавая многослойную защиту при работе с удалёнными источниками данных.