当公司的核心文档或产品需求(PRD)截图在未经授权的情况下流出,如何在缺乏显式水印的情况下定位信息源头?一种基于零宽字符(Zero Width Characters)的隐形水印技术为此提供了巧妙的解决方案。
零宽字符是什么?
在Unicode字符集中,存在一类不占用任何视觉宽度、在大多数渲染环境中不可见的特殊字符,即零宽字符。它们可以被插入到文本中,常规的复制、粘贴操作会将其一并携带,但肉眼无法察觉。
常见的零宽字符包括:
\u200b:零宽空格 (Zero Width Space)
\u200c:零宽非连字符 (Zero Width Non-Joiner)
\u200d:零宽连字符 (Zero Width Joiner)
在浏览器控制台中验证其特性:
console.log('A' + '\u200b' + 'B'); // 输出: "AB" (视觉上与普通"AB"无区别)
console.log(('A' + '\u200b' + 'B').length); // 输出: 3 (实际长度包含隐形字符)

技术原理简述
该技术的核心思想是利用零宽字符对特定信息(如员工ID)进行二进制编码,并将其隐匿地嵌入到正常文档内容中。
- 建立编码表:选取两个零宽字符分别代表二进制
0和1。
- 信息编码:将标识信息(如
User_9527)转换为二进制串,并用对应的零宽字符替换。
- 嵌入文本:将生成的隐形字符序列插入到目标文本的任意位置。
- 提取解码:从疑似泄露的文本中提取零宽字符序列,逆向解码即可还原出原始标识信息。
代码实现:编码与解码
以下是用原生JavaScript实现的一个简易版本。
1. 编码函数 (注入水印)
// 零宽字符编码映射
const zeroWidthMap = {
'0': '\u200b', // 代表二进制0
'1': '\u200c' // 代表二进制1
};
// 将文本转换为8位二进制字符串
function textToBinary(text) {
return text.split('').map(char =>
char.charCodeAt(0).toString(2).padStart(8, '0')
).join('');
}
// 将秘密信息编码为零宽字符并嵌入原文
function encodeWatermark(plainText, secret) {
const binarySecret = textToBinary(secret);
const hiddenStr = binarySecret.split('').map(b => zeroWidthMap[b]).join('');
// 示例:在第一个字符后插入水印,实践中可随机分布以增强隐蔽性
return plainText.slice(0, 1) + hiddenStr + plainText.slice(1);
}
// 使用示例
const originalText = "公司机密文档,严禁外传!";
const userWorkId = "User_9527";
const watermarkedText = encodeWatermark(originalText, userWorkId);
console.log("原文:", originalText);
console.log("带水印文本:", watermarkedText);
console.log("视觉是否相同?", originalText === watermarkedText); // false
console.log("长度对比:", originalText.length, watermarkedText.length); // 后者更长

嵌入水印后的文本在通过微信、飞书等渠道复制传播时,隐形标记会随之流动。这是前端开发中一种非常独特的交互与数据追踪思路。
2. 解码函数 (提取水印)
当获取到疑似泄露的文本时,可通过以下函数还原水印信息。
// 零宽字符解码映射
const binaryMap = {
'\u200b': '0',
'\u200c': '1'
};
function decodeWatermark(textWithWatermark) {
// 1. 使用正则提取所有零宽字符
const hiddenChars = textWithWatermark.match(/[\u200b\u200c]/g);
if (!hiddenChars) return '未发现水印';
// 2. 将零宽字符序列还原为二进制字符串
const binaryStr = hiddenChars.map(c => binaryMap[c]).join('');
// 3. 将二进制字符串解码为原始文本
let result = '';
for (let i = 0; i < binaryStr.length; i += 8) {
const byte = binaryStr.slice(i, i + 8);
result += String.fromCharCode(parseInt(byte, 2));
}
return result;
}
// 使用示例:从带水印文本中提取信息
const leakerId = decodeWatermark(watermarkedText);
console.log("提取到的标识信息:", leakerId); // 输出: User_9527

技术方案的局限性与应对
此方案的优势在于实现简单、隐蔽性极高,对不知情的泄露者能起到有效的溯源作用。然而,它并非无懈可击:
- 手动清除:如果攻击者重新键入文本,水印自然丢失。
- 技术对抗:知晓此技术的内部人员可以使用简单的脚本过滤掉所有零宽字符,例如:
text.replace(/[\u200b-\u200f]/g, '')。
因此,它更适合作为一种低成本的补充性安全防御措施,或用于监控低频、非技术性的泄露途径。
总结
零宽字符盲水印技术展示了在Web文本内容保护中一种“隐藏于无形”的思维。它不仅是文档防泄漏的一种有趣实践,也为前端开发者理解Unicode、字符编码以及数据安全提供了新的视角。在面对“如何保护网页内容不被轻易复制传播”这类问题时,此方案可以作为一个有力的技术备选答案。