XSS-уязвимости в веб-приложениях на CesiumJS возникают в первую очередь там, где 3D-сцена начинает взаимодействовать с HTML-интерфейсом: всплывающими окнами, информационными панелями, пользовательскими данными, внешними источниками (CZML, GeoJSON, REST-API). Несмотря на то что CesiumJS сам по себе не является источником XSS, он активно оперирует строками, которые часто напрямую попадают в DOM без дополнительной обработки, что делает его интеграции чувствительными к внедрению вредоносного скрипта.
Наиболее частые точки возникновения уязвимостей связаны с тем, как приложение отображает данные, связанные с объектами сцены:
1. Entity.description и HTML-рендеринг Описание
сущностей (Entity) может содержать HTML-строку. Cesium по
умолчанию отображает её внутри InfoBox через innerHTML.
viewer.entities.add({
name: "Object A",
description: "<b>Информация</b>"
});
Проблема возникает, когда данные приходят из внешнего источника:
description: userInput
Если userInput содержит <script> или
вредоносные обработчики событий (onerror,
onload), они могут быть выполнены в контексте страницы.
2. InfoBox и автоматическая вставка HTML Компонент InfoBox отображает содержимое description без строгой санитаризации. Это делает его одним из ключевых каналов XSS в Cesium-приложениях.
Особенно опасны конструкции:
<img src=x oner ror=alert(1)><svg onl oad=alert(1)><iframe src="jav * ascript:...">3. CallbackProperty и динамические строки Хотя
CallbackProperty используется для обновления значений в
реальном времени, он часто возвращает строки, которые затем вставляются
в UI.
description: new Cesium.CallbackProperty(() => {
return dynamicString;
}, false);
Если dynamicString формируется на основе
пользовательских данных без очистки, XSS может проникнуть через
обновляемые поля.
4. GeoJSON и CZML из внешних источников Cesium позволяет загружать данные:
Эти форматы могут содержать свойства, которые интерпретируются как HTML:
{
"properties": {
"description": "<img src=x oner ror=alert(1)>"
}
}
При отображении таких свойств в Entity без фильтрации возникает риск выполнения скриптов.
Cesium UI-компоненты (InfoBox, Popup, custom panels) часто используют:
element.innerHTML = ...Это означает, что любая строка, попавшая в описание, становится потенциальным DOM-кодом.
Ключевой момент: Cesium не гарантирует санитаризацию входных данных, потому ответственность полностью лежит на приложении.
Основная стратегия защиты — очистка HTML перед передачей в Cesium.
Использование DOMPurify
const clean = DOMPurify.sanitize(userInput);
viewer.entities.add({
name: "Safe Object",
description: clean
});
DOMPurify удаляет:
<script>onclick,
onerror)jav * ascript:)Запрет HTML и использование textContent
Если HTML не требуется, безопаснее полностью отказаться от него:
const element = document.createElement("div");
element.textContent = userInput;
В Cesium можно подменять InfoBox:
viewer.infoBox.frame.contentWindow.document.body.textContent = data;
Один из наиболее надёжных подходов — разделение:
Нельзя напрямую связывать пользовательский ввод с HTML внутри Cesium.
Пример опасного подхода:
description: `<h3>${userInput}</h3>`
Без экранирования это эквивалентно выполнению кода.
Безопасный вариант:
description: `<h3>${escapeHtml(userInput)}</h3>`
Базовая функция защиты:
function escapeHtml(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
Используется, когда требуется сохранить текст, но исключить интерпретацию как HTML.
Cesium позволяет хранить произвольные свойства:
properties: {
info: userInput
}
Опасность возникает при последующем рендеринге:
description: entity.properties.info
Даже если данные были «безопасно» сохранены, они становятся опасными при вставке в DOM.
Правильный подход:
Cesium поддерживает динамические свойства:
CallbackPropertyColorMaterialPropertyLabelGraphics.textЛюбое значение, возвращающее строку, потенциально может попасть в DOM.
Пример уязвимости:
label: new Cesium.LabelGraphics({
text: new Cesium.CallbackProperty(() => {
return userControlledText;
}, false)
});
Если userControlledText содержит HTML, он может быть
интерпретирован в UI слоях, особенно в кастомных оверлеях.
При работе с GeoJSON:
Cesium.GeoJsonDataSource.load(url)
Внешние данные следует фильтровать:
propertiesПример постобработки:
dataSource.entities.values.forEach(entity => {
if (entity.description) {
entity.description = DOMPurify.sanitize(entity.description);
}
});
Content Security Policy существенно снижает последствия XSS.
Рекомендуемые ограничения:
unsafe-inlineevalПример:
Content-Security-Policy:
script-src 'self';
object-src 'none';
base-uri 'self';
В Cesium-приложениях CSP особенно важен, так как:
В кастомных интерфейсах Cesium часто используется генерация HTML:
popup.innerHTML = `
<div>${entity.description}</div>
`;
Если entity.description не очищен, XSS становится
прямым.
Безопасная альтернатива:
popup.textContent = entity.description;
или использование безопасного рендера:
popup.innerHTML = DOMPurify.sanitize(entity.description);
При использовании Cesium вместе с UI-фреймворками:
Опасность возникает при передаче данных между слоями.
Антипаттерн:
setPopupHtml(entity.description);
Без фильтрации это приводит к XSS в React/Vue через
dangerouslySetInnerHTML.
Любой источник данных следует считать недоверенным:
Даже если данные выглядят безопасными, они могут содержать:
Снижение риска достигается архитектурно:
Пример безопасного слоя:
function renderDescription(text) {
const div = document.createElement("div");
div.textContent = text;
return div;
}
innerHTMLtextContent вместо HTML-рендеринга