Почему Java выполняет код, спрятанный в комментариях через Unicode?
Краткий ответ
Java обрабатывает Unicode-escape последовательности (например, \u000d) на самом раннем этапе компиляции — до того, как начинается разбор синтаксиса и лексический анализ. Это значит, что \u000d превращается в символ перевода строки (CR — carriage return) ещё до того, как компилятор "увидит" комментарий. В результате строка разбивается на две, и код после \u000d оказывается вне комментария.
Подробное объяснение
Спецификация Java (JLS §3.3) чётко говорит: на первом шаге компиляции все Unicode-escape последовательности вида \uXXXX заменяются на соответствующие символы. Это происходит до лексического анализа, то есть до того, как компилятор понимает, где комментарий, а где код.
Рассмотрим ваш пример пошагово:
// \u000d System.out.println("Hello from comment!");
- Компилятор видит
\u000dи заменяет его на символ перевода строки (U+000D CR). - После замены строка превращается в две строки:
//
System.out.println("Hello from comment!");
Первая строка — это пустой комментарий //, а вторая — уже полноценный код вне комментария, который компилируется и выполняется.
Другие опасные примеры
Такая же проблема возможна с другими управляющими символами:
\u000a— перевод строки (LF)\u000c— прогон страницы (FF)
Пример с \u000a:
// \u000a System.out.println("Unsafe!");
Тоже превратится в выполнимый код.
Как защититься?
- Используйте редакторы с подсветкой Unicode — некоторые IDE (например, IntelliJ IDEA) могут показывать предупреждения о подозрительных Unicode-символах в комментариях.
- Не копируйте код из непроверенных источников — злоумышленники могут спрятать вредоносный код в комментариях через Unicode.
- Используйте статические анализаторы — инструменты вроде SpotBugs или SonarQube могут находить такие "троянские" комментарии.
- Включайте проверку "Unicode escapes in comments" — в некоторых IDE есть настройки для строгой проверки.
Почему это вообще разрешено?
Такое поведение существует по историческим причинам. Unicode-escape последовательности нужны для того, чтобы можно было писать любые символы, даже если файл сохранён в кодировке, не поддерживающей Unicode (например, ASCII). Но побочный эффект — возможность "сломать" комментарии. В современных версиях Java (8+) нет способа отключить это поведение, так как оно закреплено в спецификации языка.
Вывод
Запомните простое правило: Unicode-escape последовательности обрабатываются раньше, чем комментарии. Поэтому никогда не используйте \u000a, \u000d и \u000c внутри комментариев, если не хотите сюрпризов. И будьте осторожны с чужим кодом!
Дополнение: Как это работает в современных IDE и инструментах
Проблема с Unicode в комментариях — это не только теоретический курьёз. В реальных проектах были случаи, когда злоумышленники прятали вредоносный код таким образом. Например, в 2018 году была обнаружена атака через \u000d в open-source проектах.
Современные инструменты для борьбы:
- IntelliJ IDEA — начиная с версии 2019.1, при вставке текста с
\u000dIDE показывает предупреждение. - SpotBugs — детектор
UNICODE_ESCAPE_IN_COMMENT. - Checkstyle — модуль
UnicodeEscapeпозволяет запретить Unicode-экранирование в комментариях.
Если вы пишете код, который будет использоваться другими, рекомендую добавить в ваш CI/CD пайплайн проверку на такие escape-последовательности. Это простое правило безопасности, которое убережёт от многих проблем.
И ещё один совет: если вам нужно вставить в комментарий символ, который не входит в ASCII, используйте настоящий Unicode-символ (например, напрямую вставьте символ ©), а не его escape-последовательность. Это чище и безопаснее.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги