Blog

扫码登录百度获取BDUSS

2018-07-17

#百度
#百度网盘
#百度贴吧

由于各种原因,登录百度获取bduss的方式越来越少了,加上某段时间百度开始限制单一机器用账号密码登录的上限,导致博主的直链解析站所依赖的bduss获取站也挂了,博主一度陷入了烦恼。 百度坑死人

所以博主研究了扫码登录

关于扫码登录

这个功能很早就有了,只是博主一直没去研究,扫码登录能获取有效期长达十年的bduss,但是除了手机百度以外,其他百度系的应用都只能调用摄像头扫码,不能在图库选择图片,这意味着用户必须有两台或以上的设备才能够使用扫码登录。

获取二维码

简单的抓包就可以发现二维码获取的链接

curl "https://passport.baidu.com/v2/api/getqrcode?lp=pc"

然后会返回一串json,反序列化后会得到 imgurlsign

{
  "imgurl": "passport.baidu.com/v2/api/qrcode?sign=c2db7cc9133219a586606e9468decf3e&lp=pc",
  "errno": ,
  "sign": "c2db7cc9133219a586606e9468decf3e",
  "prompt": "登录后威马将获得百度帐号的公开信息(用户名、头像)"
}

剩下那些就不用管了 imgurl就是获取扫码登录用的二维码,而sign是对应二维码的唯一id

不获取二维码直接使用网页授权

二维码只是一个链接,所以可以拼接链接并访问该链接授权

https://wappass.baidu.com/wp/?qrlogin=&sign=c2db7cc9133219a586606e9468decf3e

2020-03-30更新:百度不再允许pc的 User-Agent 访问此链接,会提示下载手机客户端,移动设备的 User-Agent 不受影响

获取临时bduss

获得二维码时,经过简单的筛选就可以发现后台在尝试连接一个url,但只要不扫码,一段时间以后会连接超时,然后再次进行请求,周而复始……

curl "https://passport.baidu.com/channel/unicast?channel_id=c2db7cc9133219a586606e9468decf3e&callback=this_is_callback"

这里的channel_id其实就是前面获取到的sign,扫了码再请求就会有结果了,query string 里面必须要有 callbackcallback 值可为空 然后又有了这货

this_is_callback({"errno":0,"channel_v":"{\"status\":0,\"v\":\"\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\",\"u\":null}"})

双重嵌套json,嗯…… 那个v对应的键值就是临时的bduss

登录

接上面

fetch("https://passport.baidu.com/v3/login/main/qrbdusslogin?bduss=" + JSON.parse(data.channel_v).v)

简单的GET请求,不是么? 然后就可以在返回头那的set-cookie拿你想拿的东西了

心血来潮去看了一下cookie以外返回了些啥

{
    "errInfo": {
        "no": "0",
        "msg": ""
    },
    "code": "110000",
    "message": "",
    'data': {
        "u": "https:\/\/passport.baidu.com\/",
        "userName": "",
        "hao123Param": "******FBQUFBQUFBQUFBQU******",
        "bdusssign": "",
        "authtoken": "",
        "session": {
            "version": "v3",
            "actionType": "",
            "canshare": "1",
            "authsid": "******",
            "needvcode": "0",
            "bduss": "******AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA******",
            "ptoken": "******",
            "stoken": "******",
            "ubi": "******",
            "stokenList": "[\"tb#******\",\"pp#******\",\"bp#******\",\"netdisk#******\",\"cloudforbusiness#******\",\"bdwm#******\",\"waimai#******\",\"bdwalletsdk#******\",\"baiduwalletapp#******\",\"baidugushitong#******\",\"bdbus#******\",\"fund#******\",\"nuomi#******\",\"licai#******\",\"asset#******\",\"hao123#******\",\"pcs#******\",\"dev#******\",\"fbuym#******\",\"licaiapp#******\",\"mybox#******\",\"iitnightingale#******\",\"dianying#******\",\"mall#******\",\"lv#******\",\"cmovie#******\",\"mapsafe#******\",\"im_hi#******\","ppapp#******\",\"licaient#******\",\"album#******\",\"aduqa#******\"]"
        },
        "user": {
            "username": "******",
            "weakpass": "",
            "userId": "1",
            "livinguname": "",
            "portraitUrl": "https:\/\/himg.bdimg.com\/sys\/portrait\/item\/pp.1.******.******.jpg",
            "portraitSign": "pp.1.******.******",
            "displayName": "******"
        }
    },
    "traceid": ""
}

还挺详细的,这是一个 jsonp,使用了单引号 ' 以及加多了一个反斜杠 \, 所以直接使用 JSON.parse()json_decode() 会报错,因此 js 可以直接 eval,其他语言可以预处理一遍字符串 ("是双引号 ")

示例代码只考虑理想情况,仅供参考

// regexp
str.replace(/'([^'']+)'/gm, `"$1"`).replace(/\\&/gm, "&")
// why not Function()?
function callbackfunc(callback_str) {
    const roundIndex = callback_str.indexOf('(')
    if (roundIndex === -1) {
        return {}
    }
    const callback = callback_str.slice(0, roundIndex)
    return Function(`${callback ? `const ${callback} = (obj) => obj; ` : ''}return ${callback_str}`)()
}
preg_replace("/'([^'']+)'/", '"$1"', str_replace("\\&", "&", $str))

由于百度每种服务的 stoken 都不同,有一个一次性列出所有 stokenstokenList 还是挺方便的

后记

本来很简单的事情研究了一早上,因为curl忘记加上RETURNTRANSFER,被自己蠢哭(逃ε=ε=ε=┏(゜ロ゜;)┛

demo在此,你们要的蓝色的东西https://bduss.nest.moe/,源码位于 github:BANKA2017/get-bduss

2020-03-30追记:我以前都写了些啥啊

2024-04-01追记:感谢评论区网友 @alsotang 提供的信息,我也改用 Function() 来执行回调,再也不用头疼正则表达式了


评论区