一本机读区每个校验位都通过的护照,看起来值得信任。算术对得上,校验器连续五次返回 true,开户流程继续往下走。但有必要准确弄清这个 true 到底意味着什么,因为它承诺的东西比看上去少得多。
这篇入门写给构建 KYC 和开户流程的开发者。它把常见的筛查检查分成三类:MRZ 校验位能发现的、发现不了的,以及每个漏洞该由哪项其他检查来补。下文所有机读区都是 ICAO 自己的样本,即“Utopia”的 Anna Maria Eriksson,或者为本文编造的变体。不出现任何真实证件。
这里是 doc.cheap 的博客。doc.cheap 是一个护照与身份证 OCR API。下文的内容都不依赖它。
校验位是做什么用的
MRZ 是护照照片页底部或身份证背面那一块由大写字母、数字和填充符 < 组成的区域。ICAO Doc 9303 为它规定了校验位:证件号码、出生日期和有效期各一个,护照上的可选个人号码一个,还有一个覆盖下行大部分内容的复合校验位。
每个校验位都是加权和。每个字符先换成数字(0–9 就是自身,A = 10 到 Z = 35,< = 0),再乘以循环的权重 7、3、1,总和对 10 取余就是校验位。MRZ 校验位的计算原理一文逐步讲解,并附有可以直接粘贴使用的校验器。
校验位的设计目的是发现读取错误。它从来就不是用来识别欺诈的,下文的一切都由此而来。
校验位能发现什么
单个数字出错。 因为 7、3、1 与 10 都没有公因数,把字段中任意一位数字改成另一个数字,总和对 10 的余数一定会变。3 读成 8,5 读成 6:字段校验位不再匹配,复合校验位也一样。
大多数相邻字符互换。 两个相邻字符交换位置时,总和的变化量等于两者数值之差乘以权重之差(4、2 或 6)。只有当两个数值之差是 5 的倍数时,这个变化量才是 10 的倍数。所以 98 录成 89 会被发现,而 16 录成 61 会漏过去,05 录成 50 也一样。
污损、反光和手工重录。 这正是校验位的用武之地。一道折痕压过某个字符、覆膜上的反光、有人手工重新输入机读区:大多数错误的 MRZ 数据都出自这些原因,而校验位不通过就是一个低成本的信号,说明某个字段读错了。正确的做法是重新拍照,而不是拒绝。
算术看不见的地方
即使是无意的错误,校验位也有盲区,筛查流程应该清楚这些盲区。
字母被读成数值相差 10、20 或 30 的字符。 以样本证件号码 L898902C3 为例,它的校验位是 6。把 C(数值 12)换成 2(数值 2):总和减少 10 × 3 = 30,从 316 变成 286,校验位仍然是 6。复合校验位以同样的权重覆盖这些字符,所以也照样通过。B 和 1、C 和 2 这样的字符对可以悄无声息地互相替换。
没有任何校验位覆盖的字段。 姓名、签发国、国籍和性别都不在任何字段校验位的范围内,复合校验位也跳过国籍和性别。姓名读错不会有任何提示,只有与印刷页面比对才能发现。
完全发现不了的
本节中的所有情况,按其构造方式,都会通过 MRZ 校验位。
机读区内部自洽的伪造证件。 算法是公开的,二十行代码就能写完。制作假证件的人会正确计算校验位,因为错误的校验位是每台读取设备都能免费发现的那一个错误。机读区通过校验,只说明机读区读对了,并不说明证件是真的。
照抄真实人物的数据。 用他人真实的姓名、出生日期和证件号码生成的机读区,校验位当然有效,因为数据本身是真的。镜头前的人是不是这个人,是另一个问题:需要与证件照片做人脸比对、做活体检测,或者在有权限的情况下查询签发机构的遗失被盗证件名单。
过期的证件。 有效期 120415 能通过校验位,而它是 2012 年 4 月 15 日。有效期是关于日期的规则,不是校验和:请把它与今天的日期比较。注意 YYMMDD 不含世纪,需要根据上下文确定;方法见 YYMMDD 日期格式。本站的 MRZ 校验位计算器会在校验位旁边把已过的有效期标记为已过期,两个结论可以并排查看。
换贴的照片。 MRZ 不包含图像,也不包含图像的哈希。资料页上的照片被换掉,机读区不受任何影响。
被克隆或篡改的芯片。 在带芯片的护照上,数据组由签发国签名,验证这个签名(被动认证,ICAO Doc 9303 Part 11)才能确认数据来自签发机构。这需要 NFC 读取和签发机构的证书。证件页的照片拍得再好,也回答不了这个问题。MRZ 只提供用来打开芯片的访问密钥。
常被跳过的检查:机读区与页面比对
一份证件上的大多数信息都写了两遍:一遍在 MRZ 中,一遍在视读区(VIZ),也就是供人阅读的印刷页面上。两者由同一签发机构同时制作,所以在真实、完好的证件上它们是一致的。
这使得不一致比校验位不通过更值得关注。校验位不通过,通常只是照片拍得不好。校验位全部通过,而印刷的姓、出生日期或证件号码却写着别的内容,就说明有一方读错了,或者在印刷后被改动过。无论哪种情况,这份证件都需要人工查看。
这种比对必须专门去做。读取两个区域,把印刷的值规范化(日期统一成同一种格式,姓名化简为纯大写字母),再逐个字段比较。还要考虑转写:印刷的 Müller 在机读区中可能是 MUELLER,也可能是 MULLER,取决于签发机构。MRZ 格式页面列出了每种版式包含哪些字段,也就是哪些字段可以比对。
一套经得起考验的筛查顺序
- 形态。 行数和长度决定是 TD1、TD2 还是 TD3。形态不对就是读取失败。
- 校验位。 不通过意味着重新拍照,而不是拒绝。
- 机读区与页面比对。 校验位通过但内容不一致,交给人工处理。
- 日期。 有效期与今天比较;出生日期与您的年龄规则比较。
- 身份。 与证件照片进行人脸比对和活体检测。
- 真伪。 通过 NFC 验证芯片,或由专门的服务检查物理防伪特征。
第 1 到 4 步成本低,任何图像都能做。第 5 和第 6 步是独立的产品,MRZ 算术做得再多也无法替代。
doc.cheap 的定位
doc.cheap 的护照 OCR API 覆盖第 1 到 4 步,对其余部分如实报告。响应中带有 mrz.status:passed、failed 或 absent。当结果为 failed 时,mrz.reason 会指出哪里没有通过:要么是校验位(Check digit failed for: document number),要么是与印刷页面不一致(MRZ does not match the visual zone for: surname),因为默认会读取并比对两个区域。机读区本身原样返回在 mrz.lines 和 mrz.text 中,您可以在自己的代码里重新计算校验位。document.expiry_date、document.is_expired 和 document.days_remaining 回答第 4 步。
它是识别服务,不是核验服务。它的 authenticity 块返回 not_checked,而不是暗示已经通过;照片和芯片检查交给专门的工具。想不写代码就试一个机读区,把它粘贴到 MRZ 校验位计算器即可;它在浏览器中运行,并在每个印刷的校验位旁边显示计算出的校验位。如果您正在挑选托管服务,护照 OCR API 对比把各家公开的价格并排列出。
**最后一个问题:**这些漏洞中,您的开户流程在实际中遇到过哪一个?欢迎在下方评论区告诉我们。