Здесь вы можете скрыть текст в картинку, и никто об этом не узнает.
Что все это значит? Что здесь происходит? Как это работает? Бегом читать текст внизу страницы, там я все объяснил.
Здесь будет загруженное изображение
Здесь будет информация об изображении
Найденный текст:
Введите текст, который хотите скрыть
Текст занимает 0 байт из 0 возможных.
Получившееся изображение
Здесь будет получившееся изображение
Скрытый текст
Передавайте получившееся изображение, КАК ФАЙЛ. Многие сервисы (мессенджеры, соц.сети) сжимают изображения, меняют расширение - ваши данные будут утеряны.
Стеганография - это способ скрыть одну информацию внутри другой так, чтобы никто об этом не узнал. В нашем случае будем скрывать текст внутри картинки.
То есть, на выходе вы получите картинку с "вшитым" в нее текстом. Сам текст, конечно, видно не будет, картинка тоже визуально не изменится. Никто и не догадается, что в этой картинке спрятан какой-то текст. Для просмотра скрытого текста нужно будет снова загрузить картинку на сайт, он сам ее проанализирует и отобразит спрятанный текст.
ВАЖНО: Представленный инструмент не является стегонаграфически-устойчивым, он не подходит для сокрытия критически важной информации, поскольку любой специалист по стеганографии очень быстро разгадает эту стеганограмму. Используйте его для бытовых и прочих целей, не предполагающих серьезной атаки на ваш шифр.
Теперь поступим так. Сначала я коротко опишу, как этим пользоваться, затем отвечу на самые основные вопросы, и далее, для самых любознательных полностью распишу, как это все работает.
Указатели по разделам (кликабельны)
Инструкция по использованию сервиса
Ответы на вопросы
Теория
Как спрятать текст в картинку:
Изображение ОБЯЗАТЕЛЬНО должно быть в PNG формате, иметь соответствующее расширение и корректно открываться. В общем, это должна быть нормальная, обычная картинка. Да, изображение должно обладать непрозрачными областями, информация будет записана в них. Если картинка полностью прозрачна (хотя бы чуть-чуть), она загрузится, но текст в нее не запишется (вы увидите, что свободного места для записи информации 0 байт).
Обратите внимание на максимально допустимый размер сообщения и счетчик длины, он считает не количество букв, а байты, которые занимают введенные символы, если превысите это значение, то текст не запишется.
После загрузки текста, в появившейся ниже форме вы увидите получившееся изображение и продублированный текст, который в нем скрыт. Чтобы проверить, все ли прошло успешно, загрузите изображение на сайт повторно, оно будет просканировано, и вы увидите результат.
Как увидеть спрятанный текст:
Прежде чем пользоваться, потренеруйтесь на случайных данных, посмотрите, как все работает.
Что еще важно знать:
Ответы на ваши вопросы:
Вопрос: Картинка не загружается. Что делать?
Ответ: Делайте вот что:
Вопрос: Картинка загрузилась, но текст не записывается. Что делать?
Ответ: Тут примерно то же самое:
Вопрос: Картинка загрузилась, но места под текст нет или слишком мало. Почему так?
Ответ: Место под текст зависит от количества непрозрачных пикселей. Даже незначительно изменение прозрачности делает пиксель непригодным для записи. Проверьте в графическом редакторе прозрачность изображения. Попробуйте другое изображение (можете создать сами тестовую картинку, которая точно не будет прозрачной).
Вопрос: Прозрачности вообще не должно быть?
Ответ: Прозрачные места могут быть. Но чем их больше, тем меньше текста вы сможете записать.
Вопрос: В чем проблема прозрачных пикселей?
Ответ: Если коротко, браузеры и редакторы могут принудительно изменять значение прозрачности, что приведет к потере данных. Причем, это происходит даже когда место, куда записывается информация, хотя бы чуть-чуть прозрачно. Теоретически, я мог бы придумать локальное решение для себя, но это порождает дополнительные неизвестные мне сценарии возникновения ошибок у вас, так что вы не хотите прозрачности. Прозрачность пропускаем.
Вопрос: Можно ли редактировать получившееся изображение?
Ответ: Теоретически, да. Ваш текст записывается последовательно по пикселям, начиная из левого верхнего угла картинки построчно слева-направо. Используются только непрозрачные пиксели. Поэтому, если текст небольшой, вы можете изменить или даже обрезать низ изображения, при этом данные сохранятся. Так же вы можете обрезать верх, если он прозрачен слева-направо.
Но обратите внимание вот на что. Алгоритм записывает в картинку длину вашего сообщения и читает ровно столько пикселей, сколько нужно для получения вашего текста. Данные не только записываются в непрозрачные пиксели последовательно слева-направо, но и читаются так же. Это значит, что на пути записи все прозрачные пиксели были пропущены. И если вы сделаете их непрозрачными, то алгоритм при чтении будет читать их вместо тех, в которые записана ваша информация. Таким образом как минимум часть вашего сообщения будет утеряна.
Вопрос: В картинку был записан текст, но при загрузке он не читается. Что делать?
Ответ: Если до этого вы тестировали, и все работало хорошо, то проблема скорее всего в том, что изображение было отредактировано, и данные стерлись. Вспомните, не пересылали ли вы эту картинку через какой-нибудь мессенджер или соц.сеть, как фото. Либо, возможно, вы открывали ее в каком-то редакторе и случайно изменили.
Если вы пробуете прочитать изображение не с того устройства, на котором оно было создано, попробуйте использовать другой браузер.
Вопрос: Можно ли "прочитать" полученную картинку в других сервисах?
Ответ: Теоретически, если он позволяет реализовать тот же алгоритм и может просто вывести все данные без учета тегов, то это возможно. Но если сервис работает со своими тегами и схемами записи, то сделать это не получится. Так что, зависит от сервиса, но скорее всего нет.
Однако, помните, что моя схема непригодна для шифрования критически важных данных, и специалист сможет без особого труда извлечь ваше сообщение.
Вопрос: Можно ли здесь "прочитать" картинку из других сервисов по стеганографии?
Ответ: Нет. Я использую свой тег для записи, который вряд ли используют они. Изображения, не маркированные моим тегом, сделанные не по моему формату, прочитать не получится.
Вопрос: Можно ли записать несколько независимых текстов в одну картинку?
Ответ: Можно. Данные записываются в пиксели построчно слева-направо, начиная с верхнего левого угла картинки. Таким образом, если сохраняемый текст умещается в одну строку и не доходит до края, вы можете повернуть картинку другим боком и записать туда еще что-нибудь. Представьте, что изображение - это лист бумаги, на котором вы что-то пишете. Таким образом в картинку можно записать несколько текстов и проявлять их в зависимости от ориентации изображения.
Вопрос: Сколько информации можно скрыть в картинке?
Ответ: Зависит от размера картинки. Формула, по которой рассчитываются свободные байты такая: ((высота*ширину - прозрачные_пиксели) * 3 / 8) - 11. К счастью, скрипт считает все за вас, после загрузки картинки вы увидите результат. Но чисто для примера возьмем упрощенную формулу. Предположим, что прозрачных пикселей нет, заголовок не учитываем. Тогда формула: высота*ширину*3 / 8.
Если у нас картинка размером 1000*1000 пикселей, получается 1000*1000*3/8 = 3000000/8 = 375000 байт.
1 символ не всегда занимает 1 байт. Но латиница занимает 1 байт, поэтому если мы пишем только английскими буквами, то количество байт - это количество букв. Вообще, все, что есть в ASCII - это 1 байт, если интересно просто найдите в интернете таблицу символов. Если используем кириллицу, то делите на 2 (русская буква занимает 2 байта, такая вот дискриминация), если используете всякие эмодзи и прочие неформатные символы, то там можно и байта 3-4 потратить.
Объем может казаться внушительным, но учтите, что предполагается возможность спрятать криптографический шифр, а он длиннее незашифрованного текста.
Вопрос: Почему текст измеряется в байтах, а не в символах?
Ответ: Потому что в конечном итоге мы прячем не символы, а байты символов, 1 символ не вегда занимает 1 байт. Можно было бы примерно перевести это в символы, но тогда пришлось бы брать по максимальной планке (ну вдруг пользователь решит засунуть сюда только все самые большие символы), и допустимый размер текста был бы сокращен в несколько раз, при этом полезный объем кратнки в большинстве случаев оставался бы нереализованным.
Вопрос: Где происходит весь процесс? Мои данные отправляются на сервер? Видит ли админ мои тексты и картинки?
Ответ: Весь процесс происходит у вас в браузере, код написан на js. Можете сохранить эту страницу на устройстве и работать с ней оффлайн.
Если у вас есть еще какие-то вопросы, предложения или может быть что-то сломалось и не работает - можете писать вот сюда.
А теперь давайте поговорим о том, как это все устроено.
Мы выяснили, что стеганография - это про сокрытие одной информации внутри другой. Ключевое отличие от криптографии здесь в том, что, столкнувшись с криптографическим шифром, вы скорее всего сразу поймете, что это шифр. Когда вы видите стеганограмму, вы видите нечто другое, вы, вероятно, даже не понимаете, что в этом объекте может быть что-то скрыто. И это самая привлекательная сторона: хочешь что-то спрятать - положи на самом видном месте.
Впрочем, стеганографией пользуются далеко не только шпионы и хакеры. Ее применяют, например, для защиты авторских прав, когда цифровой продукт помечается невидимым тегом.
Но как технически выглядит стеганограмма?
Давайте обратимся к такому примеру:
Первое, что я собираюсь сделать,
разобравшись с делами, это сесть
изучать стеганографию. Не терпится,
ведь это очень интересная и,
естественно, полезная
тема для освоения.
Что это такое? Для случайного человека это выглядит просто, как заметка, написанная кем-то, по видимому, очень увлеченным стеганографией. Но мы-то с вами не случайные люди. Человек, написавший эту заметку был мало того, что увлечен стеганографией, он еще и оставил нам скрытое послание. Прочитайте первые буквы каждой строки.
В современном мире под стеганографией чаще понимается скрытие электронной информации в файлах, однако принцип остается таким же. Когда я говорю "в файлах", я имею ввиду буквально в коде документа. Самой важной деталью здесь является сохранение всех первоначальных свойств файла, в котором информация скрывается. Недостаточно просто открыть байт-код и вписать туда свои данные, нужно сделать это в правильном месте и правильным образом, чтобы внешне файл не казался подозрительным и продолжил корректно работать.
Этим мы и будем сейчас заниматься.
Сложно представить себе лучший объект для скрытия данных, чем картинка. Оговоримся, что речь пойдет о растровых изображениях, прочие трогать не будем. Главный плюс изображений заключается в том, что большую часть файла занимают пиксели, код которых можно менять, как угодно - картинка все равно откроется. Главный минус - внесение больших изменений обязательно скажется визуально, со стороны будет заметно, что с картинкой что-то не так. Наша задача - найти правильный баланс.
Изображения бывают разных форматов. На ум сразу приходят PNG и JPEG, GIF... Можно и продолжить, но остановимся на PNG.
Почему PNG? Во-первых: он распространен и всем известен. Во-вторых: он достаточно стабилен. Тот же JPEG постоянно пережимается, куда бы вы его ни загружали, так что с ним банально проще потерять данные.
В примере с текстом, который я приводил выше, была допущена ключевая неточность. Дело в том, что все нужные буквы, требуемые для передачи сообщения, уже содержались в строке, ее просто нужно было разделить. С картинкой так не будет. Нужных данных там еще нет, нам необходимо вписать их самостоятельно.
Работать мы, конечно, будем не с целыми буквами, а с байтами, точнее даже с битами. Но давайте по-порядку.
Как устроено изображение? Если убрать мета-данные, оставив только саму картинку, мы получим массив байт, отвечающих за цветовые каналы пикселей. И это важный момент. Как вы знаете, пиксель состоит из нескольких цветов, которые при различной конфигурации создают тот или иной оттенок.
Всего есть 4 цветовых канала:
В массиве байт это будет выглядеть вот так:
[R, G, B, A, R, G, B, A, R, G, B, A, ...]
Важность здесь в том, что мы получаем не массив пикселей, а массив каналов. Каждый канал занимает 1 байт, соответственно, пиксель занимает 4 байта. Каждый [R, G, B, A] - это пиксель.
Менять байты целиком мы не будем. Мы могли бы, но это сразу же станет заметно. Будем работать с битами.
Байт состоит из 8 бит. Используется двоичная система, те самый нули и единицы. Вот как это примерно выглядит:
01010011
Этот байт в разных контекстах может интерпретироваться по-разому, например, в текстовом редакторе это будет буква заглавная буква S. Но в контексте цветового канала байт всегда интерпретируется, как число. Впрочем, это и есть число, записанное в двоичной системе. И в нашей привычной десятиричной системе это число 83.
Как видите, количество разрядов в нашем двоичном числе ограничено. Но насколько большим оно может быть? Давайте посчитаем. Каждый бит может принимать только 2 значения, и всего у нас 8 бит, максимальное количество разных комбинаций составляет 2^8=256. Иными словами, если мы пойдем от нуля, то вот что получим:
00000000 = 0
00000001 = 1
00000010 = 2
00000011 = 2
00000100 = 4
...
11111111 = 255
Если вы хоть раз подбирали цвета в графическом редакторе, то должны были обратить внимание, что настройка интенсивности цветовых каналов обычно предполагает выбрать значение от 0 до 255, вот это оно и есть.
Помните массив цветовых каналов, который я приводил выше?
[R, G, B, A, R, G, B, A, R, G, B, A, ...]
Теперь для нас он будет выглядет примерно так:
[10001011, 01010010, 11001010, 0001010, ...]
С этим разобрались. Давайте уже, наконец, что-нибудь спрячем. Застеганографируем.
Предлагаю спрятать букву S. Ее код нам уже известен, и мне не придется лезть смотреть другие буквы.
Как будем прятать? Мы уже выяснили, что целиком в канал ее засовывать - это плохая идея, цвет пикселя сильно поменяется. Да, внесение изменения в любом случае отразится на цвете, но нам бы сделать так, чтобы это было максимально незаметно.
Не буду нагнетать интригу. Метод, которым мы воспользуемся, называется LSB - Least Significant Bit - это наверно наиболее распространенный метод цифровой стеганографии. Он заключается в замене младшего бита каждого байта на бит скрываемого сообщения. Сейчас объясню.
Младший бит - это крайний правый бит нашего байта:
10001011
Почему именно он? Потому что его изменение минимально отражается на значении цветового канала.
10001011 = 139
10001010 = 138
Такое изменение человеческий глаз не заметит, даже если мы забьем данными всю картинку.
Но есть нюанс. В нашей реализации мы не будем трогать альфа-канал.
Начать надо с того, что это самое чувствительное место изображения. Но дело даже не в этом.
Я уже описывал в начальном разделе проблему прозрачных пикселей. В собственной реализации я столкнулся с тем, что браузер теряет данные, записанные в пиксели, альфа-канал которых имеет значение отличное от 255. Да, это касается данных, записанных и в другие каналы этого пикселя. Насколько я знаю, разные браузеры и редакторы могут менять значение альфа-канала, но точный механизм мне неизвестен. С этим можно было бы разобраться, но оно как будто лишено практического смысла, потому что это выглядит, как проблема, решение которой либо трудозатратно несоразмерно результату, либо сложно контролировать на глобальном уровне. Целесообразнее отказаться от записи в такие пиксели вообще.
Но хватит о плохом. Давайте уже стеганографировать.
Итак, буква S - 01010011. Записываем в R, G и B каналы, меняя младший бит каждого из них на следущий бит нашей буквы. Давайте для простоты представим, что наша картинка состоит из 3-х пикселей, нам как раз этого хватит.
Вот наши каналы (альфа-канал = 255, как мы договаривались):
[10001011, 01010010, 11001010, 11111111,
01100101, 11100111, 11001010, 11111111,
10010111, 00011010, 01011001, 11111111]
Пишем от старшего бита к младшему (слева-направо):
10001011 - пишем 0 -> 10001010
01010010 - пишем 1 -> 01010011
11001010 - пишем 0 -> 11001010
11111111 - это альфа-канал, его пропускаем
01100101 - пишем 1 -> 01100101
11100111 - пишем 0 -> 11100110
11001010 - пишем 0 -> 11001010
11111111 - снова альфа-канал
10010111 - пишем 1 -> 10010111
00011010 - пишем 1 -> 00011011
01011001 - этот канал уже не меняем
11111111 - снова альфа-канал, но нам уже все равно
Как видите, не везде значение бита поменялось. Поскольку бит может принимать только 2 значения: 0 и 1, в половине случаев мы можем ожидать, что состояние канала не изменится вообще.
Получившийся массив байт:
[10001010, 01010011, 11001010, 11111111,
01100101, 11100110, 11001010, 11111111,
10010111, 00011011, 01011001, 11111111]
Сохраняем результат, стеганограмма готова. Теперь чтобы прочитать скрытое сообщение, мы должны снова разобрать изображение на байты и в таком же порядке собрать нашу информацию. Этот процесс уже объяснять не буду, он понятен по аналогии.
Да, это еще не все. На практике нам нужно придумать какой-то опознавательный знак, ведь картинок может быть много, система должна сразу понимать, есть ли в изображении сообщение, чтобы не морочить нам голову потоком бессвязных символов. И еще бы добавить указание длины нашего сообщения, ведь обычно в картинке много байт, нам нет необходимости выводить их все.
В своей реализации перед сообщением я добавляю тег UNVSTEG и далее количество байт в сообщении. В нашем случае конечное сообщение бы выглядело так: UNVSTEG1S
Таким образом, мы читаем первые несколько байт, смотрим, совпадают ли они с тегом. Далее, при записи я резервирую 4 байта под длину вводимого текста. 4 байта хватит, чтобы записать число от 0 до 4.294.967.295 в десятиричной системе. Вряд ли вы столько напишете или найдете картинку, которая столько вместит, но пусть будет с запасом. В конце-концов, байты-то ваши, а не мои, что мне их экономить?
Получается: 7 байт - тег, еще 4 байта на запись длины, и того 11 байт мы съедаем технической информацией. Не так уж и много.
А может ли быть такое, что первые извлеченные байты совершенно случайно совпадут с байтами тега, при этом в картинке не будет информации, просто нули и единицы так сложились?
Теоретичеки такое возможно. В этом случае вы просто получите какой-то набор символов, и будете долго думать над глубоким смыслом, который в нем скрыт. Остроты вашим переживаниям добавит еще и то обстоятельство, что некоторые биты сложатся в целые буквы, а буквы даже могут сложиться в слова.
Но не спешите бежать проверять разные картинки. Вероятность такого исхода равна 1 к 72.057.594.037.927.936. С другой стороны, если оно все же сложилось, то можете смело закладывать квартиру и идти в казино ставить все на зеро.
Но, кроме шуток, если ваш тег состоит из 1 буквы (1 байта), то такая вероятность уже 1 к 256, что выглядит, как то, что рано или поздно обязательно произойдет. Так что делайте тег разумной длины.
Итак, что мы имеем. В 1 пиксель мы можем записать 3 бита информации (в R, G и B каналы, A не трогаем), но считать в битах мы уж не будем, это как-то совсем круто. Переведем в байты. 1 байт = 8 бит. Формула для рассчета места в картинке будет выглядеть следующим образом:
(высота*ширину - прозрачные_пиксели)*3 / 8 - 11
Ну или если совсем упростить: пиксели*3 / 8 - 11
Что можно улучшить в такой схеме?
Ну, во-первых: можно зашифровать начальные данные. Так в вашей картинке будет не просто текст, а шифр. И даже если кто-то обнаружит ваши данные в картинке, он должен будет их еще и расшифровать. Да, зашифровать можно в моем шифраторе, вот здесь.
Во-вторых: можно записывать данные не вот так подряд, а придумать какую-то особую последовательность или даже ключ, который будет содержать инструкцию о том, как проводить запись. Например, пропускать разное количество пикселей между записью разных байт, или вообще распределять записываемый текст по всему изображению в соответствии с определенным паттерном.
Можно пойти и еще дальше, и записывать не просто разные байты, а разные биты хаотично в разных местах.
В общем, придумать можно много всего. Но я в данной реализации не стал этот момент усложнять. С другой стороны, если будет свободное время и желание, то может еще вернусь к этому. Ну либо если будет интерес к теме и увижу реальный запрос на такое. Так что если вам интересно, можете писать вот сюда свои пожелания.
Ну а на этом все. Надеюсь, было интересно.
К содержанию