Сегодня экосистема языка Go охватывает огромный спектр задач, от микросервисов до критически важных финансовых платформ. Простота и эффективность Go снискали ему широкую популярность среди разработчиков, однако, как и любой инструмент, язык имеет свои «подводные камни». В этой статье мы рассмотрим неожиданную угрозу, скрывающуюся в самом сердце приложений на Go — его парсерах JSON, XML и YAML.
🧩 Парсеры — дверь для атак
Большинство разработчиков воспринимают парсеры форматов данных как нечто простое и надёжное. Но на практике именно они нередко становятся причиной уязвимостей, позволяя злоумышленникам обойти механизмы безопасности.
Наиболее распространённые форматы, используемые в Go:
- 🔸 JSON (встроенный пакет encoding/json)
- 🔹 XML (встроенный пакет encoding/xml)
- 📗 YAML (сторонняя библиотека yaml.v3)
Каждый из этих форматов обладает своими особенностями, которые могут использоваться в атаках.
🎯 Сценарии атак
Взглянем на три ключевых типа атак, описанные в оригинальной статье компании Trail of Bits, и разберём, как это работает на практике:
💥 Сценарий 1: (Де)сериализация нежелательных данных
Частая ошибка разработчиков — использование специального символа - в тегах структур Go для исключения полей из (де)сериализации:
type User struct {
Username string `json:"username"`
IsAdmin bool `json:"-,"` // Опасная ошибка
}
Оказывается, это поле всё ещё может быть заполнено, если злоумышленник отправит JSON с ключом "-". Пример:
jsonCopyEdit{
"-": true
}
⚠️ Результат: злоумышленник может повысить свои привилегии, активировав поле IsAdmin.
✅ Правильное решение: Использовать тег без запятой:
IsAdmin bool `json:"-"`
🔄 Сценарий 2: Несогласованность между парсерами
Парсеры Go проявляют нестандартное поведение с повторяющимися или различными по регистру ключами:
- JSON-парсер принимает последний ключ при дублировании и нечувствителен к регистру.
- XML-парсер ведёт себя аналогично JSON при дублировании.
- YAML-парсер корректно выдаёт ошибку при дублировании.
Например, JSON-структура:
{
"action": "UserAction",
"ACTION": "AdminAction"
}
😱 Проблема: Go воспримет последнее значение (AdminAction), а другой парсер (например, на Python или JavaScript) — первое (UserAction). Это позволяет обойти авторизацию, если системы используют разные парсеры.
✅ Рекомендация: Всегда использовать единый парсер во всех связанных системах и явно проверять наличие дублирующихся ключей.
🎭 Сценарий 3: Путаница форматов данных (Data format confusion)
Представим сервис, ожидающий XML, но получающий JSON. Из-за особенностей Go-парсера XML, он может успешно извлечь данные даже из некорректного входного формата:
{
"data": "<action>Allow</action>"
}
🔻 Результат: XML-парсер найдёт тег <action> и примет решение о разрешении доступа, хотя входные данные были некорректными.
🔒 Реальная угроза: Подобная уязвимость была обнаружена в Hashicorp Vault (CVE-2020-16250), что привело к серьёзному обходу авторизации.
🚨 Реальные последствия и примеры
Некоторые реальные уязвимости, возникшие из-за вышеописанных проблем:
- 📌 CVE-2020-16250 (Hashicorp Vault) — обход авторизации при парсинге JSON как XML.
- 📌 CVE-2017-12635 (Apache CouchDB) — обход авторизации из-за несогласованности парсеров JSON.
⚙️ Как защититься?
Несмотря на серьёзность проблемы, Go предоставляет очень ограниченные встроенные механизмы защиты. Но некоторые меры предпринять можно:
- ✅ Использовать встроенные механизмы вроде DisallowUnknownFields() (для JSON) или KnownFields(true) (для YAML).
- ✅ Писать дополнительные проверки на уровне приложений для обнаружения дублирующихся ключей или неправильного регистра.
- ✅ Применять статический анализ кода (Semgrep) для поиска ошибок в тегах структур.
Пример строгой проверки в Go:
decoder := json.NewDecoder(bytes.NewReader(input))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&targetStruct); err != nil {
log.Fatal(err)
}
💡 Личное мнение автора
Как разработчик и специалист по безопасности, могу сказать, что подобные проблемы долгое время не получали должного внимания, несмотря на свою критичность. Описанные атаки демонстрируют, как привычные механизмы, воспринимаемые как надёжные, могут скрывать серьёзные уязвимости.
Самое опасное — отсутствие возможности простого отключения некоторых «небезопасных» настроек. Например, нечувствительность к регистру в JSON-парсере Go нельзя отключить даже при большом желании, что создаёт риск в многокомпонентных системах. Надеюсь, сообщество и разработчики Go будут уделять этой проблеме больше внимания, и JSON v2 появится в обозримом будущем.
До тех пор, ответственность за безопасность ложится на плечи каждого разработчика, использующего Go в критически важных проектах.
📚 Ссылки и дополнительная информация:
Будьте внимательны и не забывайте проверять не только свой код, но и инструменты, на которых он строится. Ведь даже мелочи иногда могут иметь огромные последствия! 🚨