Текст, которого нет

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


    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 foobar 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 foobar 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, многое рассказано, в том числе и о таких символах
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 42

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

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

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

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

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

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

          +2

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

            +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 foobar ZERO WIDTH NO-BREAK SPACE

            Надо обновить пост, спасибо.

        • UFO just landed and posted this here
            +1

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


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

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

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

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


                Скриншот
                  +1

                  FreeBSD default ee editor


                  U+2060 fooâ~A bar WORD JOINER
                  U+2061 fooâ~A¡bar FUNCTION APPLICATION
                  U+2062 fooâ~A¢bar INVISIBLE TIMES
                  U+2063 fooâ~A£bar INVISIBLE SEPARATOR
                  U+180E fooá ~Nbar MONGOLIAN VOWEL SEPARATOR
                  U+200B fooâ~@~Kbar ZERO WIDTH SPACE
                  U+200C fooÂ~@Â~@â~@~Lbar ZERO WIDTH NON-JOINER
                  U+200D fooâ~@~Mbar ZERO WIDTH JOINER
                  U+FEFF foobar ZERO WIDTH NO-BREAK SPACE
                  
                  U+0020 foo bar SPACE
                  U+00A0 foo bar NO-BREAK SPACE
                  U+1680 fooá~Z~@bar OGHAM SPACE MARK
                  U+2000 fooâ~@~@bar EN QUAD
                  U+2001 fooâ~@~Abar EM QUAD
                  U+2002 fooâ~@~Bbar EN SPACE
                  U+2003 fooâ~@~Cbar EM SPACE
                  U+2004 fooâ~@~Dbar THREE-PER-EM SPACE
                  U+2005 fooâ~@~Ebar FOUR-PER-EM SPACE
                  U+2006 fooâ~@~Fbar SIX-PER-EM SPACE
                  U+2007 fooâ~@~Gbar FIGURE SPACE
                  U+2008 fooâ~@~Hbar PUNCTUATION SPACE
                  U+2009 fooâ~@~Ibar THIN SPACE
                  U+200A fooâ~@~Jbar HAIR SPACE
                  U+202F fooâ~@¯bar NARROW NO-BREAK SPACE
                  U+205F fooâ~A~_bar MEDIUM MATHEMATICAL SPACE
                  U+3000 fooã~@~@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
                  
                    +4
                    Интересно, сколько проблем возникает из-за невероятно актуальной предоставленной программистам возможности именовать объекты на древнемонгольском. При том, что сами монголы давно перешли на кириллицу.
                      +1

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


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

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

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


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


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

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

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

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

                      И, разумеется, следует помнить об эквивалентности графических изображений, возникающих из различных комбинаций кодов Unicode (Unicode Equivalence).
                        +4
                        Вспомнилось. На приёме у логопеда:
                        — Доктор, у меня проблема. Я не выговариваю букву «...»!
                        — Какую?
                        — НУ, БУКВУ «...»!!!
                          +4
                          Я решил довести идею скрытых символов до абсурда возможности применения и написал невидимый кодировщик
                          • UFO just landed and posted this here
                              0
                              • UFO just landed and posted this here
                                  0
                                  Увы, не подскажу. Я эту раскладку в гугле нашёл.
                                0

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

                                  0

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

                                    0

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

                                    • UFO just landed and posted this here
                                        0

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

                                        • UFO just landed and posted this here
                                    • UFO just landed and posted this here
                                        0
                                        В macOS выбор богат для AltGr

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


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

                                        • UFO just landed and posted this here
                                            0

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

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

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

                                              • UFO just landed and posted this here
                                      0

                                      del

                                      0
                                      del

                                      Only users with full accounts can post comments. Log in, please.