关于12306如何检测身份证号码正确性的分析

Posted by zhang on

最近有媒体爆出12306可以用假的身份证号码在12306.cn上买票的新闻。所以对12306.cn如何验证身份证号码真伪产生了兴趣。

怎么验证呢,当然离不开chrome大婶和火狐妹子了,

首先修改下个人信息,随便打上一个身份证号码,基本上你打的一定是不对的,所以他会提示你:“请正确输入18位的身份证号!

我们来找下这个提示从哪里来的呢?

我找啊找,找到了“formCheck.js”,而且没有被压缩,可视性不错,

 在这里边,我发现 了这么一段

function validateSecIdCard(value){
	var iSum = 0;
	var sId= value;
	var aCity = {
		11 : "北京",
		12 : "天津",
		13 : "河北",
		14 : "山西",
		15 : "内蒙",
		21 : "辽宁",
		22 : "吉林",
		23 : "黑龙",
		31 : "上海",
		32 : "江苏",
		33 : "浙江",
		34 : "安徽",
		35 : "福建",
		36 : "江西",
		37 : "山东",
		41 : "河南",
		42 : "湖北",
		43 : "湖南",
		44 : "广东",
		45 : "广西",
		46 : "海南",
		50 : "重庆",
		51 : "四川",
		52 : "贵州",
		53 : "云南",
		54 : "西藏",
		61 : "陕西",
		62 : "甘肃",
		63 : "青海",
		64 : "宁夏",
		65 : "新疆",
		71 : "台湾",
		81 : "香港",
		82 : "澳门",
		91 : "国外"
    };
	if (!/^d{17}(d|x)$/i.test(sId)) {
		return false;
	}
	sId = sId.replace(/x$/i, "a");
	//非法地区
	if (aCity[parseInt(sId.substr(0, 2))] == null) {
		return false;
	}
	var sBirthday = sId.substr(6, 4) + "-" + Number(sId.substr(10, 2))
			+ "-" + Number(sId.substr(12, 2));
	var d = new Date(sBirthday.replace(/-/g, "/"));
	//非法生日
	if (sBirthday != (d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d
			.getDate())) {
		return false;
	}
	for ( var i = 17; i >= 0; i--) {
		iSum += (Math.pow(2, i) % 11) * parseInt(sId.charAt(17 - i), 11);
	}
	if (iSum % 11 != 1) {
		return false;
	}
	return true;
}

function validateFirIdCard(value){
	var iSum = 0;
	var sId;
	var aCity = {
		11 : "北京",
		12 : "天津",
		13 : "河北",
		14 : "山西",
		15 : "内蒙",
		21 : "辽宁",
		22 : "吉林",
		23 : "黑龙",
		31 : "上海",
		32 : "江苏",
		33 : "浙江",
		34 : "安徽",
		35 : "福建",
		36 : "江西",
		37 : "山东",
		41 : "河南",
		42 : "湖北",
		43 : "湖南",
		44 : "广东",
		45 : "广西",
		46 : "海南",
		50 : "重庆",
		51 : "四川",
		52 : "贵州",
		53 : "云南",
		54 : "西藏",
		61 : "陕西",
		62 : "甘肃",
		63 : "青海",
		64 : "宁夏",
		65 : "新疆",
		71 : "台湾",
		81 : "香港",
		82 : "澳门",
		91 : "国外"
	};
	//如果输入的为15位数字,则先转换为18位身份证号
	if (value.length == 15)
		sId = idCardUpdate(value);
	else
		sId = value;
	if (!/^d{17}(d|x)$/i.test(sId)) {
		return false;
	}
	sId = sId.replace(/x$/i, "a");
	//非法地区
	if (aCity[parseInt(sId.substr(0, 2))] == null) {
		return false;
	}
	var sBirthday = sId.substr(6, 4) + "-" + Number(sId.substr(10, 2))
			+ "-" + Number(sId.substr(12, 2));
	var d = new Date(sBirthday.replace(/-/g, "/"));
	//非法生日
	if (sBirthday != (d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d
			.getDate())) {
		return false;
	}
	for ( var i = 17; i >= 0; i--) {
		iSum += (Math.pow(2, i) % 11) * parseInt(sId.charAt(17 - i), 11);
	}
	if (iSum % 11 != 1) {
		return false;
	}
	return true;
}
function idCardUpdate(_str) {
	var idCard18;
	var regIDCard15 = /^(d){15}$/;
	if (regIDCard15.test(_str)) {
		var nTemp = 0;
		var ArrInt = new Array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8,
				4, 2);
		var ArrCh = new Array(’1’, ’0’, ’X’, ’9’, ’8’, ’7’, ’6’, ’5’, ’4’, ’3’,
				’2’);
		_str = _str.substr(0, 6) + ’1’ + ’9’ + _str.substr(6, _str.length - 6);
		for ( var i = 0; i < _str.length; i++) {
			nTemp += parseInt(_str.substr(i, 1)) * ArrInt[i];
		}
		_str += ArrCh[nTemp % 11];
		idCard18 = _str;
	} else {
		idCard18 = "#";
	}
	return idCard18;
}

不知道有没有注意前两段是检查了相应的城市、生日等信息。那么最后一段是干什么的呢?

这我们需要从身份证号的构成来分析了。

<中华人民共和国国家标准GB 11643-1999《公民身份号码》中规定:公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。

18位数字组合的方式是:

1 1 0 1 0 2 Y Y Y Y M M D D 8 8 8 X
地址码 出生日期码 顺序码 校验码

  • 地址码指的是公民常住户口所在县(市、旗、区)的行政区划代码,如120224是天津市宝坻区。
  • 出生日期码表示公民出生的公历年(4位)、月(2位)、日(2位)。
  • 顺序码是给同地址码同出生日期码的人编定的顺序号,其中奇数分配给男性,偶数分配给女性。
  • 最后一位是校验码,这里采用的是ISO 7064:1983,MOD 11-2校验码系统。校验码为一位数,但如果最后采用校验码系统计算的校验码是“10”,碍于身份证号码为18位的规定,则以“X”代替校验码“10”。
  •     这样,就出现了黄牛使用网上的一些身份证号码生成器就能生成符合条件的身份证号码从而注册大量的号码来刷票。 面对这种情况,通过本地的相关算法来判断身份证号码是否合法似乎已经无法解决。但是如果使用公安部提供的身份证信息查询的服务似乎也是不太靠谱,毕竟能github都能被拖垮,公安部的那家事业单位的服务器很难顶的住啊……万一出问题,问题就可就大了。

    #About Me

    张小璋,野蛮生长成世界500强企业供应链金融产品经理的法语毕业生。微信公众号:张小璋碎碎念(ID: SylvainZhang )。
    一直在互联网金融公司从事产品经理工作并负责互联网金融产品线,深耕互联网金融和区块链领域。「PMCAFF」、「人人都是产品经理」专栏作家、「PmTalk」签约作家。