Текстовые редакторы, основная задача которых — отображение моноширинного шрифта (например, кода), должны, как и следует из названия, показывать символы одной ширины.


invisible symbols in diff


Но есть нюанс


В Unicode есть символы, видеть которые не положено. Текстовый редактор может просто отрендерить текст с таким символом, а может предпринять какие-то действия, чтобы сделать его заметным.


Кто же они?


Код Пример Название
U+2060 foo?bar 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 foo?bar ZERO WIDTH SPACE
U+200C foo???bar ZERO WIDTH NON-JOINER
U+200D foo?bar ZERO WIDTH JOINER
U+FEFF foo?bar ZERO WIDTH NO-BREAK SPACE

Word joiner, U+2060

Пришёл на смену zero-width no-break space (U+FEFF), потому что U+FEFF стал использоваться для кодирования BOM (byte-order mark, несколько байт в начале файла, обозначающие его кодировку и порядок байт). Этот символ запрещает перенос строки там, где он встречается.


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

Используется, когда нужно обозначить границу слов, не вставляя пробел. Этот текст будет переноситься по словам:


Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word?Word


А этот нет:


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


Но если его запустить с параметром -A в linux или -v в macOS, то почти все символы видны (спасибо за подсказку в комментариях):


cat -v invisibles.txt 
U+2060 foo?M-^A?bar WORD JOINER
U+2061 foo?M-^A?bar FUNCTION APPLICATION
U+2062 foo?M-^A?bar INVISIBLE TIMES
U+2063 foo?M-^A?bar INVISIBLE SEPARATOR
U+180E foo?M-^Nbar MONGOLIAN VOWEL SEPARATOR
U+200B foo?M-^@M-^Kbar ZERO WIDTH SPACE
U+200C foo?M-^@?M-^@?M-^@M-^Lbar ZERO WIDTH NON-JOINER
U+200D foo?M-^@M-^Mbar ZERO WIDTH JOINER
U+FEFF foo?bar ZERO WIDTH NO-BREAK SPACE

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: молодец, показал всё, ещё и выделил (хотя, на самом деле, сделал это less). Просто прекрасно, для 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, многое рассказано, в том числе и о таких символах
Поделиться с друзьями
-->

Комментарии (42)


  1. WST
    08.10.2016 13:46
    +3

    Вспомнилось, как много лет назад я иногда злоупотреблял такими символами, чтобы побыстрее исчерпать трафик или квоту на объём БД у своей «жертвы». Например, пишу фразу «Привет! Как дела?», а внутри спрятаны тысячи невидимых знаков. Короткое сообщение начинает «весить» достаточно много. Правда, иногда эти символы выдают себя нагрузкой на браузер (или Jabber-клиент) — к примеру, становится «трудно» выделять текст, прокрутка подтормаживает и т.д.


    1. kemko
      08.10.2016 16:15
      +1

      Эффект видимо зависит от понимания жертвой своего jabber-клиента. У меня всегда было включено сжатие, на экономию со школьных обедов не так много можно было купить интернет-трафика.


      1. WST
        08.10.2016 16:19
        +4

        Чаще всего «жертвами» были изрядно досаждавшие попрошайством ботов арабы, у которых нередко были такие слабые телефоны, что на zlib им тупо не хватало памяти (всё ж таки, я о временах J2ME изначально говорил).


        1. kemko
          08.10.2016 16:45
          +1

          Видимо я был мажором. На Siemens CX65 zlib вполне работал.


  1. iloskutov
    08.10.2016 15:59
    +10

    Cat не показывает их

    Он вообще ничего не показывает — он просто выводит содержимое файла на консоль, а она уже отображает его так, как считает нужным. По Вашему же скриншоту видно, что поведение разных терминалов несколько отличается, но cat здесь совершенно ни при чём


    1. JIghtuse
      08.10.2016 19:22
      +2

      Да, стоило его хотя бы с опцией -A запустить — отображает непечатаемое. Не знаю, всё ли — исходников тестов не приложено.


      1. Antelle
        08.10.2016 19:34
        +2

        Исходник вот: https://gist.github.com/antelle/216aa1f34c0b9dee89e574934325d2ce
        Проверил на маке, видно почти всё, только опция в cat под macos называется -v. Под linux не проверял, могу предположить, что будет так же, возможно ещё последний появится.


        output
        cat -v invisibles.txt 
        U+2060 foo?M-^A?bar WORD JOINER
        U+2061 foo?M-^A?bar FUNCTION APPLICATION
        U+2062 foo?M-^A?bar INVISIBLE TIMES
        U+2063 foo?M-^A?bar INVISIBLE SEPARATOR
        U+180E foo?M-^Nbar MONGOLIAN VOWEL SEPARATOR
        U+200B foo?M-^@M-^Kbar ZERO WIDTH SPACE
        U+200C foo?M-^@?M-^@?M-^@M-^Lbar ZERO WIDTH NON-JOINER
        U+200D foo?M-^@M-^Mbar ZERO WIDTH JOINER
        U+FEFF foo?bar ZERO WIDTH NO-BREAK SPACE


  1. Sirikid
    08.10.2016 18:28
    +1

    Очевидно мы должны добавить в Unicode специальный символ для отображения невидимых символов (в случае необходимости).


    1. Antelle
      08.10.2016 18:38
      +1

      Обычно принято использовать или bullet (•), или replacement character (?), или


      квадратик такой


  1. Power
    08.10.2016 21:03
    +7

    А git diff не молодец и он ничего не выделил. Он просто раскрасил строчки целиком и скормил на вход less.


  1. vbif
    08.10.2016 23:54
    +1

    В Википедии до сих пор не могут решить, что делать с неразрывными пробелами в викификаторе: при викификации конструкции типа « » превращаются в неразрывные пробелы, а неразрывные пробелы при последующей викификации превращаются в обычные. Такое поведение автор объясняет тем, что неразрывные пробелы в редакторе не отличить от обычных, и потому надо от них избавляться. Тема поднималась уже не один раз, и все соглашались, что это — очевидная недоработка, что так делать нельзя, но отремонтировать до сих пор никто не берётся.


    1. vbif
      09.10.2016 00:02

      конструкции типа « »


  1. Crandel
    09.10.2016 07:28
    +2

    Arch linux, Emacs через терминал


    Скриншот


  1. equand
    09.10.2016 11:49
    +1

    FreeBSD default ee editor


    U+2060 fooa~A bar WORD JOINER
    U+2061 fooa~A?bar FUNCTION APPLICATION
    U+2062 fooa~A?bar INVISIBLE TIMES
    U+2063 fooa~A?bar INVISIBLE SEPARATOR
    U+180E fooa ~Nbar MONGOLIAN VOWEL SEPARATOR
    U+200B fooa~@~Kbar ZERO WIDTH SPACE
    U+200C fooA~@A~@a~@~Lbar ZERO WIDTH NON-JOINER
    U+200D fooa~@~Mbar ZERO WIDTH JOINER
    U+FEFF fooi»?bar ZERO WIDTH NO-BREAK SPACE
    
    U+0020 foo bar SPACE
    U+00A0 foo bar NO-BREAK SPACE
    U+1680 fooa~Z~@bar OGHAM SPACE MARK
    U+2000 fooa~@~@bar EN QUAD
    U+2001 fooa~@~Abar EM QUAD
    U+2002 fooa~@~Bbar EN SPACE
    U+2003 fooa~@~Cbar EM SPACE
    U+2004 fooa~@~Dbar THREE-PER-EM SPACE
    U+2005 fooa~@~Ebar FOUR-PER-EM SPACE
    U+2006 fooa~@~Fbar SIX-PER-EM SPACE
    U+2007 fooa~@~Gbar FIGURE SPACE
    U+2008 fooa~@~Hbar PUNCTUATION SPACE
    U+2009 fooa~@~Ibar THIN SPACE
    U+200A fooa~@~Jbar HAIR SPACE
    U+202F fooa~@?bar NARROW NO-BREAK SPACE
    U+205F fooa~A~_bar MEDIUM MATHEMATICAL SPACE
    U+3000 fooa~@~@bar IDEOGRAPHIC SPACE
    

    FreeBSD default vi


    U+2060 foo\xe2\x81\xa0bar WORD JOINER
    U+2061 foo\xe2\x81\xa1bar FUNCTION APPLICATION
    U+2062 foo\xe2\x81\xa2bar INVISIBLE TIMES
    U+2063 foo\xe2\x81\xa3bar INVISIBLE SEPARATOR
    U+180E foo\xe1\xa0\x8ebar MONGOLIAN VOWEL SEPARATOR
    U+200B foo\xe2\x80\x8bbar ZERO WIDTH SPACE
    U+200C foo\xc2\x80\xc2\x80\xe2\x80\x8cbar ZERO WIDTH NON-JOINER
    U+200D foo\xe2\x80\x8dbar ZERO WIDTH JOINER
    U+FEFF foo\xef\xbb\xbfbar ZERO WIDTH NO-BREAK SPACE
    
    U+0020 foo bar SPACE
    U+00A0 foo bar NO-BREAK SPACE
    U+1680 foo\xe1\x9a\x80bar OGHAM SPACE MARK
    U+2000 foo\xe2\x80\x80bar EN QUAD
    U+2001 foo\xe2\x80\x81bar EM QUAD
    U+2002 foo\xe2\x80\x82bar EN SPACE
    U+2003 foo\xe2\x80\x83bar EM SPACE
    U+2004 foo\xe2\x80\x84bar THREE-PER-EM SPACE
    U+2005 foo\xe2\x80\x85bar FOUR-PER-EM SPACE
    U+2006 foo\xe2\x80\x86bar SIX-PER-EM SPACE
    U+2007 foo\xe2\x80\x87bar FIGURE SPACE
    U+2008 foo\xe2\x80\x88bar PUNCTUATION SPACE
    U+2009 foo\xe2\x80\x89bar THIN SPACE
    U+200A foo\xe2\x80\x8abar HAIR SPACE
    U+202F foo\xe2\x80\xafbar NARROW NO-BREAK SPACE
    U+205F foo\xe2\x81\x9fbar MEDIUM MATHEMATICAL SPACE
    U+3000 foo\xe3\x80\x80bar IDEOGRAPHIC SPACE
    


  1. vadimr
    09.10.2016 12:57
    +4

    Интересно, сколько проблем возникает из-за невероятно актуальной предоставленной программистам возможности именовать объекты на древнемонгольском. При том, что сами монголы давно перешли на кириллицу.


    1. a-motion
      10.10.2016 09:09
      +1

      Полагаю, что существуют люди, которые изучают древнемонгольский, и эти люди, в целом, ничуть не хуже нас с вами. Я лично около двадцати пяти лет назад немало натерпелся от бородатых программистов, которые рассуждали так: «интересно, сколько проблем может возникнуть из-за невероятно актуальной предоставленной программистам возможности писать по-русски, при том, что русские отлично способны использовать транслит».


      Проблемы криворуких программистов, которым в 2016 году в диковинку существование таких сущностей — должны оставаться проблемами [отныне безработных] программистов.


      1. vadimr
        10.10.2016 09:28
        +2

        Изучение древнемонгольского каким-то образом побуждает именовать на этом языке переменные в программах?


        1. a-motion
          10.10.2016 09:55
          -3

          Изучение древнемонгольского побуждает алкать возможности вводить такие символы.


          Здравый смысл побуждает не использовать конструкцию if valid(symbol) then ... else ... в парсерах. Потому что не автору парсера решать, что мне втемяшится использовать в качестве идентификатора, это не его собачье дело (если он, конечно, достаточно квалифицирован.)


          Я так именую методы, которые пишут некий «особенный» внутренний лог: ? и ?, и я не стану пользоваться говноязыком, который мне это сделать не позволит.


          1. vadimr
            10.10.2016 10:00
            +3

            Забавно Вам будет отлаживать Вашу программу с какого-нибудь удалённого терминала, не поддерживающего юникодный ввод.


            1. a-motion
              10.10.2016 10:33
              -3

              Забавно Вам будет отлаживать Вашу программу с какого-нибудь удалённого терминала,
              не поддерживающего юникодный ввод.

              Здесь смешно все: и наличие в наше время терминалов, не поддерживающих юникод, и отладка с удаленного терминала. Оглядитесь, тут вокруг уже XXI век наступил, причем — больше пятнадцати лет тому назад. Ну и да, я умею настраивать деплой так, чтобы ржать в голос над словами «отладка с удаленного терминала».


  1. Alex_kk
    09.10.2016 18:25
    +1

    Статья полезна тем, что показывает, что есть неочевидное там, где, казалось бы, все очевидно.
    А что касается практического использования Unicode в разработке, то следует помнить о «нестандартных» «пробельных» символах (см., напр., \u00A0, \u0082, \u0083, \u0085) и о символах управления потоком (RTL/LTR; см., напр., \u202A \u202B \u202C \u202D \u202E). На опыте проверено, что такие символы могут встречаться в пользовательском вводе (и, что удивительно, в системах электронного документооборота в России!). За идентификаторами в программах мы, программисты, уж как нибудь уследим. :) А вот за пользователем…

    И, разумеется, следует помнить об эквивалентности графических изображений, возникающих из различных комбинаций кодов Unicode (Unicode Equivalence).


  1. hdfan2
    10.10.2016 09:23
    +4

    Вспомнилось. На приёме у логопеда:
    — Доктор, у меня проблема. Я не выговариваю букву «...»!
    — Какую?
    — НУ, БУКВУ «...»!!!


  1. Unk
    10.10.2016 17:08
    +4

    Я решил довести идею скрытых символов до абсурда возможности применения и написал невидимый кодировщик


  1. stychos
    11.10.2016 12:31
    +1

    Реквестирую название указанной в топике клавиатуры.


    1. hdfan2
      11.10.2016 12:39

      1. stychos
        11.10.2016 12:43

        Спасибо! А не существует клавиатур для типографики — с полным набором непечатаемых символов?


        1. hdfan2
          12.10.2016 06:51

          Увы, не подскажу. Я эту раскладку в гугле нашёл.


    1. Antelle
      11.10.2016 12:39

      Вот отсюда: https://en.wikipedia.org/wiki/File:German-T2-Keyboard-Prototype-May-2012.jpg
      Скорее всего, типографские символы — гравировка просто.


      1. grossws
        11.10.2016 12:47

        Они через alt gr набираются, если ОС поддерживает. Т. е. в обычном режиме там используется три функции на клавишу.


        1. Antelle
          11.10.2016 12:50

          Набираются, но кажется, stychos интересуется, где взять клавиатуру, чтобы нарисованы именно были.


          1. stychos
            11.10.2016 12:52

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


            1. Antelle
              11.10.2016 12:54

              Закажите гравировку или печать, оно недорого стоит, могут что угодно нанести.


              1. stychos
                11.10.2016 12:56

                Я практически в третьем мире живу, тут не так просто сделать качественную гравировку или печать чего-либо кроме самоклеек. Хотя, наверно, китайцы сейчас могут делать штучные заказы и на эту тему — надо будет попробовать.


        1. stychos
          11.10.2016 12:54

          В macOS выбор богат для AltGr, но всё равно, далеко не все. Иногда бывает необходимо, например, часто вставлять символ «Indent to here».


          1. a-motion
            11.10.2016 12:57

            В macOS выбор богат для AltGr

            Бгг. Вот моя раскладка: https://github.com/mudasobwa/huya-xkb


            Кроме того, даже в MacOS можно сделать свою какую-угодно раскладку, просто, как и все остальное, немного неестественным образом.


            1. stychos
              11.10.2016 12:59

              Почему неестественным? Есть очень даже удобная утилитка для этого. Я просто думал, может есть готовые best practices для типографики, отлитые в опыте и железе :)

              А раскладка знатная!


              1. a-motion
                11.10.2016 13:03

                Неестественным — потому, что когда мне нужен внезапно на полчаса какой-нибудь нестандартный символ, я редактирую текстовый файл и обновляю кэш: клавиша включена. Я посмотрю, как вы утилиткой себе на часик при редактировании презентации какой-нибудь впилите нестандартный буллит.


                1. vbif
                  11.10.2016 13:14

                  Интересно, никто ещё не придумал удобных менеджеров буфера обмена, чтобы можно было хранить одновременно несколько фрагментов, и при необходимости вставлять нужный по хоткею? Помню, что-то подобное пытались в msoffice сделать.


                  1. a-motion
                    11.10.2016 13:23
                    +1

                    Как бы в Linux из коробки, в неестественных OS — не знаю, но думаю тоже кто-нибудь запилил.


                1. stychos
                  11.10.2016 14:20
                  +1

                  В макоси также можно отредактировать на полчасика файл с ремапами, да и раскладку тоже можно ручками в текстовом редакторе отредактировать, не вижу особых отличий xml от Вашего конфига. Совсем ленивые пользуются Ukelele, вроде тоже не создаёт сложностей.


    1. grossws
      11.10.2016 12:46

      del


  1. hdfan2
    11.10.2016 12:38

    del