请求协助实现Kusto字符串差异计算 用于识别同形异义域名
字符串差异计算与同形异义域名识别方案
一、基础字符串差异计算
1. 获取具体差异字符
通过双指针遍历两个字符串,逐个对比字符,同时处理长度不一致的情况,输出新增、删除或替换的字符细节:
def get_string_diff(s1, s2): diff_details = [] i = j = 0 len1, len2 = len(s1), len(s2) while i < len1 and j < len2: if s1[i] != s2[j]: diff_details.append(f"位置{i}: {s1[i]} → {s2[j]}") i += 1 j += 1 else: i += 1 j += 1 # 处理s1剩余字符(删除操作) while i < len1: diff_details.append(f"位置{i}: 删除 {s1[i]}") i += 1 # 处理s2剩余字符(新增操作) while j < len2: diff_details.append(f"位置{len1}: 新增 {s2[j]}") j += 1 return diff_details # 示例 print(get_string_diff("outlook", "outlooka")) # 输出 ['位置6: 新增 a']
2. 统计差异字符数量
结合逐字符对比和长度差计算,快速得到总差异数:
def count_diff_chars(s1, s2): diff_count = 0 min_length = min(len(s1), len(s2)) # 对比相同长度部分 for c1, c2 in zip(s1, s2): if c1 != c2: diff_count += 1 # 加上长度差异的部分 diff_count += abs(len(s1) - len(s2)) return diff_count # 示例 print(count_diff_chars("outlook", "outlooka")) # 输出 1
3. 集合差异方案(局限性说明)
将字符串转为集合可快速找出存在差异的字符类型,但会丢失位置、重复信息,仅适合快速排查字符种类差异:
def get_set_based_diff(s1, s2): set_s1 = set(s1) set_s2 = set(s2) return { "仅在第一个字符串出现的字符": set_s1 - set_s2, "仅在第二个字符串出现的字符": set_s2 - set_s1 } # 示例 print(get_set_based_diff("outlook", "outlooka")) # 输出 {'仅在第二个字符串出现的字符': {'a'}, '仅在第一个字符串出现的字符': set()}
二、同形异义(Homoglyph)域名识别方案
普通差异计算无法识别视觉相似的字符替换(如0和o、1和l),针对域名场景需结合同形字符归一化和编辑距离实现精准识别:
1. 核心思路
- 建立同形字符映射表,将视觉相似的字符转换为统一基准字符
- 计算归一化后的域名编辑距离,结合长度差判断是否为疑似同形异义域名
2. 实现代码
# 可扩展的同形字符映射表(覆盖常见混淆字符) HOMOGLYPH_MAPPING = { '0': 'o', 'O': 'o', '1': 'l', 'L': 'l', 'ı': 'i', 'İ': 'i', '8': 'b', 'B': 'b', 'ε': 'e', 'E': 'e', 'ς': 's', 'S': 's', 'т': 't', 'T': 't', 'ж': 'x', 'Ж': 'x' } def normalize_homoglyph(domain): """将域名中的同形字符转换为基准字符""" normalized = [] # 统一转为小写后处理 for char in domain.lower(): normalized.append(HOMOGLYPH_MAPPING.get(char, char)) return ''.join(normalized) def levenshtein_distance(a, b): """计算两个字符串的编辑距离(插入、删除、替换的最小操作数)""" if len(a) < len(b): return levenshtein_distance(b, a) if len(b) == 0: return len(a) prev_row = list(range(len(b) + 1)) for i, char_a in enumerate(a): curr_row = [i + 1] for j, char_b in enumerate(b): insert_cost = prev_row[j + 1] + 1 delete_cost = curr_row[j] + 1 replace_cost = prev_row[j] + (char_a != char_b) curr_row.append(min(insert_cost, delete_cost, replace_cost)) prev_row = curr_row return prev_row[-1] def is_suspicious_homoglyph_domain(domain1, domain2): """判断两个域名是否为疑似同形异义域名""" # 提取主域名(假设域名格式为xxx.xxx,可根据实际情况调整提取逻辑) def extract_main_domain(d): return d.split('.')[0] main1 = extract_main_domain(domain1) main2 = extract_main_domain(domain2) # 归一化同形字符 norm1 = normalize_homoglyph(main1) norm2 = normalize_homoglyph(main2) # 计算编辑距离和长度差 distance = levenshtein_distance(norm1, norm2) len_diff = abs(len(norm1) - len(norm2)) # 阈值可根据需求调整:长度差≤1且编辑距离≤2时判定为疑似 return len_diff <= 1 and distance <= 2 # 示例测试 print(is_suspicious_homoglyph_domain("outlook.com", "0utlook.com")) # 输出 True(0替换o) print(is_suspicious_homoglyph_domain("google.com", "g00gle.com")) # 输出 True(0替换o) print(is_suspicious_homoglyph_domain("outlook.com", "outlooka.com"))# 输出 True(新增字符) print(is_suspicious_homoglyph_domain("apple.com", "orange.com")) # 输出 False(差异过大)
3. 优化建议
- 扩展同形字符映射表:覆盖更多Unicode混淆字符(如西里尔字母、希腊字母与英文字母的相似字符)
- 域名预处理:先去除前缀(如www.)、统一后缀格式,避免无关差异干扰
- 调整阈值:针对短域名(如3-5字符)可将编辑距离阈值设为1,长域名适当放宽
内容的提问来源于stack exchange,提问作者Krithika
相关产品推荐
相关产品推荐

