Sneak a secret message inside a string
Have you never received an email from a stranger that seemed to know lots of things about you and wanted to recruit you (or sell something)?
Have you ever wanted to know where the hell that guy found your email address?
It's possible and easy with hidden strings. It consists in encoding the name of a platform as invisible characters in your name on the said platform.
When the sender's crawler retrieves your name, the location from which they got your email will be hidden in the message all along and you'll know what to think of the content of the message.
The applications may vary, knowing that a platform leaks your data, how a recruiter found you...
I already did something similar by hiding invisible spaces (U+FEFF) in a recognizable pattern. The drawback was that I had to store a dictionary of patterns against website.
Reading this post, I realized that there were actually several invisible characters. The implication is that one can encode any message in a series of invisible characters.
This repository is a variation of the technique described above.
npm install -g sneacret
To hide secret
inside the string Container
:
sneacret hide -c Container -s secret
Arguments:
--container (-c)
: the string in which to hide the secret--secret (-s)
: the secret to hide--to-clipboard (-t)
: copies the result to the clipboard if xclip is installed
Reveal the secret hidden inside a string
To reveal the secret hidden inside Container
:
sneacret show -c Container
Arguments:
--container (-c)
: the string that contains the secret to reveal--to-clipboard (-t)
: copies the result to the clipboard if xclip is installed
To show all the supported characters, use:
sneacret alphabet
Arguments: none
stringWithSecret="$(sneacret hide -c Container -s Secret)"
# Displaying the result in the terminal might not work.
# Depending on the font, some invisible characters might get truncated.
# Pipe it to your clipboard or a file to avoid any issues.
sneacret show -c "$stringWithSecret"
There are 6 invisible characters that I identified, 5 of which seem to be supported correctly by most websites (see in src/utils.js). I could have encoded the utf-8 character code of each character in my secret in binary like it's done in the article but I figured I'd try to be more efficient.
For my use-case, being able to hide characters from the alphabet and a few punctuation marks is enough. This means that I have ~30 characters to encode.
Given the number of invisible characters I found, I can encode all my alphabet in just 3 slots because: 5^3 > 125
.
This means that if I create an alphabet where all letter are numbered from 0 to 25, I can express them in a base-6 number where 0 === String.fromCharCode(0x200B)
, 1 === String.fromCharCode(0x200C)
etc for all my characters.
Concrete example:
Name=Quilici
Secret=github
SecretAsCharacterCodes=6,8,19,7,20,1
SecretAsCodeInBase6=10,12,31,11,32,01
SecretAsSpaces='Quilici' <- yes there are 12 invisible spaces there!
Paste the result in dillinger.io if you don't trust it.
The spaces may not visible but they will still change some behaviors
- In some sites, if not supported by the font they'll show as squares
- In some sites they just won't show for multiple possible reasons
- The double click selects a word. Here it will select only the part that's not cut by the spaces
- Wrapping may decide to cut your word in two
- Your name may exceed limits on the platform because the spaces count as characters of course
- If used in the service provided by the platform, it may break their code