XSS и безопасность при работе с данными

В веб-картографии основная поверхность атаки формируется за счёт динамического отображения внешних данных: GeoJSON, WMS/GetFeatureInfo, атрибутов объектов, пользовательских слоёв и всплывающих окон. Любая точка, полигон или подпись часто содержит произвольные поля, которые интерпретируются интерфейсом как текст или HTML. Именно здесь возникает ключевая проблема XSS: данные становятся частью DOM без достаточной очистки.

В контексте OpenLayers опасность усиливается тем, что библиотека предоставляет гибкие механизмы рендеринга через стили, попапы и пользовательские функции отрисовки. Эта гибкость требует строгого разделения между данными и представлением.


Основные векторы XSS в картографических приложениях

Атрибуты объектов (Feature properties)

Каждый объект векторного слоя может содержать произвольные свойства:

  • название
  • описание
  • HTML-фрагменты (часто ошибочно)
  • ссылки
  • метаданные

Опасность возникает при использовании таких значений в интерфейсе:

  • всплывающие окна (overlay)
  • карточки объектов
  • списки объектов
  • легенды

Классическая ошибка — прямое внедрение:

popup.innerHTML = feature.get('description');

Если description приходит из внешнего GeoJSON или API, он становится полноценным XSS-вектором.


Всплывающие окна и overlays

Overlays часто строятся через innerHTML, так как разработчики хотят поддерживать форматирование. Это создаёт риск внедрения:

  • <script>
  • inline onerror
  • jav * ascript: URI
  • скрытых SVG-обработчиков

Особенно опасны сценарии, где popup формируется через шаблон:

content.innerHTML = `
  <h3>${name}</h3>
  <p>${description}</p>
`;

Даже если имя кажется безопасным, оно может содержать закрывающие теги и внедрение новых DOM-узлов.


GeoJSON и внешние источники данных

GeoJSON часто загружается с API или пользовательских сервисов. Основные проблемы:

  • отсутствие валидации схемы
  • доверие к строковым полям properties
  • смешивание данных и HTML

Пример опасного поля:

{
  "type": "Feature",
  "properties": {
    "name": "<img src=x oner ror=alert(1)>"
  }
}

Если это значение напрямую отображается, возникает XSS.


WMS / GetFeatureInfo

Запросы GetFeatureInfo могут возвращать HTML-ответы от серверов ГИС. Если сервер не очищает данные, клиентское приложение автоматически становится уязвимым.

Типичные проблемы:

  • HTML в атрибутах слоя
  • вставка неподконтрольных ссылок
  • неожиданные script-теги в описаниях

SVG и стилизация иконок

OpenLayers позволяет использовать SVG-иконки и динамические стили. Опасные ситуации:

  • загрузка внешних SVG без очистки
  • inline SVG с event-обработчиками
  • использование пользовательских URL для icon.src

SVG может содержать:

  • onload
  • onclick
  • <script>

Принципы безопасного рендеринга данных

Разделение данных и DOM

Базовое правило: данные никогда не должны интерпретироваться как HTML.

Безопасный подход:

  • использовать textContent вместо innerHTML
  • экранировать все строковые поля
  • не доверять GeoJSON и API
const el = document.createElement('div');
el.textContent = feature.get('description');

Очистка HTML перед вставкой

Если HTML всё же необходим (например, для форматирования карточек объектов), требуется строгая санация:

  • DOMPurify
  • строгий allowlist тегов
  • удаление атрибутов событий

Пример подхода:

import DOMPurify from 'dompurify';

popup.innerHTML = DOMPurify.sanitize(rawHtml);

Важно: даже очищенный HTML должен быть минимизирован по возможностям.


Ограничение шаблонов

Шаблоны интерфейса не должны включать прямую интерполяцию внешних данных:

// небезопасно
`<div>${feature.get('name')}</div>`

Безопаснее:

const div = document.createElement('div');
div.appendChild(document.createTextNode(feature.get('name')));

Безопасная работа с событиями карты

Click-события и feature picking

При обработке кликов по карте часто извлекаются объекты:

map.on('click', (event) => {
  const feature = map.forEachFeatureAtPixel(event.pixel, f => f);
});

Опасность возникает при немедленном отображении свойств без проверки.

Безопасный подход:

  • проверка типа данных
  • фильтрация неожиданных полей
  • отказ от HTML-интерпретации

Защита от инъекций через координаты

Хотя координаты кажутся безопасными, они могут использоваться в:

  • генерации URL
  • логировании
  • отображении в DOM

Опасность возникает при вставке без проверки:

innerHTML = `Lat: ${lat}, Lon: ${lon}`;

Даже числовые данные должны выводиться как текстовые узлы.


Контроль внешних URL и ресурсов

Tile servers и CORS

Подключение тайловых серверов:

  • требует проверки источника
  • может быть вектором подмены изображений
  • может содержать tracking или injection через URL-параметры

Важно:

  • использовать whitelist доменов
  • запрещать произвольные URL из пользовательского ввода

Проверка ссылок в атрибутах

Любые ссылки из данных должны проходить фильтрацию:

  • только http/https
  • запрет jav * ascript:
  • проверка домена

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

<a href="jav * ascript:alert(1)">link</a>

Content Security Policy как базовый слой защиты

CSP снижает ущерб даже при ошибках:

  • запрет inline script
  • запрет eval
  • ограничение источников изображений и скриптов

Рекомендуемая стратегия:

  • script-src 'self'
  • запрет 'unsafe-inline'
  • строгие правила для img-src

CSP не заменяет санацию, но блокирует большинство XSS-пейлоадов.


Защита функций стилей и рендеринга

OpenLayers позволяет использовать функции:

style: function(feature) {
  return new Style({
    text: new Text({
      text: feature.get('name')
    })
  });
}

Опасность:

  • возврат HTML вместо текста
  • динамическая генерация стилей из внешних данных

Правило:

  • стиль должен обрабатывать только примитивы
  • никакого HTML в стилях

Валидация структуры данных

Перед использованием внешних источников:

  • проверка схемы GeoJSON
  • ограничение типов полей
  • удаление неизвестных атрибутов

Пример подхода:

  • allowlist свойств (name, id, type)
  • игнорирование остальных полей

Безопасная работа с изображениями и иконками

Иконки часто загружаются по URL из данных:

Риски:

  • подмена изображения
  • XSS через SVG
  • tracking pixel

Меры:

  • фиксированный список иконок
  • запрет пользовательских URL
  • проверка MIME-типа

Изоляция данных и UI-слоя

Архитектурный принцип:

  • слой данных не должен знать о DOM
  • слой UI не должен напрямую интерпретировать данные как HTML
  • промежуточный слой трансформации обязателен

Типичная схема:

  1. загрузка данных
  2. валидация
  3. нормализация
  4. безопасное отображение

Типовые ошибки при интеграции с API

  • доверие к сторонним GIS-сервисам
  • отображение raw HTML из GetFeatureInfo
  • отсутствие sanitization в popups
  • динамическая генерация HTML-легенд

Каждая из этих ошибок приводит к тому, что внешние данные становятся исполняемым контентом внутри страницы.


Минимальный безопасный паттерн отображения объектов

const name = feature.get('name');

const el = document.createElement('div');
const title = document.createElement('strong');
title.textContent = name;

el.appendChild(title);
popup.setElement(el);

Ключевая идея: никакого HTML-строкового конструирования.


Итоговые принципы защиты

  • любые внешние данные считаются недоверенными
  • DOM строится только через безопасные API
  • HTML используется только после санации
  • SVG и URL требуют строгой фильтрации
  • CSP применяется как обязательный слой защиты
  • GeoJSON и GIS-ответы всегда проходят валидацию
  • визуализация отделена от данных на архитектурном уровне