两三个星期前,看到篇文章讲用零宽字符隐藏信息来抓那些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 奇葩技巧之隐藏代码

“短”化你的代码