Текст, якого немає

Текстові редактори, основне завдання яких — відображення гарнітура шрифту (наприклад, коду), повинні, як і випливає з назви, показувати символи однієї ширини.
invisible symbols in diff
Але є нюанс
В Unicode є символи, бачити які не годиться.
Текстовий редактор може просто отрендерить текст з таким символом, а може зробити якісь дії, щоб зробити його помітним.
Хто ж вони?












Код Приклад Заголовок U+2060 foobar WORD JOINER U+2061 foo⁡bar FUNCTION APPLICATION U+2062 foo⁢bar INVISIBLE TIMES U+2063 foo⁣bar INVISIBLE SEPARATOR U+180E foo᠎bar MONGOLIAN VOWEL SEPARATOR U+200B foobar ZERO WIDTH SPACE U+200C foo€€bar ZERO WIDTH NON-JOINER U+200D foobar ZERO WIDTH JOINER U+FEFF foobar ZERO WIDTH NO-BREAK SPACE
Word joiner, U+2060
Прийшов на зміну zero-width no-break space (U+FEFF), тому що U+FEFF став використовуватися для кодування BOM (byte-order mask, кілька байт на початку файлу, що позначають його кодування і порядок байт). Цей символ забороняє перенос рядка там, де він зустрічається.
Zero-width no-break space, U+FEFF
Застарілий символ, замінений на word joiner, використовувався в тих же цілях.
Zero-width joiner, U+200D
Використовується в індійських і арабських шрифти для об'єднання символів, які без нього були б з'єднані.
Zero-width non-joiner, U+200C
У накресленнях з лігатурами можна вставити його між літерами, щоб лігатури не було:
zero-width non-joiner
Він зустрічається навіть на клавіатурах:
key
Zero-width space, U+200B
Використовується, коли потрібно позначити кордон слів, не вставляючи пробіл. Цей текст буде переноситися за словами:
WordWordWordWordWordWordWordWordwordwordwordwordwordwordwordwordwordwordwordwordwordword
А цей ні:
WordWordWordWordWordWordWordWordwordwordwordwordwordwordwordwordwordwordwordwordwordword
Invisible Operators: function application U+2061, invisible times U+2062, invisible separator U+2063
"Невидимі оператори", додані в Unicode 3.2. Потрібні для позначення математичних операцій у виразах.
Наприклад, ця запис: Aij
Може означати або індекс (i, j) в двовимірному масиві, або індекс i*j в одновимірному. Для усунення неоднозначності можна використовувати або Invisible times, або Invisible separator, щоб було зрозуміло, що малося на увазі.
Аналогчино, f (x + y), це або множення, або функція.
Візуально вони не повинні відрізнятися, але деякі парсери зможуть зрозуміти, що малося на увазі.
Mongolian vowel separator, U+180E
З назви зрозуміло, для чого він. Цей символ вже не раз викликав проблеми. Дуже добре описаний в це відповіді.
Як це виглядає
Звичайно ж, відображення залежить не тільки від редактора, але ще і від шрифту, подивимося на рендеринг тексту, не змінюючи налаштувань редакторів.
Atom, Sublime, VSCode, Xamarin Studio, XCode, Notepad++:
invisibles in text editors
Cat не показує їх:
invisibles in cat
Vim теж не повідомляє про деякі символи, навіть з включеною налаштуванням set list, а ось less справляється краще:
invisibles in terminal
Web
GitHub, ось так показуються ці символи в pull request-ах і diff-ах:
invisibles in github
Один з популярних редакторів коду, CodeMirror:
invisibles in codemirror
У тому ж CodeMirror, використовуваному jsbin, в IE частина символів видно:
invisibles in codemirror
ACE здогадується, що там бяка, і говорить, що щось тут нечисто", але от що саме — не завжди показує:
invisibles in ace
Редактори коду і diff tools
Редактори на платформі IntelliJ:
invisibles in IntelliJ
Різні інструменти порівняння коду під macOS (P4Merge, FileMerge, KDiff3):
invisibles in diff
KDiff3, спроба зарахована, але цього не достатньо.
SourceTree: не обробляє текст взагалі ніяк, погано:
invisibles in SourceTree
Tortoise, теж майже нічого:
invisibles in diff
git diff
: молодець, показав усе, ще й виділив. Просто чудово, для diff tools це зразок для наслідування:
invisibles in git diff
Anguish: brainfuck, якого немає
Хтось зробив мова програмування Anguish, що використовує тільки невидимі символи.
Він заснований на brainfuck, але використовує не знаки пунктуації, а символи, про які ми говорили вище.
Є навіть інтерпретатор на Perl і приклади використання.
Експлуатація
Поганий код, фу таким бути, зробити закладку можна зовсім просто:
function f() {
// ну ви зрозуміли, на що замінити
return 'access_denined';
}
let code = f();
if (code === 'access_denied') {
return 401;
}

Що робити
Пиши чистий код, %username%. Йди best practices, їх придумали не просто так, а для того, щоб тримати менше речей в голові, у тому числі своєчасно помічаючи такі штуки. Побачив магічну сходинку, дивний або непроверяемый default case, ще щось: є час — не полінуйся, перепиши як треба. Проводь код-рев'ю, дивись що коммитят в твою ріпу, підтримуй гарне покриття. Пам'ятай, що рядку може бути не тільки те, що видно на екрані, перевір у hex-редакторі, якщо виникла підозра.
Взагалі, ймовірність реалізації бекдор через невидимий символ, звичайно, є, але швидше ні, ніж так: знайти його досить просто, а вставити закладку в говнокод можна й іншими методами.
Почитати
  • Unicode Demystified, A Practical programmer's Guide to the Encoding Standard, by Richard Gillam (ви знаєте, де шукати) — хороша книга про unicode, багато розказано, в тому числі і про таких символах
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.