关于 israeli-id-validator
israeli-id-validator 是用于校验和处理以色列个人及注册实体编号的技能。它说明校验位规则、安全输入处理和类型标注限制,并附有 Python 与 JavaScript 参考。
主要功能
- 01以色列编号校验位验证
- 02个人及实体格式指南
- 03生成开发测试编号
- 04支持批量与遮罩模式的 Python 命令行工具
- 05JavaScript 实现参考
- 06输入处理与隐私防护
适用场景
- 01检查以色列编号格式
- 02为本地开发测试生成编号
- 03在浏览器或 Node 中实现校验
- 04在命令行和 CI 输出中遮罩编号
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skills-il/ developer-tools --skill israeli-id-validator安装后在 Claude Code 中输入/israeli-id-validator确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CHANGELOG.mdevidence.jsonmetadata.jsonoptimization-log.jsonSKILL_HE.mdreferences/id-formats.mdjavascript.mdscripts/validate_id.py详细介绍
以色列身份证号码验证器
说明
步骤 1:识别 ID 类型
| 类型 | 前缀 | 长度 | 示例 | 备注 |
|---|---|---|---|---|
| Teudat Zehut(个人 ID) | 无(无法推断) | 9 位数字 | 123456782 | 这些数字不包含出生日期、年龄或居留身份信息 |
| 公司/注册实体 | 通常以 5 开头 | 9 位数字 | 51-530820-3 | 前两位通常与实体类型相关(代码 50-59,见下表),这只是可能的标签,并非规则。校验位算法与个人 ID 相同 |
公司和注册实体代码(前两位):
| 前缀 | 实体 |
|---|---|
| 50 | 政府公司、养老金/公积金或地方委员会 |
| 51 | 私营公司(Chevra Ba'am / Ltd) |
| 52 | 上市公司 |
| 53 | 授权时期合伙企业(Shutafut Mandatorit,英国托管时期) |
| 54 | 普通合伙企业 |
| 55 | 合伙企业(Shutafut) |
| 56 | 外国公司 |
| 57 | 合作社(Aguda Shitufit)/基布兹 |
| 58 | Amuta(非营利组织)/公益公司 |
| 59 | 捐赠基金(Hekdesh),本身不是公司 |
基于前缀的类型判断只是一种启发式方法:以 5 开头的 9 位数字通常属于注册实体(资料称前两位“通常”与实体类型相关,且所有列出的代码都以 5 开头),但只有签发登记机构的判断才具有权威性。绝不能因为数字的前缀而拒绝该号码,或决定它应填写在哪个字段中:仅当用户已说明该号码是公司号码时,才使用前缀为其标注类型。个人 Teudat Zehut 无法通过前缀识别类型。
步骤 2:使用校验位算法验证
以色列 ID 校验位算法(适用于所有类型):
import re
def validate_israeli_id(id_number: str) -> bool:
"""Validate Israeli ID number (TZ, company, amuta, etc.)"""
# Discard ONLY the separators a human or a spreadsheet legitimately puts
# inside an ID. Do NOT strip every non-digit: that silently turns
# "62819482.1" into the VALID id 628194821, and turns "", "abc" and a row
# of punctuation into "000000000". A validator that accepts garbage is
# worse than one that rejects a real ID, because nobody finds out.
# Do NOT gate on str.isdigit() either: it is True for Arabic-Indic digits
# and superscripts, which either crash int() or validate as another value.
# For the REASON (empty / malformed / bad check digit) use parse_id() in
# scripts/validate_id.py; this compact version only answers yes or no.
if not isinstance(id_number, str):
return False # an int has already lost its leading zeros
# Trim whitespace, a CSV line break, the BOM and LRM/RLM/ALM at the ENDS only.
# Inside the number a line break or bidi mark stays an error: bidi controls
# can make the screen show different digits from the ones stored.
EDGE = " \t\u00a0\r\n\u200b\ufeff\u200e\u200f\u061c"
SEPARATORS = " -\u2010\u2011\u2013\u2014\u202f\u00a0\u00ad\u200b\u200c\u200d\u2060\ufeff"
stripped = ''.join(c for c in id_number.strip(EDGE) if c not in SEPARATORS)
if stripped == '' or not re.fullmatch(r'[0-9]{1,9}', stripped):
return False # malformed input, NOT an invalid ID
id_str = stripped.zfill(9)
if id_str == '000000000': # passes Luhn but is never a real ID
return False
total = 0
for i, digit in enumerate(id_str):
# 1-based odd positions (indices 0,2,4,6,8) multiply by 1
# 1-based even positions (indices 1,3,5,7) multiply by 2
weight = 1 if i % 2 == 0 else 2
val = int(digit) * weight
if val > 9:
val = val // 10 + val % 10 # Sum digits if > 9
total += val
return total % 10 == 0
步骤 3:提供结果
对于有效 ID:说明该号码通过了校验位检查(这表示号码格式正确,并不能证明该号码已签发或属于此人),并且仅将基于前缀判断的类型标注为可能类型 对于无效 ID:报告无效,指出未通过的检查,并建议检查常见错误:
- 数字顺序颠倒
- 缺少/多出数字
- 校验位错误
步骤 4:生成测试 ID(用于开发)
用于开发和测试时,生成有效的测试 ID:
def generate_test_id(prefix: str = "") -> str:
"""Generate a valid Israeli ID number for testing."""
import random, re
if not re.fullmatch(r'[0-9]{0,8}', prefix):
raise ValueError("prefix must be 0-8 ASCII digits") # else an 11-digit "ID"
if prefix == '00000000':
raise ValueError("an all-zeros prefix can only yield 000000000")
while True:
base = prefix + ''.join(str(random.randint(0, 9)) for _ in range(8 - len(prefix)))
if base != '00000000': # would yield 000000000, which validation rejects
break
# Calculate check digit
total = 0
for i, digit in enumerate(base):
weight = 1 if i % 2 == 0 else 2
val = int(digit) * weight
if val > 9:
val = val // 10 + val % 10
total += val
check = (10 - (total % 10)) % 10
return base + str(check)
注意:生成的 ID 仅供测试使用。切勿将随机 ID 用作真实身份证明。通过校验位检查的生成号码仍可能是某个真实人士已签发的 ID;生成器无法判断这一点,因此请勿让任何人或第三方看到生成的号码(参见注意事项)。
示例
示例 1:验证 TZ
用户提问:“123456782 是有效的以色列 ID 吗?” 结果:运行算法,并说明有效或无效及其原因。
示例 2:代码实现
用户提问:“我需要用 JavaScript 验证以色列 ID。”
结果:使用 references/javascript.md 中经过测试的移植版本(该版本将 ID 保留为字符串,使用 padStart(9, '0') 补齐,并且只接受指定的分隔符和 ASCII [0-9]),并将 references/id-formats.md 中的黄金测试向量作为单元测试运行。不要自行编写移植版本:使用 Number() 丢弃前导零,以及使用 \D 删除字符,是 JavaScript 版本最常出错的两种方式。
示例 3:生成测试数据
用户提问:“我需要 10 个有效的公司号码用于测试。” 结果:生成 10 个以 51- 开头的有效 ID 用于测试。
附带资源
脚本
scripts/validate_id.py,用于验证、识别、格式化和生成以色列 ID 号码(Teudat Zehut、公司、amuta、合伙企业)。支持详细模式,逐步显示校验位计算过程;支持通过控制前缀批量生成测试 ID;也支持从任何 ID 号码识别类型。退出代码:0 表示有效,1 表示格式正确但校验位不通过,2 表示输入格式错误。validate --mask只会显示末 4 位,用于日志和 CI 输出。运行:python scripts/validate_id.py --help
参考资料
references/id-formats.md,介绍所有以色列身份证号码格式的规范,包括 Teudat Zehut、注册实体(完整的 50–59 号段:公司、上市公司、合伙企业、合作社、amuta、遗赠基金)及其签发机构、格式模式、附带演算示例的 Luhn 变体校验位算法、黄金测试向量表(有效、数字换位、差一、全零、漏掉前导零、格式错误)和常见验证错误。实现验证逻辑或排查校验位失败问题时请查阅。references/javascript.md,提供与 Python 脚本交叉验证的 JavaScript 移植版,以及 JavaScript 移植通常会出错的地方。用户需要在浏览器或 Node 中进行验证时请使用。
参考链接
- 人口与移民管理局,“申请身份证”(gov.il),申请 Teudat Zehut 的官方服务页面。旧主题页面(
/he/departments/topics/identity_card)现已返回 404。 - 公司管理局在线登记系统(justice.gov.il),用于查询公司、合伙企业和 amutot。合作社(前缀为 57)由独立的合作社登记官监管,因此这里可能查不到真实的 57 号号码。
- Kolzchut,《身份证、护照和旅行证件》,面向公民权利的维基中心,介绍身份证、护照和旅行证件,以及如何办理和补办。
- 《隐私保护法》第 13 号修正案(law.co.il 分析),2025 年修正案,对《隐私保护法》进行了全面修改。于 2025 年 8 月 14 日生效。
注意事项
- 以色列身份证号(Teudat Zehut)恰好为 9 位数字,并带有 Luhn(模 10)校验位。Agent 可能会生成随机的 9 位数字,但这些数字无法通过校验位验证。
- 少于 9 位的以色列身份证号必须在左侧补零。像
"12345678"这样的 ID 实际上是"012345678"。Agent 可能会去掉前导零,导致验证失败。 - 以色列身份证号不以日期编码。无法根据数字推断出生日期、年龄、出生年份或居留身份。受美国 SSN 直觉影响而训练的 Agent 往往会臆造这种假设。
- 不要根据个人 ID 的首位数字范围输入或拒绝该 ID。数字中没有记录在案的公民身份编码(常见的“按范围区分本地人、居民与外籍劳工”的说法只是传闻)。所有个人 ID,无论首位数字是什么,都通过相同的 Luhn 校验;应一律将其视为普通的 9 位 ID。
- PII / 隐私日志记录:绝不要在应用程序日志、错误消息、遥测或分析事件中记录未脱敏的以色列 ID。以色列《隐私保护法》已由第 13 号修正案全面修订(于 2025 年 8 月 14 日生效),发生严重安全事件时必须立即向隐私保护局报告(见下一项)。在非授权场景(调试 UI、支持工具、面向客户的收据)中显示 ID 时,最多只显示末尾 4 位,例如
xxxxx6782。持久化到非必要存储之前,应使用密钥保存在数据库之外的 HMAC-SHA256,或使用令牌化保险库。未加密的 ID 哈希并不能提供保护:整个可能值空间不到 10^9,校验位还会进一步缩小范围,因此只需几秒钟就能通过暴力破解还原整张表。 - 超出代码规范范围的隐私合规事项不在此处讨论。 隐私保护局正在执行第 13 号修正案:2026 年 7 月,在修正案生效后首次处以罚款(法定减额后为 256,000 NIS,罚款对象为一家医疗基金),原因是未按《信息安全条例》要求立即向隐私保护局报告严重安全事件;该局还表示,报告义务在事件为人所知时即产生。身份证号数据库很可能受这些条例约束。关于合规计划本身(安全级别、事件报告、是否必须任命隐私保护官),请使用
israeli-privacy-shield技能并咨询法律顾问;此验证器仅涵盖上述代码层面的规则。 - 个体经营者的企业编号不是 5x 开头的注册实体编号:个体经营者豁免登记证上的 osek 编号通常是经营者本人的 9 位 Teudat Zehut(在公司管理局注册时会分配 5x 开头的编号)。只接受 5x 开头编号的“企业编号”字段会拒绝个体经营者。凡是要求填写企业编号的地方,都应接受这两种格式;两者都通过相同的校验位检查。
- IDF 个人编号(mispar ishi)是由 IDF 签发的独立标识符,不在此技能的范围内。不要用平民身份证号的校验位算法处理它:本技能未说明其校验位规则,因此通过或失败的结果都没有意义。
000000000可以通过 Luhn 校验(其数字之和为 0,可被 10 整除),但它从来不是真实 ID。这是常见的哨兵值 / 空字段误判:空字符串或默认值为零的数值列经过左侧补零后,就会变成这个值。应明确拒绝全零值,不要仅凭“有效”结果就信任它。- 验证前去掉所有非数字字符,会导致误报。 这是看起来最自然的一行代码,却会让
62819482.1被验证为628194821。只移除指定的分隔符;其他内容一律视为格式错误输入,而不是直接删除。这就是验证器与过滤器的区别。 - 反转后的 ID 仍然能通过校验位检查。 权重 1、2、1、2、1、2、1、2、1 正反读都相同,因此,数字顺序被反转的 9 位 ID(例如从视觉顺序的 PDF、OCR 或旧系统中提取的号码)总是能通过验证:
287654321是123456782的反转形式,并且能通过校验。只有去掉前导零后的 8 位值通常无法通过。校验位无法检测反转,因此应将号码与用户看到或输入的内容进行比对,绝不要把 VALID 当作数字顺序正确的证明。出于同样的原因,绝不要丢弃双向覆盖或隔离字符(U+202A 至 U+202E、U+2066 至 U+2069):"\u202e287654321"在屏幕上显示为 123456782,但存储的值却是 287654321。 - 以色列 ID 最多为 9 位,因此应拒绝更长的输入,而不是让它因校验和不匹配而失败。
0000000018不是“无效 ID”,它根本就不是 ID;告诉用户 ID 无效,会让他们去找一个其实并不存在的输入错误。 - 生成的测试 ID 可能是真实用户的 ID。 通过校验位并不能说明该号码是否已被签发,而生成器也无法得知。绝不要让生成的号码进入会离开开发者本机的种子数据、暂存环境邮件、屏幕截图、文档或演示租户中。如果必须向他人展示测试数据,请使用已发布的示例
123456782,或使用一个故意无法通过校验位检查的号码。 - 你自己的 CLI 输出也是日志。 在终端中打印完整 ID 会使其进入 Shell 历史记录和 CI 日志。请使用
scripts/validate_id.py validate --mask,它只会打印末尾 4 位;--verbose会打印每一位,且不能与--mask同时使用。
故障排除
错误:“ID 看起来有效,但无法识别”
原因:校验位通过了,但该 ID 尚未签发。 解决方案:该算法只验证格式,不验证是否存在。只有签发机构才能确认某个号码是否已签发:对于公司、合伙企业和 amuta,请参阅参考链接中的公司管理局搜索。本技能未记录个人 ID 的公开查询方式。
错误:“经过范围/前缀筛选后,ID 未通过验证”
原因:上游筛选器根据首位数字的范围拒绝 ID(例如,将某个首位数字视为“非个人 ID”),或者 ID 原本以 8 位数字存储,导致前导零被丢弃,进而使校验位验证失败。
解决方案:所有个人 ID 都使用相同的 9 位 Luhn 校验,不受首位数字影响,而且不存在可靠的按范围映射状态的方法。验证前,先补齐前导零(zfill(9)),并移除任何首位数字范围白名单。请注意,以 5 开头的 9 位数字通常是已注册实体的号码,而非个人 ID。
错误:“长度不匹配/前导零被丢弃”
原因:电子表格、JSON 解析器或数值列丢弃了前导零(例如,012345678 作为整数存储后变成 12345678)。
解决方案:始终将 ID 存储为字符串。读取时,先在左侧补齐至 9 位,再运行校验:Python 使用 zfill(9),JS 使用 padStart(9, '0')。只有补齐后才拒绝。
错误:“输入无效,ID 中含有连字符或空格”
原因:用户粘贴了带格式的公司或 amuta 号码,例如 51-530820-3 或 58 012345 3。
解决方案:仅删除分隔符(空格、连字符和破折号、不换行空格和窄不换行空格、零宽字符),并且只修剪两端的空白字符、制表符、换行符和 LRM/RLM 标记(数字中间出现制表符或换行符属于格式错误),然后进行长度检查,再使用 zfill(9)。带格式的号码和原始数字形式必须通过相同的校验。
不要删除所有非数字字符。 re.sub(r'[^0-9]', '', id) 看起来效果相同,但事实并非如此:它会删除有问题的字符,而不是报告它们,因此 62819482.1 会变成有效的 ID 628194821,而 ""、"abc" 和 "!!!!!!!!!" 都会变成 000000000。请使用明确的分隔符集合,并将其他任何字符判定为格式错误。数字检测也不要使用 \d 或 str.isdigit():在 Python 中,两者都会匹配阿拉伯-印度数字,因此 re.sub(r'\D', '', id) 会让这些数字通过,随后被传入 int()。
错误:“无法区分空字段和无效 ID”
原因:验证器只返回一个布尔值,因此“用户留空了”“用户输入了字母”和“该 ID 的校验位不通过”都会返回 False,导致表单显示错误的消息。
解决方案:返回具体原因,而不只是结果。scripts/validate_id.py 使用退出码区分这三种情况:0 表示有效,1 表示格式正确但校验位不通过,2 表示输入格式错误;具体原因会输出到标准输出(MALFORMED - too long: 10 digits、MALFORMED - empty input、MALFORMED - contains characters that are not digits or separators: '.')。请根据退出码编写脚本,而不是根据文本。
错误:“输入有 9 位,但算法验证失败”
原因:常见原因是 ID 主体部分发生了数字调换或单个数字输入错误,而非校验位本身。另一个原因是该值为军方的 mispar ishi,这是独立的 IDF 标识符,本技能未记录其校验位(请参阅注意事项),因此验证失败并不能说明该标识符有误。
解决方案:请用户根据源文件重新输入 ID。如果仍然验证失败,但用户坚持认为 ID 正确,请建议通过其他渠道向签发机构核实;不要擅自“修正”校验位。