文本文件水印
#coding
背景
突然的一个思考,如果售卖的数据库可以直接提供文本格式给客户下载,怎么避免客户直接把这个数据库传输给其他人?
方案讨论
方案一
法律威慑。在合同上写明如果二次分发,有权XXX,赔偿XXX。同时在文本文件抬头和结尾补充醒目的字样进一步威慑。
方案二
零宽度字符水印。对于每个客户,构建唯一的编号,然后将这个编号编码为一串零宽度的字符串,嵌入到这个文本文件中。
ZERO_WIDTH_SPACE = '\u200B' # 表示 0
ZERO_WIDTH_NON_JOINER = '\u200C' # 表示 1
ZERO_WIDTH_JOINER = '\u200D' # 分隔符
INVISIBLE_SEPARATOR = '\u2060' # 不可见分隔符
# 将客户编号转换为不可见字符
def embed_watermark(text, client_id):
# 将客户编号转换为二进制字符串
binary_id = ''.join(format(ord(char), '08b') for char in client_id)
# 将二进制字符串转换为不可见字符
watermark = ''.join(
ZERO_WIDTH_NON_JOINER if bit == '1' else ZERO_WIDTH_SPACE
for bit in binary_id
)
# 添加不可见分隔符
watermark = INVISIBLE_SEPARATOR.join(watermark[i:i+8] for i in range(0, len(watermark), 8))
# 将水印嵌入文本
return text + watermark
# 从文本中提取客户编号
def extract_watermark(text):
# 提取所有不可见字符
watermark = ''.join([char for char in text if char in (ZERO_WIDTH_SPACE, ZERO_WIDTH_NON_JOINER, INVISIBLE_SEPARATOR)])
# 移除不可见分隔符
watermark = watermark.replace(INVISIBLE_SEPARATOR, '')
# 将不可见字符转换为二进制字符串
binary_id = ''.join('1' if char == ZERO_WIDTH_NON_JOINER else '0' for char in watermark)
# 将二进制字符串转换为客户编号
client_id = ''.join(chr(int(binary_id[i:i+8], 2)) for i in range(0, len(binary_id), 8))
return client_id
# 示例
original_text = "这是一个示例文本。"
client_id = "110101010101010101010101"
# 嵌入水印
watermarked_text = embed_watermark(original_text, client_id)
print("嵌入水印后的文本:", watermarked_text)
# 提取水印
extracted_id = extract_watermark(watermarked_text)
print("提取的客户编号:", extracted_id)
这是GPT给的方案,但是实际中,现在的notepad等文本编辑器都是可以显示出这些特殊字符的,如果采用这个方案,最好是在更隐蔽的地方进行嵌入。
方案三
虚构信息。根据自己的数据库类型进行设计。譬如说,我的数据库是基因突变信息数据库,我可以设计一批虚构的突变。只要设计大概24个位点,就可以识别10000000个客户了。
1,在热门的基因中设计,可以确保就算客户对基因进行筛选后,这些特殊设计也会保留下来;
2,和人群数据库,COSMIC数据库等比对,确保这个虚构突变不会出现在任何已知数据库中(即在真实情况中,受检者是不会检出这个突变的,避免影响);
3,采取这批次位点的排列组合,在内部用于对客户进行识别。
方案四
同义词。我个人觉得这个方案最隐匿。还是以基因突变信息数据库为例。选择一批热点突变,譬如EGFR L858R、ALK G12C等,对他们的描述中的一些词语采用不影响意义的同义词。提供给每个客户的数据库的这些位点的描述都是唯一的,通过识别这批位点的特定同义词字符的排列组合,达成对客户的识别。
这个方案隐匿度高,而且不用与人群数据库等进行比较。缺陷是和上述一样,需要根据数据的特点进行设计。
伪代码
# 同义词替换水印方案
# 定义热点突变及其同义词选项
hotspot_mutations = {
"EGFR L858R": {
"mutation": ["mutation", "variant", "alteration"],
"associated with": ["associated with", "linked to", "related to"],
"non-small cell lung cancer": ["non-small cell lung cancer", "NSCLC"]
},
"ALK G12C": {
"mutation": ["mutation", "variant", "alteration"],
"associated with": ["associated with", "linked to", "related to"],
"lung cancer": ["lung cancer", "NSCLC"]
}
}
# 生成客户唯一标识
def generate_watermark(client_id):
# 将客户编号转换为同义词选择
choices = []
for bit in client_id:
choices.append(int(bit) % 3) # 假设每个位置有 3 个同义词选项
return choices
# 嵌入水印
def embed_watermark(database, client_id):
choices = generate_watermark(client_id)
watermarked_db = database.copy()
for i, (mutation, descriptions) in enumerate(hotspot_mutations.items()):
for j, (key, synonyms) in enumerate(descriptions.items()):
# 替换同义词
watermarked_db = watermarked_db.replace(
f"{mutation}: {key}",
f"{mutation}: {synonyms[choices[i * len(descriptions) + j]]}"
)
return watermarked_db
# 提取水印
def extract_watermark(database):
choices = []
for mutation, descriptions in hotspot_mutations.items():
for key, synonyms in descriptions.items():
# 查找使用的同义词
for index, synonym in enumerate(synonyms):
if f"{mutation}: {synonym}" in database:
choices.append(str(index))
break
return ''.join(choices)
# 示例
database = """
EGFR L858R: mutation is associated with non-small cell lung cancer.
ALK G12C: mutation is associated with lung cancer.
"""
client_id = "110101010101010101010101"
# 嵌入水印
watermarked_db = embed_watermark(database, client_id)
print("嵌入水印后的数据库:\n", watermarked_db)
# 提取水印
extracted_id = extract_watermark(watermarked_db)
print("提取的客户编号:", extracted_id)
后记
实际操作中所有方案可以全用上,增强容错。