如何验证链上哈希未被篡改?

解读

在国内 CouchDB 面试中,面试官提出“链上哈希”并非让你去讲比特币或以太坊,而是把 CouchDB 的文档版本链(_rev 树)当成一条“轻量级区块链”,考察你能否用数据库原生机制自证数据未被篡改。核心诉求是:

  1. 不依赖外部区块链,仅用 CouchDB 内置能力完成“篡改检测”;
  2. 回答必须落地到HTTP 接口、Erlang 层验证、国内等保/国密合规三个维度;
  3. 区分“单节点篡改”与“多主集群恶意回滚”两种场景,给出可落地的验证脚本与运维方案。

知识点

  • _rev 树与 MVCC:CouchDB 每次更新都生成新的 revision 节点,形成不可变的树形历史,叶子节点即为最新合法版本
  • _changes 连续 feed:国内金融项目常用 ?feed=continuous&heartbeat=30000&since=now 做实时审计流,每条变更带 seq 与 rev,可视为“链上高度”。
  • Erlang 层校验和couch_db:validate_doc_hash/2 在写入前计算 sha256(<<Id,Rev,Body>>)与 _local/sha256 中的预期值比对,防内存级篡改。
  • 国密 SM3 插件:国产信创环境需替换默认哈希,在 etc/vm.args 加 -couch_hash_alg sm3,验证脚本同步升级。
  • 多主回滚检测:利用 /_node/_local/_changes 与各节点 seq 一致性哈希,若出现 seq 回退或 rev 树分叉深度 > max_revs_limit,即可判定“链”被恶意回滚。

答案

验证步骤(可直接在面试官笔记本上演示):

  1. 生成可信基线
    写入文档后立刻抓取:

    curl -u $user:$pass http://127.0.0.1:5984/mydb/doc1?revs=true > baseline.json
    

    提取 id、rev、body 的 SM3 哈希并写入 _local 可信区:

    echo -n "doc1-3-abc{...}" | openssl dgst -sm3 -binary | base64
    curl -X PUT -u $user:$pass http://127.0.0.1:5984/mydb/_local/sha3-doc1 \
         -d '{"sm3":"q8T+W7m...","seq":42}'
    
  2. 周期性自检脚本(符合等保审计要求)

    #!/bin/bash
    DOC=$(curl -s -u $user:$pass http://127.0.0.1:5984/mydb/doc1?revs=true)
    REV=$(echo "$DOC" | jq -r '._rev')
    BODY=$(echo "$DOC" | jq -c 'del(._rev)|del(._id)')
    CALC_SM3=$(echo -n "doc1-$REV-$BODY" | openssl dgst -sm3 -binary | base64)
    STORED_SM3=$(curl -s -u $user:$pass http://127.0.0.1:5984/mydb/_local/sha3-doc1 | jq -r '.sm3')
    if [[ "$CALC_SM3" != "$STORED_SM3" ]]; then
        echo "ALERT: 链上哈希被篡改"
        # 触发国产 SOC 平台 webhook
    fi
    

    脚本放 crontab 每分钟执行,输出到 syslog 并对接国内 SOC,满足等保 2.0 审计留存 180 天要求。

  3. 多主集群防回滚
    在各节点运行:

    curl -u $user:$pass http://node{1..3}:5984/mydb/_changes?since=0&limit=1 | jq '.last_seq'
    

    若任一节点 last_seq 小于基线 seq,或 rev 树深度突增,立即标记“链分叉”,通过 CouchDB 的 pre-commit hook 拒绝新写入,并通知运维人工介入。

拓展思考

  • Q:如果攻击者连 _local 也一起改怎么办?
    答:把 基线哈希再写一份到外部 WORM 存储(国产蓝光光盘或华为 OceanStor 锁盘),CouchDB 侧只存引用指针,实现离线不可抵赖

  • Q:如何验证附件(_attachments)哈希?
    答:附件有 stub+length+revpos 三元组,脚本里把 attsrevpos 排序后计算 SM3 累积哈希,再与基线比对;大文件用流式分片,防止内存打爆

  • Q:能否用 Fabric 等真区块链再存一次?
    答:可以,但国内银行生产网常隔离外网,更实用做法是把 CouchDB 的 seq 与哈希每日批量写入内部联盟链(如长安链),实现“链上链”双锚定,既满足监管,又保持 CouchDB 原生性能。