• 请不要在回答技术问题时复制粘贴 AI 生成的内容
itechnology
V2EX  ›  程序员

手机号你们通常怎么在加密保存的同时兼具模糊搜索的功能?

  •  
  •   itechnology · 8h 29m ago · 2303 views

    我目前的方案是这样的:

    1 、手机号经过 AES-GCM 对称加密后保存到数据库,每次加密会随机生成向量 IV ,保存到数据库的值是“向量 IV:密文”,秘钥是全局唯一的(保存在 nacos 中)。

    2 、为了支持手机号模糊搜索,我利用了项目中现成的 es ,文档的结构如下:

    @Data
    @Document(indexName = "user_phone_index")
    public class UserPhoneDocument {
    
        @Id
        private String id; // 与 MySQL 的 userId 一致
    
        @Field(type = FieldType.Keyword)
        private Long userId;
    
        /**
         * 带位置的 N-Gram HMAC 盲索引数组(对 "位置:四位切片" 哈希),必须为 keyword 类型。
         * 例:0:1381 、1:3812 → 各自 HMAC
         */
        @Field(type = FieldType.Keyword)
        private List<String> phoneTokens;
    
        // 存储完整手机号的 HMAC (用于精确搜索)
        @Field(type = FieldType.Keyword)
        private String phoneFullHash;
    
    }
    

    phoneTokens 和 phoneFullHash 都会用全局唯一的 hmac key (保存在 nacos )进行哈希。

    phoneTokens 是一个滑动切片,固定四位,每次向右滑动一位。

    这样设置以后,就可以做到模糊搜索,但要求搜索时至少输入 4 位数以上。

    31 replies    2026-08-04 18:08:54 +08:00
    xmsl
        1
    xmsl  
       8h 17m ago
    受教了!很好的教程
    opengps
        2
    opengps  
       8h 13m ago
    如果允许弱一点,其实可用解绑的方案来模糊查询,就是所有手机号明文,但是个独立的单列表(手机号没直接绑定到用户 id 上),模糊查询之后,密文跨表关联
    itechnology
        3
    itechnology  
    OP
       7h 56m ago
    @opengps 你这个方案不咋行吧?一般场景都是看你数据库有没有明文保存手机号,并不是说你没有关联用户 id 就没关系了。
    opengps
        4
    opengps  
       7h 52m ago
    @itechnology #3 所以说也是个弱方案,至少做到解绑了直接关系,数据库被脱裤也只是知道有这个手机号,但不一定知道这个号是哪个 id 的
    shintendo
        5
    shintendo  
       7h 38m ago
    手机号模糊搜索这需求感觉好奇怪
    itechnology
        6
    itechnology  
    OP
       7h 30m ago
    @shintendo 不奇怪啊,这种需求很常见。
    WaldenHorizon
        7
    WaldenHorizon  
       7h 29m ago
    如果是为了过等保,防止别人拿走物理文件,在存储层开启加密就行了,不影响应用层任何功能
    我之前不了解这个东西做等保的时候,产品要求手机号精确搜索,是在代码层用相同的加密方式和密钥处理搜索字符串,然后精确搜索密文
    AutumnVerse
        8
    AutumnVerse  
       7h 13m ago via iPhone
    ngram 分词后,拿到你 ngram 数组的结果,不就能反推出手机号了吗?最后还是相当于明文存储
    ajaxfunction
        9
    ajaxfunction  
       7h 5m ago
    糟糕,我完全是明文,只是前端显示的时候是 脱敏的
    AutumnVerse
        10
    AutumnVerse  
       7h 3m ago via iPhone
    我的方案,es 里面存两份数据
    第一份跟你的 hash 一致,用于精确搜索
    第二份是 138901***42 这种部分打码的数据,这样能用前缀搜索,也能用尾缀做区分
    shintendo
        11
    shintendo  
       6h 54m ago
    @itechnology 一般精确搜索或者尾号 4 位搜索就可以了吧,这个任意位置搜索岂不是可以反推明文
    wu00
        12
    wu00  
       6h 50m ago   ❤️ 1
    搜索 8562 ,出结果 186****8866 ?
    Dxxxxs
        13
    Dxxxxs  
       6h 35m ago
    @AutumnVerse #8 HMAC 不就是解决这个问题的吗
    theliang
        14
    theliang  
       6h 32m ago
    这用户量大的时候这索引有点恐怖
    gouLang
        15
    gouLang  
       6h 11m ago
    也就是说 11 位的手机号从前四位开始不断向右滑, 每次切四位, 生成 phonetokens 吗, 一共切八次?
    Tsing2
        16
    Tsing2  
       6h 5m ago   ❤️ 1
    如果这是面试题的话:
    1. 不用可逆的加密,用哈希。
    2. 存完整手机号的哈希。
    3. 存前四位的哈希。
    4. 存后四位的哈希。
    以上是最基本的思路,根据 PRD 作相应修改
    chjqpmain
        17
    chjqpmain  
       5h 5m ago via iPhone
    果然我在真实项目的全周期中一堆东西都不知道,感谢分享,要继续了解
    visper
        18
    visper  
       4h 22m ago
    用假加密,一一对应。
    itechnology
        19
    itechnology  
    OP
       4h 19m ago
    @gouLang 是的
    potatowish
        20
    potatowish  
       4h 16m ago via iPhone
    是我孤陋寡闻了,对于手机号、身份证号的,我只见过精准匹配查询
    nullico
        21
    nullico  
       4h 15m ago
    感觉没必要,现在绝大多数场景要不就是精确搜索,要不就是尾号四位。当然,可能你产品比较特殊,不过我觉得绝大多数场景都没必要这么复杂,很直接全部和尾号这部分 hash 一下都行,然后直接搜这个
    itechnology
        22
    itechnology  
    OP
       4h 10m ago
    @shintendo 如果项目场景已经确定了只会有精确搜索或者手机 4 位尾号搜索,那就不需要引入 es 了,直接建两个辅助字段,分别保存完整手机号 HMAC 和手机 4 位尾号 HMAC 就可以了,比我这个方案要简单、方便很多。
    SoulFlame
        23
    SoulFlame  
       3h 52m ago
    以前我是自己写了一套映射算法,然后存储映射后字符串,别人乍一看数据库存储的似乎就是加密了。应付下得了
    nnnnnnamgn
        24
    nnnnnnamgn  
       1h 34m ago
    没做过类似的业务,要整个手机号加密存储的场景是什么?
    ming2050
        25
    ming2050  
       1h 30m ago
    话说这个安全需求是怎么产生的
    xiangbohua
        26
    xiangbohua  
       1h 13m ago
    @opengps 你的关联表在哪?跨表关联那关联表意思在一个库里面,然后库被脱了,那不还是明文啊
    opengps
        27
    opengps  
       1h 11m ago
    @xiangbohua #26 密文列是用密钥临时算的,算完了才有完整临时表可用来跨表关联
    ala2008
        28
    ala2008  
       1h 2m ago
    我们之前做 3 级等保,存储加密,直接干掉搜索功能,不能搜索手机号 这些都有现成方案吧,淘宝、jd 、拼多多开发文档
    rammiah
        29
    rammiah  
       56 mins ago
    @ming2050 #25 手机号等敏感信息有的备案要求加密存储,但是产品里有模糊搜索的需求
    xndeye
        30
    xndeye  
       32 mins ago
    手机号这种固定格式数据通常不会做全文模糊吧?一般也就是 完整匹配、前三位+后四位匹配、后 4 位匹配 三种情况,直接做盲索引就行了
    DengDDDD
        31
    DengDDDD  
       Just Now
    做 单字节 hash+密文串, 模糊查询查前面部分
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   4393 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 54ms · UTC 10:09 · PVG 18:09 · LAX 03:09 · JFK 06:09
    ♥ Do have faith in what you're doing.