百度web登录协议分析

源码将发布于个人公众号中,欢迎关注与投稿

文章目录

  • 前言
  • 1. 环境与软件
  • 2. 实现效果
  • 3. 分析过程
    • 3.1 正常贴吧首页登录流程
    • 3.2 账号框失去焦点
      • 请求头分析
      • 请求参数分析
      • 返回值分析
    • 3.3 点击提交按钮

前言

本文为百度登录协议分析,由于协议分析常常需要试错故本文书序顺序为作者分析顺序(所以你将看到分析完某一步的时候,还要回退一步分析别之前接口)。
代码实现采用Java语言与Rhino框架实现。
java模拟部分登录接口源码
源码:
https://github.com/fanmingyi/tiebaWebProtocol

1. 环境与软件

  1. Chrome
  2. MACOS
  3. Charles
  4. IDEA
  5. WebStorm

2. 实现效果

  1. 使用Java代码实现协议登录。
    为简化本博文数量,不做验证码分析,和邮箱验证步骤。(较为简单有兴趣的同学可私下继续分析)

实现时间:2019年05月27日

3. 分析过程

3.1 正常贴吧首页登录流程

  1. 打开贴吧首页地址:https://tieba.baidu.com/#
  2. 点击主页右上角登录按钮
  3. 在弹出窗口输入账号
  4. 验证码输入
  5. 输入密码
  6. 点击登录按钮
  7. 登录异常邮箱验证

图3.1 正常登录流程1

图3.1 正常登录流程2

3.2 账号框失去焦点

打开Charles处理好SSL与代理相关配置,Chrome 打开开发者控制台(F12按键)观察数据请求。

图3.2 账号失去焦点

当用户输入完账号时,会自然而然的把焦点移除到密码框或者其他区域。当账号输入框失去焦点时会触发账号检测接口,判断账号是否需要输入验证码。

图3.2显示账号框失去焦点时访问多个接口,这里只挑关键接口描述。其他接口并不影响整体流程

  1. 接口地址
    https://passport.baidu.com/v2/api/

  2. 请求方式
    GET

  3. 请求参数

    1. logincheck
    2. token
    3. tpl
    4. apiver
    5. tt
    6. sub_source
    7. username
    8. loginversion
    9. dv
    10. traceid
    11. callback
  4. 请求头

图3.2 账号失去焦点请求头
  1. 返回示例数据

无验证码

bd__cbs__5vdw2x({
	"errInfo": {
		"no": "0"
	},
	"data": {
		"codeString": "",
		"vcodetype": "",
		"userid": "",
		"mobile": "",
		"displayname": "",
		"isconnect": ""
	},
	"traceid": ""
})

有验证码(不做过多分析)

bd__cbs__61uvo4({
	"errInfo": {
		"no": "0"
	},
	"data": {
		"codeString": "njG3706f5779ef5f59f029a159a5b01f98a31ee5b066a0189db",
		"vcodetype": "47d9xtxhYYpw2VQmiJ7ol0uMKom+0ekdbPlIT9wX876Qrw3ZOFONQJunv5n2OB6Cw97fKMoTZIzOoG\/FHgtXH+0174AjRSgNqRUb",
		"userid": "",
		"mobile": "",
		"displayname": "",
		"isconnect": ""
	},
	"traceid": ""
})

请求头分析

从“图3.2 账号失去焦点请求头”可知此请求需带带上cookies。细看图cookiesvalue数值为BAIDUID,FG等。

依据WEB前端知识可知,cookies两种生成方式分别为:

  1. js主动生成
  2. 访问网页时服务端返回Set-Cookies请求头

当本地之前已存储cookies时,如访问同源域名将自动带上之前cookies。关于浏览器同源策略不在此做过多讲解

根据优先级我们应先分析第二种情况产生了cookies 。从正常登录流程可知登录需要打开贴吧首页,所以试探性检测贴吧首页返回的cookies信息

打开贴吧首页请求头信息如下:

图3.2 贴吧首页返回请求头

从上图"图3.2 贴吧首页返回请求头"可知贴吧首页会返回一些必要的cookies用于下一步请求

请求参数分析

采用对比法分析一些固定数值(判断固定值最好开两个不同浏览器查看结果进行比对,因为作者对于抓包有点点小经验所以一般都能看出,所以本文没有采用多开不同浏览器的策略)。

输入账号"12345"第一次模拟账号框失去焦点拦截但表单参数如下:

参数名 数值
logincheck
token a51a82e34350f43bae9007aac62fff9f
tpl tb
apiver v3
tt 1557814422439
sub_source leadsetpwd
username 12345
loginversion v4
dv tk0.1105670664575046915578…
traceid
callback bd__cbs__61uvo4

输入账号"18675969904"第一次模拟账号框失去焦点拦截但表单参数如下(手机号乱打的不是我的)

参数名 数值
logincheck
token a51a82e34350f43bae9007aac62fff9f
tpl tb
apiver v3
tt 1557814395510
sub_source leadsetpwd
username 18675969904
loginversion v4
dv tk0.110567066457504…
traceid
callback bd__cbs__5vdw2x

综上对比可预估参数信息

参数名 说明
logincheck 空数据,应该用于区分接口
token 尚不明确
tpl 固定值
apiver 固定值
tt 时间戳
sub_source 固定值
username 账号
loginversion 固定值
dv 尚不明确
traceid
callback JSONP跨域用的,可以不传字段名与数值

综上所述需要分析的字段

  1. token
  2. dv

(1)token分析

假设我们请求表单的token数值为fdc7c3bb0d7360daa1261a89fe3c487c
直接在浏览器中直接搜索fdc7c3bb0d7360daa1261a89fe3c487c看看结果如何

图3.2 token搜索结果1

图3.2 token搜索结果2

可以明显的看到这个token是从另一个接口而来,鼠标移动到"图3.2 token搜索结果2"中的顶部标签栏可以看到请求相关信息或者通过Charles搜搜关键字查找到对应URL

获取token接口如下:

  1. URL
    https://passport.baidu.com/v2/api/
  2. 请求方式
    GET
  3. 请求头
图3.2 获取token请求头
  1. 请求参数
参数名 数值
getapi
tpl tb
apiver v3
tt 1559012718154
class login
gid 1F8687F-6BD5-48BD-A0FB-5211E3951305
loginversion v4
logintype dialogLogin
traceid
callback bd__cbs__2kfado
  1. 返回请求头
图3.2 获取token返回的请求头
6. 返回的消息体
bd__cbs__2kfado({
	"errInfo": {
		"no": "0"
	},
	"data": {
		"rememberedUserName": "",
		"codeString": "",
		"token": "8ed65d8c86ebecf334223c2d972cb0d6",
		"cookie": "1",
		"usernametype": "",
		"spLogin": "rate",
		"disable": "",
		"loginrecord": {
			'email': [],
			'phone': []
		}
	},
	"traceid": ""
})

获取token接口分析

  1. 请求时需要带上存储的同源cookies(推荐代码实现的时候每一次都存储返回的cookies,调用新接口时带上即可)
  2. 返回值含有设置cookies选项需注意
  3. 参数含分析
参数名 数值
getapi 用于区分接口,空values
tpl 固定值
apiver 固定值
tt 时间戳
class 固定值
gid 尚不明确
loginversion 固定值
logintype 固定值
traceid
callback JSONP相关可不传

如法炮制在浏览器搜索gid数值,看看有没有结果。
结果如下:

图3.2 gid搜索

从搜索结果可知gid应该js动态生成。对于此类型的参数一般需要从源码分析。直接从Chrome开发控制台中查看网络发起者源码位置,如下图

图3.2 token接口触发源

图3.2 token接口调用栈

我们断点到调用栈最后一个函数如下:

图3.2 token接口分析1

根据上图分析可以得到,当调用最后一个函数时已经传入拼接好的URL地址所以包含gid,但是没有看到gid的生成代码,因此需要向上分析函数。

此处具体函数调用栈分析代码省略部分,我们直接看最终向上寻找gid生成代码处。

图3.2 gid源码分析1

图3.2 gid源码分析2

图3.2 gid源码分析3

“图3.2 gid源码分析1”发了重要gid代码提取处,但不清楚这个gid是在文件哪个部分生成。所以复制“图3.2 gid源码分析1”源码到WebStorm中分析得到后两张图结果。

致辞gid生成算法分析完成

function generatedGid() {
                        return "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(e) {
                            var t = 16 * Math.random() | 0
                                , n = "x" === e ? t : 3 & t | 8;
                            return n.toString(16)
                        }).toUpperCase()
                    }

gid分析完成后token数值也就清晰可见。

最后还差一个参数尚未分析dv

(2)dv分析

经过前面的分析估计你已经忘记dv是啥了。dv是账号框失去焦点时进行账号检测所需递交的参数之一。

根据前文的介绍相信对作者的分析方式已经有所了解,总结来说就是:
(1)对比多次数据判断数据是否是固定值。(记得删除cookies和更换浏览器进行比对)如果是固定值那么不做分析
(2)直接在Chrome搜索对应values,此处可以判断参数是否通过jsonp等方式获取,或者直接写死在某一个html或js中
(3)查看cookies是否存在对应参数 (本文没有这么做,因为大厂很少把cookies取出在放入参数中)
(4)Chrome查看网络发起源,根据调用栈分析,如果发现重要关键步骤,复制js源码到WebStorm进行交叉引用分析

这处直接给出dv生成算法

/*
* 加密算法
* */
initEncyptArray.prototype = {

    'int': function (e) {
        return encryptInt(e, this.dict)
    },
    iary: function (mArray) {
        for (var t = '', n = 0; n < mArray.length; n++) {
            var r = encryptInt(mArray[n], this.dict2);
            t += r.length > 1 ? r.length - 2 + r : r
        }
        return t
    },
    bary: function (mArray) {
        for (var t = 0, n = {}, r = 0; r < mArray.length; r++)
            mArray[r] > t && (t = mArray[r],
                n[mArray[r]] = !0);
        var o = parseInt(t / 6);
        o += t % 6 ? 1 : 0;
        for (var a = '', r = 0; o > r; r++) {
            for (var i = 6 * r, d = 0, c = 0; 6 > c; c++)
                n[i] && (d += Math.pow(2, c)),
                    i++;
            a += this.dict[d]
        }
        return a
    },
    str: function (e) {
        for (var t = [], n = 0; n < e.length; n++) {
            var r = e.charCodeAt(n);
            r >= 1 && 127 >= r ? t.push(r) : r > 2047 ? (t.push(224 | r >> 12 & 15),
                t.push(128 | r >> 6 & 63),
                t.push(128 | r >> 0 & 63)) : (t.push(192 | r >> 6 & 31),
                t.push(128 | r >> 0 & 63))
        }
        for (var o = '', n = 0, a = t.length; a > n;) {
            var i = t[n++];
            if (n >= a) {
                o += this.dict[i >> 2],
                    o += this.dict[(3 & i) << 4],
                    o += '__';
                break
            }
            var d = t[n++];
            if (n >= a) {
                o += this.dict[i >> 2],
                    o += this.dict[(3 & i) << 4 | (240 & d) >> 4],
                    o += this.dict[(15 & d) << 2],
                    o += '_';
                break
            }
            var c = t[n++];
            o += this.dict[i >> 2],
                o += this.dict[(3 & i) << 4 | (240 & d) >> 4],
                o += this.dict[(15 & d) << 2 | (192 & c) >> 6],
                o += this.dict[63 & c]
        }
        return o
    }

}


/**
 * 初始化一个加密数组
 * @param token
 */
function initEncyptArray(token) {
    var encyptArray = [[48, 57], [65, 90], [97, 122], [45, 45], [126, 126]]
        , dict = expandArray(encyptArray)
        , dict2 = expandArray(encyptArray.slice(1));
    token && (dict = funReoder(dict, token),
        dict2 = funReoder(dict2, token)),
        this.dict = dict,
        this.dict2 = dict2
}

/**
 * 重排序encryptArray
 * @param encryptArray
 * @param token
 * @returns {*}
 */
function funReoder(encryptArray, token) {
    for (var tokenArray = token.split(''), r = 0; r < encryptArray.length; r++) {
        var encryptArrayIndex = r % tokenArray.length;
        encryptArrayIndex = tokenArray[encryptArrayIndex].charCodeAt(0),
            encryptArrayIndex %= encryptArray.length;
        var encryptArrayChar = encryptArray[r];
        encryptArray[r] = encryptArray[encryptArrayIndex],
            encryptArray[encryptArrayIndex] = encryptArrayChar
    }
    return encryptArray
}

/**
 * 展开一个二维数组
 * @param mExpandArray
 * @returns {Array}
 */
function expandArray(mExpandArray) {
    for (var arrayRel = [], n = 0; n < mExpandArray.length; n++)
        for (var r = mExpandArray[n][0]; r <= mExpandArray[n][1]; r++)
            arrayRel.push(String.fromCharCode(r));
    return arrayRel
}

/**
 * 转化某个int类型字符串
 * @param encyptIntStr
 * @param encryptDictitonArray
 * @returns {*}
 */
function encryptInt(encyptIntStr, encryptDictitonArray) {
    var rel = '';
    var encyptInt = Math.abs(parseInt(encyptIntStr));
    if (encyptInt) {
        for (; encyptInt;) {
            rel += encryptDictitonArray[encyptInt % encryptDictitonArray.length],
                encyptInt = parseInt(encyptInt / encryptDictitonArray.length);
        }
    } else {
        rel = encryptDictitonArray[0];
    }


    return rel
}


/**
 *  用加密算法拼接信息
 * @param info
 * @param token
 * @returns {string}
 */
function joinBaseBrowserMsg(info, token) {

    var encyptObj = new initEncyptArray(token);

    var broserInfo = {
        flashInfo: 0,
        mouseDown: 1,
        keyDown: 2,
        mouseMove: 3,
        version: 4,
        loadTime: 5,
        browserInfo: 6,
        token: 7,
        location: 8,
        screenInfo: 9
    };

    var resultArray = [encyptObj.iary([2])];


    for (var infoProperty in info) {

        var infoPropertyEntity = info[infoProperty];

        if (void 0 !== infoPropertyEntity && void 0 !== broserInfo[infoProperty]) {
            var temp;

            if ('number' == typeof infoPropertyEntity) {
                (temp = infoPropertyEntity >= 0 ? 1 : 2, infoPropertyEntity = encyptObj.int(infoPropertyEntity));
            } else {
                if ('boolean' == typeof infoPropertyEntity) {
                    (temp = 3, infoPropertyEntity = encyptObj.int(infoPropertyEntity ? 1 : 0))
                } else {
                    if ('object' == typeof infoPropertyEntity && infoPropertyEntity instanceof Array) {
                        (temp = 4, infoPropertyEntity = encyptObj.bary(infoPropertyEntity))
                    } else {
                        (temp = 0, infoPropertyEntity = encyptObj.str(infoPropertyEntity + ''))
                    }
                }
            }

            infoPropertyEntity && resultArray.push(encyptObj.iary([broserInfo[infoProperty], temp, infoPropertyEntity.length]) + infoPropertyEntity);

        }
    }
    return resultArray.join('')
}



var token = 'tk' + Math.random() + (new Date).getTime()



var info = {
    "mouseDown": "",
    "keyDown": "",
    "mouseMove": "",
    "version": 26,
    "loadTime": (new Date()).valueOf(),
    "browserInfo": "3,2,74",
    "token": token,
    "location": ",undefined",
    "screenInfo": "23,0,1904,44,1920,1080,1920,1920,992"
}

var result = token + '@' + joinBaseBrowserMsg(info, token)

上诉result便是dv生成结果

返回值分析

bd__cbs__5vdw2x({
	"errInfo": {
		"no": "0"
	},
	"data": {
		"codeString": "",
		"vcodetype": "",
		"userid": "",
		"mobile": "",
		"displayname": "",
		"isconnect": ""
	},
	"traceid": ""
})

有验证码(不做过多分析)

bd__cbs__61uvo4({
	"errInfo": {
		"no": "0"
	},
	"data": {
		"codeString": "njG3706f5779ef5f59f029a159a5b01f98a31ee5b066a0189db",
		"vcodetype": "47d9xtxhYYpw2VQmiJ7ol0uMKom+0ekdbPlIT9wX876Qrw3ZOFONQJunv5n2OB6Cw97fKMoTZIzOoG\/FHgtXH+0174AjRSgNqRUb",
		"userid": "",
		"mobile": "",
		"displayname": "",
		"isconnect": ""
	},
	"traceid": ""
})

如果是有验证码类型的后面自己再简单分析下 接入打码平台即可。关于验证码作者会出东方财富的协议分析讲解打码。

3.3 点击提交按钮

图3.3 登录触发的网络协议

点击登录后触发四个协议。

  1. 获取RSA密钥,用户第三部加密密码
  2. 提交用户轨迹到服务器,服务器返回一些重要信息用于第三部提交
  3. 使用RSA密钥加密 并配合第二部参数提交
  4. 根据第三部的返回值跳转对应地址或者提示账号密码错误(这块不做分析了 意义不大)

后文请求接口的cookies返回和提交不再做过多讲解,一句话就是把以前的接口返回的cookies带上,和把新请求的cookies保存合并。

(1)获取RSA密钥接口分析

URL:
https://passport.baidu.com/v2/getpublickey
请求方式:
GET
请求参数:

参数名 数值
token 上文有讲解token 的获取
tpl 固定值
apiver 固定值
tt 时间戳
gid 上文有讲解
loginversion 固定值
traceid js计算生成
callback JSONP相关可不传

返回值

{
	"errno":'0',
	"msg":'',
	"pubkey":'-----BEGIN PUBLIC KEY-----	\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1m+0pgXdwA3MXL+21PsURrNYC\nJgwEMqx1Fib52dGSh3yMjwAvFgGdC7AqmF3LElYuA8eRCIwlwzQ3RIAnWd+4uihj\nd07Iz5d4NMaCbkjQFhTpYQDRzfJHaIam9kaCwXeIP32hy6JXvAJmYrowy5ZbvTAF\n1tf\/VCJjqO7TUkkAFQIDAQAB\n-----END PUBLIC KEY-----\n',
	"key":'KcoJV7Cr4Btbig6aaDJa0YfEAb72dp2F',   
 	"traceid": "16F55C01"
}

示例:
https://passport.baidu.com/v2/getpublickey?token=6dd25c0c14c43f75f8b4d5225064b213&tpl=tb&apiver=v3&tt=1559136918602&gid=7131BB0-D111-439D-9C75-9CD2EB855501&loginversion=v4&traceid=16F55C01&callback=bd__cbs__9umh06

上面就差traceId我们不知道怎么生成了,此处直接给出算法

function createHeadID() {

    var t = (new Date).getTime() + getRandom().toString()
        , n = Number(t).toString(16)
        , i = n.length
        , s = n.slice(i - 6, i).toUpperCase();
    return s;
}


function getRandom() {
    return parseInt(90 * Math.random() + 10, 10)
}

createHeadID()+"01"

(2)提交用户轨迹到服务器

URL:
https://passport.baidu.com/viewlog
请求方式:
GET
请求参数:

参数名 数值
ak 1e3f2dd1c81f2075171a547893391274(定值)
as 2283e880(定值)
fs 计算生成
v 随机数
callback JSONP相关可不传

返回值

{"code":0,
"data":{
"tk":"6571L4PexHC2mg5aMjl2hgiywV8fkmyJhToo9rGuiiswaRjimqr5bAc9eDG8ss\/ltjVr",
"as":"2283e880",
"ds":"iiDK6LRWXfVqYSyyx1Cw2cgDrtgEXlE\/ZUC3c9+t6umTjBOy4Za9kO+BctvtqpLnn3Okj0lrS70L5KO5Cm8dG62GZpoKBBqLMx8N9EOpQjarQ\/6FMJOjoW8ma1wAClGQGMiv2wb6e\/nwIPoxuXZmTaRMcheTXTA1MUmZ4KuO89Ebjuk5n\/S2LoNPqq9t\/eMgn\/4rDHLeoTQFJqwBs2LlioZzTD6cAJcHSmVtOoLt1cJkz08FmmXaK3z\/ZI+TxwLEpL7PuoIOLQbZ05F0Nj8FmzE98vFam4Vv2Js1opuXjUgIwBaGUbpSEf4I8zYKjuT1BfK5f9VVwm3\/hdGSfdDT8WvTK5a\/NVleIxydSejZCLi8i1YhYUU7AkOtlSJQFOgMui5MNC+Zuy68vKVPURnuPlR82F5H+DNMvxETRK2VLBs7TGqo2OVf1k5ReaVC+kcr8EileT1BnWRNoq9LZ6\/waRtJ5Ew03XVoYJXnI1X1m\/ei8Gk4gDm\/0655Z7s3QGrdF07cI8fMUmj9Z5j2iCH6UldSKvewmqtAoCNFlHp3yFEfa7YAftqS1Ub5skqK0CrMvc0it0ztJtz1x+7R1\/F6vMrury6jzyHAf1Arp4uquKiqV85llaAbFYDJg\/IvYAtE0zPfYCFPAmoTlKvqXOzKnzyFSeRmMLGp+fEoGYhTmEVzlU\/BFT81V\/4sdIexkhHvdlKDzcc6kK65T5ZBGa4xsnY++4sZHTtGHooyf1fBu+gH1j9\/0c59Vczg8Lo6is2PrUXnM4e6f1dMgDeL9Dls3VrxFI6nKrb8G1bUPNSfQWre+HT63X31TZ1Z9S7soxx9JhXobtgM\/bqnBFinhtqpbDHXEWGkVSKR5bKuiXgwRGREs2m+il5yFbuIip9M3LYUnryU240427CSYurNSz3EKB5ncLXTgM++loRdTKMZtzJC8nYFhogldtRuaLY34wJvAo66IhCiaKLIohP3oHf32dTHVvZ3LHDjIfE6TNTAiq8FQYDSJT6xBqs6B0RyDf94o0WBkrqoyqt7yf\/lAY\/ALRX7N94x9uByTM2BLhnYByXMTIrC5co1JMgOPLkgooJt2uxtkzGZ9Michi0EWtYCNe+zOg9vwO4EHXwuUi7fhcHGHy5BD7EnQcTn+KoSzDuxV2aSVWUVglXPiEGobrKa0tssYm6Hbk8IuBrN8GgUg8frfWtFK1LCGzsWr78z7C+etARXfmxYo4XC8ri0myBTdCuoVspZ5h1UHvn5sgdS0tPB6lYh+zzCjVpTb5TU90le3Rlzr5ccj1p8Kn8GHYi24\/ZMTjl9IcuypsZZ3tIIpQadVtOePs6U2nTmyGeDbDEtgIZYiIxtjYQPhcEBzNGZls6OQ9uDDM1BgoIh9WUSv0M="
}

示例:
https://passport.baidu.com/viewlog?ak=1e3f2dd1c81f2075171a547893391274&as=2283e880&fs=6Y20xog7DXa710pcCvK08tNqzM5XIxpA6moW1MmZ8oEpZ38VgWP0YfqaJUUQcK4qjh%2F%2BpZbJGkZQfdpJjvRGsL1eNis1pE%2FCkbwu9eEs5NDV5P1gwRKQq7ruEhJrtbIwZuQY5Ypda6ZFVyeZV3ceW%2Fk7ZEUwcEEGpl3zTk3R31Wg7wrAdu6hLLH9eFwH21BDM25%2Fls2dkBWUOqsnKiYnTMWlIniPxw9ONginb%2FjH495SqFNYyU4%2FGZIXquA%2BNEa2ccUdsSFub1fSnptNdHDsBEcEo9BHj90J9uJQXDUp5NeTNhq0agSfiD8%2BZ1%2BWw2KayaqMyKf5t8g4CmDzh0WhCAEmGc%2FZGXhHFwYc8aTsqeMKFGnkgU04DRAE5uwDxppjeXN5n5OJvR1KMh9GDezRxwzQxAPk9j1wyYZQW9tWPK6IgBsAYBiAvNjPakPkSsNsEE184CoS9YoKZ3yy4xwd6ozh5nQ5VXKa38Mbn5tFiNxznWiLICYRgD80853aZNQoay36zRIOwxTPIuumwhRrKIwme%2F2xFyZrvAwBOGRGT8LxZ0UMTtgTYBuTLufhLsDMN8DQnEm1Sz75HHx0q78zRxQMJ%2FKJ9fX4R6urvykOmM4TLGgCI%2B7zASxeIId%2Fn9YA6G2mmQQd6MEYO4dFXL8X0eBtmeSSehYEL%2Fwb0eDe5Jd6NxzP8OldCl22TpDn%2Bjdz6xzK3JmlSfMRT4yqJq468iN%2BAL50ULsTJoWsEU0IZGLwi1SeB7ZzSt7mfBiZ9KD3%2B2alnJp5kHqvO89XvRRHL3YUIqimGQXmGl86qN7Gg%2BXd4r5iclJTZdCvmH5G%2Fyb7v93PG6iEsSFfmSTxDF6Q%2BobP5jcmitDFcZUdIjFfjBzyMt858IHa1ERu5grQwPDp%2BnQMiU6vAVuhsMrLLfmWcIUgXJFSyZ%2BdBzldZYd6xgEFYx%2F%2FVCvmnTMVTOrN60v0OzEJsTgf%2FAB3rLUsWRp8DapAEXGVOfyTYRVyhfQ7uaOFov%2BNDx6F53Rpx6h63TyUNjFC%2FUmashDvMO1wLku%2F6c0bE1i80yhpiYBDZko7pM%2B4VSp7G0LxHFaZq8dIvswTrQ0CE6A%2BY7D7w%2B7YhXEliFObLgrLWVXDinQd6Erj1seGcYc5PVlb2fISu%2FdR%2BrRriPYhUROU1pgj4DIwwL%2F9TU3JvOHmPg6ug0MEn%2FIKn0xTTnQt66Q9cR0vSl%2Bs4d0zUY7G%2FHAVoHeQiiXlurtNqqvcLaB29laqUQQnYdvRicHQg%2Bv95q8a%2BDn%2F0AqmWlgHNqLr6QwsLYA4ibcpv8v9Z%2Fg9Rng7x7MbClvesLLpagdwM3L6Q0Sis%2BN4izM6REB%2BIoVfhMjfxg2HSI89Cg91SQ%3D%3D&callback=jsonpCallbackb5057&v=1996

fs参数算法:
已去除windows,docment函数,可以直接使用js生成fs(Java可以使用Rhino框架,Python使用Pyv8)
fs参数算法js源码
https://github.com/fanmingyi/tiebaWebProtocol/blob/master/src/main/resources/baiduEncyptFS.js

请自信查看上诉最后几行即可,本作者使用Rhino 框架运行js所以注释了var encypteText="" (Rhino支持java层声明一个js对象)。
encypteText对象是什么?encypteText是一个json字符串包含用户的一些点击和鼠标移动轨迹

   //当前时间戳
        var time = System.currentTimeMillis()
		//将上面时间戳带入字符串并少许修改时间。比如:${time - 552}就是讲当前时间戳加上500毫秒返回在凭借字符串,可自行更换你所熟系的语言生成。此处为kotkin语言
        var msg =
            """{"cl":[{"x":883,"y":150,"t":${time}},{"x":918,"y":234,"t":${time + 539}},{"x":898,"y":63,"t":${time + 1508}},{"x":904,"y":30,"t":${time + 1872}}],"mv":[{"fx":1008,"fy":20,"t":${time - 1293}},{"fx":946,"fy":246,"t":${time - 552}},{"fx":962,"fy":194,"t":${time - 395}},{"fx":915,"fy":151,"t":${time - 238}},{"fx":883,"fy":150,"t":${time + 160}},{"fx":916,"fy":225,"t":${time + 318}},{"fx":918,"fy":235,"t":${time + 911}},{"fx":897,"fy":250,"t":${time + 1067}},{"fx":901,"fy":128,"t":${time + 1225}},{"fx":898,"fy":63,"t":${time + 1690}},{"fx":904,"fy":31,"t":${time + 2095}},{"fx":906,"fy":46,"t":${time + 2267}},{"fx":909,"fy":59,"t":${time + 2424}},{"fx":914,"fy":71,"t":${time + 2582}},{"fx":914,"fy":76,"t":${time + 3175}},{"fx":917,"fy":164,"t":${time + 3332}}],"sc":[],"kb":[{"key":"a","t":${time + 2285}}],"cr":{"screenTop":23,"screenLeft":0,"clientWidth":1905,"clientHeight":257,"screenWidth":1920,"screenHeight":1080,"availWidth":1920,"availHeight":1008,"outerWidth":1920,"outerHeight":991,"scrollWidth":1905,"scrollHeight":1905},"ac_c":0}"""

v参数生成算法比较简单,具体如下

Math.floor(1e4 * Math.random() + 500).toString()

(3)提交密码

URL:

参数:

参数 数值
staticpage https://tieba.baidu.com/tb/static-common/html/pass/v3Jump.html(固定)
charset UTF-8 (固定)
token 上文有所讲述
tpl tb(规定值)
subpro
apiver v3(固定)
tt 时间戳
codestring 验证码(此处不做分析,如果没有验证码就为空)
safeflg 0 (固定)
u https://tieba.baidu.com/ (固定)
isPhone
detect 1 (固定)
gid 上文已讲解
quick_user 0 (固定)
logintype dialogLogin (固定)
logLoginType pc_loginDialog (固定)
idc
loginmerge true(固定)
splogin rate(固定)
username 账号
password 使用公钥加密后的密码
mem_pass on (固定)
rsakey 获取公钥接口包含此参数
crypttype 12(固定)
ppui_logintime 429817(随机数你可以写死)
countrycode
fp_uid
fp_info
loginversion v4(固定)
ds 上传用户轨迹时返回json 有
tk 上传用户轨迹时返回json有
dv 上文已有
traceid 上文已有

返回值:


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body>
<script type="text/javascript">


	var href = decodeURIComponent("https:\/\/tieba.baidu.com\/tb\/static-common\/html\/pass\/v3Jump.html")+"?"

var accounts = '&accounts='

href += "err_no=4&callback=parent.bd__pcbs__ywe98r&codeString=&userName=caizhangtao99209%40163.com&phoneNumber=&mail=&hao123Param=&u=https://tieba.baidu.com/&tpl=tb&secstate=&gotourl=&authtoken=&loginproxy=&resetpwd=&vcodetype=&lstr=<oken=&bckv=&bcsync=&bcchecksum=&code=&bdToken=&realnameswitch=&setpwdswitch=&bctime=&bdstoken=&authsid=&jumpset=&appealurl=&realnameverifyemail=0&traceid=4C5E3C01&realnameauthsid=&bind_mobile_token=&upgrade_mobile_token=&upgrade_mobile=&guide_upgrade_mobile=&scscene=&scnewuser="+accounts;

if(window.location){
    window.location.replace(href);
}else{
   document.location.replace(href);
}
script>
body>
html>

仔细看返回的html中的js链接err_no=4,便是登录结果错误码为4,部分请求错误码如下,如果账号密码返回正确保存cookies就可以去玩了(可能存在邮箱验证 请自行后续分析)
完整版请参考https://ss0.bdstatic.com/5LMZfyabBhJ3otebn9fN2DJv/passApi/js/loginv4_21b4899.js

   "-1": {
    //                     msg: '系统错误,请您稍后再试,帮助中心',
    //                     field: ""
    //                 },
    //                 1: {
    //                     msg: "您输入的帐号格式不正确",
    //                     field: "userName"
    //                 },
    //                 2: {
    //                     msg: '用户名或密码有误,请重新输入或找回密码',
    //                     field: "userName"
    //                 },
    //                 3: {
    //                     msg: "验证码不存在或已过期,请重新输入",
    //                     field: ""
    //                 },
    //                 4: {
    //                     msg: "帐号或密码错误,请重新输入或者找回密码",
    //                     field: "password"
    //                 },
    //                 5: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 6: {
    //                     msg: "您输入的验证码有误",
    //                     field: "verifyCode"
    //                 },
    //                 7: {
    //                     msg: "用户名或密码有误,请重新输入或找回密码",
    //                     field: "password"
    //                 },
    //                 16: {
    //                     msg: '您的帐号因安全问题已被限制登录,帮助中心',
    //                     field: ""
    //                 },
    //                 257: {
    //                     msg: "请输入验证码",
    //                     field: "verifyCode"
    //                 },
    //                 100027: {
    //                     msg: "百度正在进行系统升级,暂时不能提供服务,敬请谅解",
    //                     field: ""
    //                 },
    //                 120016: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 18: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 19: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 20: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 21: {
    //                     msg: "没有登录权限",
    //                     field: ""
    //                 },
    //                 22: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 23: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 24: {
    //                     msg: "百度正在进行系统升级,暂时不能提供服务,敬请谅解",
    //                     field: ""
    //                 },
    //                 400031: {
    //                     msg: "请在弹出的窗口操作,或重新登录",
    //                     field: ""
    //                 },
    //                 400032: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 400034: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 401007: {
    //                     msg: "您的手机号关联了其他帐号,请选择登录",
    //                     field: ""
    //                 },
    //                 120021: {
    //                     msg: "登录失败,请在弹出的窗口操作,或重新登录",
    //                     field: ""
    //                 },
    //                 500010: {
    //                     msg: "登录过于频繁,请24小时后再试",
    //                     field: ""
    //                 },
    //                 200010: {
    //                     msg: "验证码不存在或已过期",
    //                     field: ""
    //                 },
    //                 100005: {
    //                     msg: "系统错误,请您稍后再试",
    //                     field: ""
    //                 },
    //                 120019: {
    //                     msg: "请在弹出的窗口操作,或重新登录",
    //                     field: "userName"
    //                 },
    //                 110024: {
    //                     msg: "此帐号暂未激活,重发验证邮件",
    //                     field: ""
    //                 },
    //                 100023: {
    //                     msg: "开启Cookie之后才能登录,如何开启?",
    //                     field: ""
    //                 },
    //                 17: {
    //                     msg: '您的帐号已锁定,请解锁后登录',
    //                     field: "userName"
    //                 },
    //                 400401: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 400037: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 50023: {
    //                     msg: "1个手机号30日内最多换绑3个账号",
    //                     field: ""
    //                 },
    //                 50024: {
    //                     msg: "注册过于频繁,请稍候再试",
    //                     field: ""
    //                 },
    //                 50025: {
    //                     msg: "注册过于频繁,请稍候再试;也可以通过上行短信的方式进行注册",
    //                     field: ""
    //                 },
    //                 50028: {
    //                     msg: '帐号或密码多次输错,请3个小时之后再试或找回密码',
    //                     field: ""
    //                 },
    //                 50029: {
    //                     msg: '帐号或密码多次输错,请3个小时之后再试或找回密码',
    //                     field: ""
    //                 },
    //                 50030: {
    //                     msg: "抱歉,该手机号的申请次数已达当日上限,请更换手机号",
    //                     field: ""
    //                 },
    //                 50031: {
    //                     msg: "抱歉,该手机号的申请次数已达当月上限,请更换手机号",
    //                     field: ""
    //                 },
    //                 50032: {
    //                     msg: "抱歉,该手机号的申请次数已达本季度上限,请更换手机号",
    //                     field: ""
    //                 },
    //                 400413: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 400414: {
    //                     msg: "",
    //                     field: ""
    //                 },
    //                 400415: {
    //                     msg: "帐号存在风险,为了您的帐号安全,请到百度钱包/理财/地图任一APP登录并完成验证,谢谢",
    //                     field: ""
    //                 },
    //                 400500: {
    //                     msg: "您登录的帐号已注销,请登录其他帐号或重新注册",
    //                     field: ""
    //                 },
    //                 400702: {
    //                     msg: "该手机号码登录功能已关闭,请通过用户名和密码登录",
    //                     field: "userName"
    //                 },
    //                 72200: {
    //                     msg: "您的帐号因冻结暂时无法登录,请前往冻结时的手机APP,在登录页点击遇到问题进行解冻",
    //                     field: ""
    //                 },
    //                 96001: {
    //                     msg: "您的帐号因违反百度用户协议被限制登录",
    //                     field: ""
    //                 },
    //                 400703: {
    //                     msg: "手机号已被运营商二次放号,请前往https://passport.baidu.com/v2/?reg注册新帐号",
    //                     field: ""
    //                 }
    //             },
    //             t.errMsg.checkVerifycode = {
    //                 500002: {
    //                     msg: "您输入的验证码有误",
    //                     field: "verifyCode"
    //                 },
    //                 500018: {
    //                     msg: "验证码已失效,请重试",
    //                     field: "verifyCode"
    //                 }
    //             }

首先利用获取的公钥进行加密,而后对其base64加密。
此处你可以用其他语言实现或者使用如下js
你可以利用如下的js进行完成
https://github.com/fanmingyi/tiebaWebProtocol/blob/master/src/main/resources/baiduLoginPwdEncrypt.js

百度核心登录算法分析完毕。

你可能感兴趣的:(web协议)