Python实现罗马数字转阿拉伯数字的输入校验问题咨询
罗马数字转阿拉伯数字校验逻辑漏洞说明
靠枚举非法子串做输入校验本质是「黑名单」思路,从根上就没法覆盖所有非法场景,你的代码目前有明确的可绕过漏洞,具体如下:
一、校验流程本身的逻辑bug
你现在的两层校验(非法子串校验、合法字符校验)都存在循环索引不重置的问题,用户第一次输入错误后重新输入的内容,不会重新走全量校验,直接漏判:
- 非法子串校验是按
not_allowed列表顺序逐个检查的,比如第一次输入LL触发了第一个非法项的拦截,用户重输时如果输入IIII,这时候循环已经走到not_allowed的后续索引,不会回头检查IIII这个非法项,直接放行。 - 合法字符校验的问题更明显:比如第一次输入
IA,校验到索引1的A非法触发重输,用户新输入AB,此时循环的索引i已经走到1,只会检查新字符串索引1的B,完全不会校验索引0的A是否合法。 - 空输入完全没做拦截,用户直接敲回车的话,代码会走到转换逻辑直接触发索引报错。
二、非法子串枚举遗漏的可绕过场景
你列的50多个非法串只覆盖了非常少的非法组合,以下非法输入都能直接绕过你的黑名单,被转换逻辑算出结果:
- 跨量级的非法减法组合:比如
MIM(你没枚举),按你的转换逻辑会算出1999,小于4000直接输出,但合法的1999写法是MCMXCIX,MIM根本不符合罗马数字规范。类似的漏网之鱼还有XIIX(会被算成18,合法写法是XVIII)、IXCM(会被算成1089,合法写法是MLXXXIX)、IIIX(会被算成8,合法写法是VIII)。 - 嵌套减法组合:比如
IXL(你虽然列了,但XCL、ICM这类串没列全),这类连续小值接大值的写法全是非法的,靠枚举根本列不完。 - 带特殊字符的输入:比如全角罗马数字
Ⅰ、带空格的I、带连接符的I-V,只要刚好卡到你校验循环的索引bug,就能绕过合法字符检查。 - 你虽然最后加了结果>=4000的拦截,但只要非法串算出的结果在1-3999之间,就会被当成合法值输出,比如前面说的
MIM、XIIX都属于这类。
三、为什么枚举非法子串的方案走不通
合法罗马数字的规则是固定有限的,但非法组合的数量是合法组合的上百倍,靠列黑名单永远有漏的。与其枚举所有错的写法,不如直接按罗马数字的白名单规则做校验,从根源上堵漏洞:
- 基础校验:输入不能为空,所有字符必须是
I/V/X/L/C/D/M7个合法字符,不满足直接判错 - 字符重复规则:
V/L/D三个代表5、50、500的字符,整个字符串里最多出现1次,绝对不能重复;I/X/C/M四个字符,连续重复不能超过3次 - 减法规则:只有当小值出现在大值左侧时才做减法,且必须同时满足:
- 做减法的小值只能是
I/X/C(V/L/D永远不能当被减数放在大值左边) - 小值和右侧大值只能差一个量级:
I后面只能接V/X,X后面只能接L/C,C后面只能接D/M,其他所有小值在大值左边的情况全是非法 - 做减法的小值不能连续出现(比如
IIV、IIX都非法)
- 做减法的小值只能是
- 最后校验转换结果在1-3999区间即可
四、现有代码的其他小问题
- 错误提示太笼统,用户不知道自己输入错在哪
- 转换逻辑没有对连续多个小值接大值的情况做拦截,只要漏过黑名单就会算出错误结果
- 重输入逻辑没有做循环重置,用户每次重输都可能漏过前面的校验项
内容的提问来源于stack exchange,提问作者ItsPeterHU
相关产品推荐
相关产品推荐

