在浏览器上用零宽字符藏私货

两三个星期前,看到篇文章讲用零宽字符隐藏信息来抓那些xjb复制的人

Be careful what you copy: Invisibly inserting usernames into text with Zero-Width Characters

挤点时间出来记录一下,以免过目就忘

首先讲下零宽字符,零宽字符是一种不可打印的Unicode字符,包括零宽连字、零宽不连字、零宽空格。不显示的前提貌似是在浏览器上,我试了复制到qq和复制到编辑器里都是会格式错误,一眼就发现文本中藏了私货。然后讲如何编码与解码。

首先要将编码前的文本转为8位二进制的形式。

1
2
3
4
5
const zeroPad = num =>00000000’.slice(String(num).length) + num;
const textToBinary = username => (
username.split('').map(char =>
zeroPad(char.charCodeAt(0).toString(2))).join(' ')
);

例如a,将a的ASCII码转为2进制形式,不足8位的在前面用0填充,每个8位间用空格隔开。

其次将其中的空格、0、1分别用对应的零宽字符替换

1
2
3
4
5
6
7
8
9
10
11
const binaryToZeroWidth = binary => (
binary.split('').map((binaryNum) => {
const num = parseInt(binaryNum, 10);
if (num === 1) {
return ''; // zero-width space \u200b
} else if (num === 0) {
return '‌'; // zero-width non-joiner \u200c
}
return '‍'; // zero-width joiner \u200d
}).join('') // zero-width no-break space
);

这里将1用零宽空格\u200b,0用零宽不连字\u200c,空格用零宽连字\u200d替换

这样就编码好了一段另类的二进制,将这段零宽字符插到文本中,就可以充当一种指纹,当别人无脑复制盗你的文章的时候,将他文章中的零宽字符解码后甩他脸上。他就无话可说了。

然后再讲下如何解码

首先是要将零宽字符还原成二进制

1
2
3
4
5
6
7
8
9
10
const zeroWidthToBinary = string => (
string.split('').map((char) => { // zero-width no-break space
if (char === '') { // zero-width space \u200b
return '1';
} else if (char === '‌') { // zero-width non-joiner \u200c
return '0';
}
return ' '; // add single space
}).join('')
);

如果是\u200b就替换成1,是\u200c就替换成0,如果都不是就替换成空格,这样就还原了编码第一步后的二进制组了

然后就是还原二进制为ASCII码了

1
2
3
4
const binaryToText = string => (
string.split(' ').map(num =>
String.fromCharCode(parseInt(num, 2))).join('')
);

挺有趣的一个技巧,在我搜索资料的过程中,我发现了有老哥用零宽来缩短代码显示和隐藏代码hhhhh

js 奇葩技巧之隐藏代码

“短”化你的代码