You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求协助实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 18:30:15