Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WBI 签名相关问题 #919

Open
4 of 7 tasks
cxw620 opened this issue Dec 25, 2023 · 40 comments
Open
4 of 7 tasks

WBI 签名相关问题 #919

cxw620 opened this issue Dec 25, 2023 · 40 comments
Labels
更新/Update 更新过时内容

Comments

@cxw620
Copy link
Contributor

cxw620 commented Dec 25, 2023

由于 Bilibili 在网页端引入的 WBI 风控鉴权目前并未彻底成熟, 特开此 Issue 追踪最新进展, 有最新发现还请各位发在评论区及时补充~

Since the WBI signing for risk control introduced by Bilibili is not yet fully deployed, this Issue is specially opened to track the latest progress. If you have the latest findings, please post them in the comment area in time~

算法实现 | Sign Algorithm

Caution

文档内示例不能及时更新, 勿盲目照搬.
Examples may be out of date. DO NOT JUST COPY THEM

1. 获取实时口令 `img_key``sub_key`
[nav 接口](../../login/login_info.md#导航栏用户信息) 中获取 `img_url``sub_url` 两个字段的参数。
**注:`img_url``sub_url` 两个字段的值看似为存于 BFS 中的 png 图片 url,实则只是经过伪装的实时 Token,故无需且不能试图访问这两个 url**
```json
{"code":-101,"message":"账号未登录","ttl":1,"data":{"isLogin":false,"wbi_img":{"img_url":"https://i0.hdslb.com/bfs/wbi/7cd084941338484aae1ad9425b84077c.png","sub_url":"https://i0.hdslb.com/bfs/wbi/4932caff0ff746eab6f01bf08b70ac45.png"}}}
```
截取其文件名,分别记为 `img_key``sub_key`,如上述例子中的 `7cd084941338484aae1ad9425b84077c``4932caff0ff746eab6f01bf08b70ac45`
`img_key``sub_key` 全站统一使用,观测知应为**每日更替**,使用时建议做好**缓存和刷新**处理。
特别地,发现部分接口将 `img_key``sub_key` 硬编码进 JavaScript 文件内,如搜索接口 `https://s1.hdslb.com/bfs/static/laputa-search/client/assets/index.1ea39bea.js`,暂不清楚原因及影响。
2. 打乱重排实时口令获得 `mixin_key`
把上一步获取到的 `sub_key` 拼接在 `img_key` 后面(下例记为 `raw_wbi_key`),遍历重排映射表 `MIXIN_KEY_ENC_TAB`,取出 `raw_wbi_key` 中对应位置的字符拼接得到新的字符串,截取前 32 位,即为 `mixin_key`
重排映射表 `MIXIN_KEY_ENC_TAB` 长为 64,内容如下:
```rust
const MIXIN_KEY_ENC_TAB: [u8; 64] = [
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
36, 20, 34, 44, 52
]
```
重排操作如下例:
```rust
fn gen_mixin_key(raw_wbi_key: impl AsRef<[u8]>) -> String {
const MIXIN_KEY_ENC_TAB: [u8; 64] = [
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42,
19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60,
51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52,
];
let raw_wbi_key = raw_wbi_key.as_ref();
let mut mixin_key = {
let binding = MIXIN_KEY_ENC_TAB
.iter()
// 此步操作即遍历 MIXIN_KEY_ENC_TAB,取出 raw_wbi_key 中对应位置的字符
.map(|n| raw_wbi_key[*n as usize])
// 并收集进数组内
.collect::<Vec<u8>>();
unsafe { String::from_utf8_unchecked(binding) }
};
let _ = mixin_key.split_off(32); // 截取前 32 位字符
mixin_key
}
```
`img_key` -> `7cd084941338484aae1ad9425b84077c``sub_key` -> `4932caff0ff746eab6f01bf08b70ac45` 经过上述操作后得到 `mixin_key` -> `ea1db124af3c7062474693fa704f4ff8`
3. 计算签名(即 `w_rid`
若下方内容为欲签名的**原始**请求参数(以 JavaScript Object 为例)
```javascript
{
foo: '114',
bar: '514',
zab: 1919810
}
```
`wts` 字段的值应为当前以秒为单位的 Unix 时间戳,如 `1702204169`
复制一份参数列表,添加 `wts` 参数,即:
```javascript
{
foo: '114',
bar: '514',
zab: 1919810,
wts: 1702204169
}
```
随后按键名升序排序后编码 URL Query,拼接前面得到的 `mixin_key`,如 `bar=514&foo=114&wts=1702204169&zab=1919810ea1db124af3c7062474693fa704f4ff8`,计算其 MD5 即为 `w_rid`
需要注意的是:如果参数值含中文或特殊字符等,编码字符字母应当**大写** (部分库会编码为小写字母),空格应当编码为 `%20`(部分库按 `application/x-www-form-urlencoded` 约定编码为 `+`)。
例如:
```javascript
{
foo: 'one one four',
bar: '五一四',
baz: 1919810
}
```
应该被编码为 `bar=%E4%BA%94%E4%B8%80%E5%9B%9B&baz=1919810&foo=one%20one%20four`
4. 向原始请求参数中添加 `w_rid``wts` 字段
将上一步得到的 `w_rid` 以及前面的 `wts` 追加到**原始**请求参数编码得到的 URL Query 后即可,目前看来无需对原始请求参数排序。
如前例最终得到 `bar=514&foo=114&zab=1919810&w_rid=8f6f2b5b3d485fe1886cec6a0be8c5d4&wts=1702204169`

Rust EXAMPLE: Rust Playground

已经或等待确认的常见问题 | FAQ

  • 大部分业务接口均将采用 WBI 风控鉴权, 表现在请求 Path 内包含 wbi 字样
  • WBI 签名错误, 不同接口返回的内容不一样, 不能根据错误码判断
    • 部分接口存在故意的脏数据污染(JSON Response 错误码为 0 但是 data 只包含 v_voucher 之类的内容)
  • 部分接口使用各自特定的 img_key, sub_key(可能被硬编码在 js 文件内), 而不是 nav 接口拿到的
    • 使用公共的抑或各自特定的 img_key, sub_key 获得的 w_rid 均有效
    • 用于风控鉴别(硬编码在 js 文件内的难以及时更新)
  • 出现新参数 w_ks(未实装)

Last Updated: 2023/12/26 12:43

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Dec 26, 2023

部分接口使用各自特定的 img_key, sub_key(可能被硬编码在 js 文件内), 而不是 nav 接口拿到的

这个能贴一下列出个列表吗?没发现

@xiaoyv404 xiaoyv404 added the 更新/Update 更新过时内容 label Dec 26, 2023
@cxw620
Copy link
Contributor Author

cxw620 commented Dec 26, 2023

部分接口使用各自特定的 img_key, sub_key(可能被硬编码在 js 文件内), 而不是 nav 接口拿到的

这个能贴一下列出个列表吗?没发现

例如 api.bilibili.com/x/web-interface/wbi/search/all/v2, 参考 #885, 但是今天看了眼又改回去用公共的了. 估计还在测试吧. 有待观察.

@aqua1548
Copy link

想问一下得出的wts和w_rid在使用的时候都是返回403,权限不足,想问一下这大概是什么问题

@Lightning-Lion
Copy link
Contributor

img_key, sub_key(可能被硬编码在 js 文件内)

想问一下这里硬编码的img_key要如何获取呢?

我试了一下直接抓包搜索全部JS文件,是没有img_key或者sub_key字样的。
iShot_2023-12-28_下午5 13 43

@cxw620
Copy link
Contributor Author

cxw620 commented Dec 28, 2023

想问一下得出的wts和w_rid在使用的时候都是返回403,权限不足,想问一下这大概是什么问题

NO CODE NO FIX

@cxw620
Copy link
Contributor Author

cxw620 commented Dec 28, 2023

img_key, sub_key(可能被硬编码在 js 文件内)

想问一下这里硬编码的img_key要如何获取呢?

我试了一下直接抓包搜索全部JS文件,是没有img_key或者sub_key字样的。 iShot_2023-12-28_下午5 13 43

不叫这两字样. 获取方法只能手动操作吧或许. 等后面看看, 应该会改成动态获取的, 反正现在都能用.

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Dec 28, 2023

img_key, sub_key(可能被硬编码在 js 文件内)

想问一下这里硬编码的img_key要如何获取呢?

我试了一下直接抓包搜索全部JS文件,是没有img_key或者sub_key字样的。 iShot_2023-12-28_下午5 13 43

不叫这两字样. 获取方法只能手动操作吧或许. 等后面看看, 应该会改成动态获取的, 反正现在都能用.

是硬编码和 nav 的都能过风控吗?

@cxw620
Copy link
Contributor Author

cxw620 commented Dec 28, 2023

img_key, sub_key(可能被硬编码在 js 文件内)

想问一下这里硬编码的img_key要如何获取呢?

我试了一下直接抓包搜索全部JS文件,是没有img_key或者sub_key字样的。 iShot_2023-12-28_下午5 13 43

不叫这两字样. 获取方法只能手动操作吧或许. 等后面看看, 应该会改成动态获取的, 反正现在都能用.

是硬编码和 nav 的都能过风控吗?

不清楚, 看官方用的哪个, 不排除就是区分拿来识别爬虫增大爬虫成本的. 我测试请求量不大也没有时间规律, 没遇到被风控.

推荐阅读: 爬虫与反爬-接口安全的风控介绍 by 哔哩哔哩技术

总之, 你的请求和官方越相像, 被风控的几率越低, 如何权衡逆向成本和被风控, 自己考虑.

网页端收集设备指纹算少的, APP 端搜集设备信息更猛, 而且 bili_ticket 这个风控用的 jwt 获取逻辑是 APP 通过各种方法搜集设备信息后加密发送到服务端, 服务器返回该 jwt. 而加密操作是在 native 层实现的, 我没有逆向 native 层的能力, 故无能为力.

@intzaaa
Copy link

intzaaa commented Dec 29, 2023

贡献一个 Dart 语言 Demo,若有写的不好的地方希望各位前辈指教。

https://gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a

dart run lib/wbi.dart "https://api.bilibili.com/x/web-interface/wbi/search/default?w_rid=43e90d4b8069036c02776f4fadec98a6&wts=1703843266"
wts=1703843266&w_rid=43e90d4b8069036c02776f4fadec98a6

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Dec 29, 2023

贡献一个 Dart 语言 Demo,若有写的不好的地方希望各位前辈指教。

gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a_ (too large to embed)_

dart run lib/wbi.dart "https://api.bilibili.com/x/web-interface/wbi/search/default?w_rid=43e90d4b8069036c02776f4fadec98a6&wts=1703843266"
wts=1703843266&w_rid=43e90d4b8069036c02776f4fadec98a6

pr 一份加进去吧?

@cxw620
Copy link
Contributor Author

cxw620 commented Dec 29, 2023

贡献一个 Dart 语言 Demo,若有写的不好的地方希望各位前辈指教。

https://gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a

dart run lib/wbi.dart "https://api.bilibili.com/x/web-interface/wbi/search/default?w_rid=43e90d4b8069036c02776f4fadec98a6&wts=1703843266"
wts=1703843266&w_rid=43e90d4b8069036c02776f4fadec98a6

虽然但是, 我个人认为这 demo 如果没人维护加进 docs 里没什么意义, 我的建议是 demo 发在 discussions 里面, 供初学者参考而已.

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Dec 29, 2023

贡献一个 Dart 语言 Demo,若有写的不好的地方希望各位前辈指教。
gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a_ (too large to embed)_

dart run lib/wbi.dart "https://api.bilibili.com/x/web-interface/wbi/search/default?w_rid=43e90d4b8069036c02776f4fadec98a6&wts=1703843266"
wts=1703843266&w_rid=43e90d4b8069036c02776f4fadec98a6

虽然但是, 我个人认为这 demo 如果没人维护加进 docs 里没什么意义, 我的建议是 demo 发在 discussions 里面, 供初学者参考而已.

emmm,已经加进去的都有人维护吗,呆

@cxw620
Copy link
Contributor Author

cxw620 commented Dec 29, 2023

贡献一个 Dart 语言 Demo,若有写的不好的地方希望各位前辈指教。
gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a_ (too large to embed)_

dart run lib/wbi.dart "https://api.bilibili.com/x/web-interface/wbi/search/default?w_rid=43e90d4b8069036c02776f4fadec98a6&wts=1703843266"
wts=1703843266&w_rid=43e90d4b8069036c02776f4fadec98a6

虽然但是, 我个人认为这 demo 如果没人维护加进 docs 里没什么意义, 我的建议是 demo 发在 discussions 里面, 供初学者参考而已.

emmm,已经加进去的都有人维护吗,呆

没有啊, 得看贡献那个 demo 的有没有空 update. Rust 的我可以持续更新, 毕竟我天天用.

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Dec 29, 2023

贡献一个 Dart 语言 Demo,若有写的不好的地方希望各位前辈指教。
gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a_ (too large to embed)gist.github.com/intzaaa/84410e7a6f470889017c8885d1a12a7a(太大而无法嵌入)_

dart run lib/wbi.dart "https://api.bilibili.com/x/web-interface/wbi/search/default?w_rid=43e90d4b8069036c02776f4fadec98a6&wts=1703843266"
wts=1703843266&w_rid=43e90d4b8069036c02776f4fadec98a6

虽然但是, 我个人认为这 demo 如果没人维护加进 docs 里没什么意义, 我的建议是 demo 发在 discussions 里面, 供初学者参考而已.

emmm,已经加进去的都有人维护吗,呆

没有啊, 得看贡献那个 demo 的有没有空 update. Rust 的我可以持续更新, 毕竟我天天用.

那还不如加进去...加了还看得到不会埋了,有人看到可能还有别人更

@intzaaa
Copy link

intzaaa commented Dec 30, 2023

#920 @cxw620 你把rust示例链接加进去吧😄

@woshishabii
Copy link

那个wbi的python实例要不要把ua加进去

@z0z0r4
Copy link
Collaborator

z0z0r4 commented Jan 7, 2024

那个wbi的python实例要不要把ua加进去

ua 又不在 params,和 wbi 没关系吧

@woshishabii
Copy link

那个wbi的python实例要不要把ua加进去

ua 又不在 params,和 wbi 没关系吧

但是示例不能直接用有点怪(划掉)

@nankaine
Copy link

nankaine commented Jan 10, 2024

刚才测试了一下,python已经无法获取到了
#934

@lovetingyuan
Copy link

刚才测试了一下,python已经无法获取到了 #934

js的还能用 按理说这个算法跟语言应该无关的

@power721
Copy link

想问一下得出的wts和w_rid在使用的时候都是返回403,权限不足,想问一下这大概是什么问题

我也遇到了。Java RestTemplate+cookie返回403,不加cookie返回352,curl+user-agent正常返回。

@BTMuli
Copy link

BTMuli commented Jan 14, 2024

https://api.bilibili.com/x/player/wbi/playurl 这个接口也是硬编码的吗?我自己拿 nav 的返回数据只有 v_voucher

@Allenyep
Copy link

Allenyep commented Jan 15, 2024

样例链接:https://api.bilibili.com/x/space/wbi/arc/search?mid=544291240&pn=1&ps=25&index=1&order=pubdate&order_avoided=true&platform=web&web_location=1550101&dm_img_list=[%7B%22x%22:3767,%22y%22:1266,%22z%22:0,%22timestamp%22:45,%22type%22:0%7D,%7B%22x%22:3775,%22y%22:1364,%22z%22:37,%22timestamp%22:271,%22type%22:0%7D]&dm_img_str=V2ViR0wgMS4wIChPcGVuR0wgRVMgMi4wIENocm9taXVtKQ&dm_cover_img_str=QU5HTEUgKEFUSSBUZWNobm9sb2dpZXMgSW5jLiwgQU1EIFJhZGVvbiBQcm8gNTUwME0gT3BlbkdMIEVuZ2luZSwgT3BlbkdMIDQuMSlHb29nbGUgSW5jLiAoQVRJIFRlY2hub2xvZ2llcyBJbmMuKQ&w_rid=d2d2fac1f0352f7bc79b8287dcc91010&wts=1705304656

java签名函数有问题?

StringJoiner param = new StringJoiner("&");
//排序 + 拼接字符串
map.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach(entry -> param.add(entry.getKey() + "=" + URLUtil.encode(entry.getValue().toString())));
String s = param + mixinKey;
String wbiSign = SecureUtil.md5(s);

本地计算wbi签名:554a526ec8343507a6fe89e3887bb623

@cxw620
Copy link
Contributor Author

cxw620 commented Jan 15, 2024

样例链接:https://api.bilibili.com/x/space/wbi/arc/search?mid=544291240&pn=1&ps=25&index=1&order=pubdate&order_avoided=true&platform=web&web_location=1550101&dm_img_list=[%7B%22x%22:3767,%22y%22:1266,%22z%22:0,%22timestamp%22:45,%22type%22:0%7D,%7B%22x%22:3775,%22y%22:1364,%22z%22:37,%22timestamp%22:271,%22type%22:0%7D]&dm_img_str=V2ViR0wgMS4wIChPcGVuR0wgRVMgMi4wIENocm9taXVtKQ&dm_cover_img_str=QU5HTEUgKEFUSSBUZWNobm9sb2dpZXMgSW5jLiwgQU1EIFJhZGVvbiBQcm8gNTUwME0gT3BlbkdMIEVuZ2luZSwgT3BlbkdMIDQuMSlHb29nbGUgSW5jLiAoQVRJIFRlY2hub2xvZ2llcyBJbmMuKQ&w_rid=d2d2fac1f0352f7bc79b8287dcc91010&wts=1705304656

java签名函数有问题?

StringJoiner param = new StringJoiner("&");
//排序 + 拼接字符串
map.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach(entry -> param.add(entry.getKey() + "=" + URLUtil.encode(entry.getValue().toString())));
String s = param + mixinKey;
String wbiSign = SecureUtil.md5(s);

本地计算wbi签名:554a526ec8343507a6fe89e3887bb623

WBI 签名算法以我发的 Rust example 为准, 文档内的可能过时.

应该是是文档内示例问题, 我测试没问题.

https://api.bilibili.com/x/player/wbi/playurl 这个接口也是硬编码的吗?我自己拿 nav 的返回数据只有 v_voucher

等我有空看看, 或者你自己逆向一下 js. 我知道的都写出来了.

@cxw620
Copy link
Contributor Author

cxw620 commented Jan 16, 2024

https://api.bilibili.com/x/player/wbi/playurl 这个接口也是硬编码的吗?我自己拿 nav 的返回数据只有 v_voucher

Show your code example

@BTMuli
Copy link

BTMuli commented Jan 16, 2024

@cxw620
Copy link
Contributor Author

cxw620 commented Jan 16, 2024

Show your code example

https://github.com/BTMuli/TeyvatGuide/blob/master/src/pages/common/Test.vue

我建议你好好看看我的说明 WBI 签名是咋弄的, 写的什么玩意

你都用 tauri 了, 业务代码为什么不全扔 Rust 里面, 又不是热点路径.

@BTMuli
Copy link

BTMuli commented Jan 16, 2024

你都用 tauri 了, 业务代码为什么不全扔 Rust 里面, 又不是热点路径.

我要是会写rust全梭哈rust了

mixin_key 的获取我测过样例的数据,是没问题的,在之后的按着流程可能有理解上的差异,我明天再研究研究😣

@intzaaa
Copy link

intzaaa commented Jan 17, 2024

你都用 tauri 了, 业务代码为什么不全扔 Rust 里面, 又不是热点路径.

我要是会写rust全梭哈rust了

mixin_key 的获取我测过样例的数据,是没问题的,在之后的按着流程可能有理解上的差异,我明天再研究研究😣

可以试着参考下我那个例子?Dart 和 JavaScript 很相似,你应该可以看懂?

#920

@BTMuli
Copy link

BTMuli commented Jan 17, 2024

可以试着参考下我那个例子?Dart 和 JavaScript 很相似,你应该可以看懂?

#920

草了,我刚刚写了个纯ts的测试,才发现那个 url 没有鉴权 文档的问题属于是

是我开心的太早了,旧链接这是,还是慢慢写测试吧T_T

我会了( ̄▽ ̄)" BTMuli/TGAssistant@f989d71

@ShiinaRinne
Copy link

ShiinaRinne commented Mar 1, 2024

今天测试发现, 唯独这个接口需要携带cookie请求, 前两天用的cookie过期了没注意xd


x

https://api.bilibili.com/x/space/wbi/arc/search
请问各位大佬在这个投稿详情的接口上,签名还可以使用吗

        url = "https://api.bilibili.com/x/space/wbi/arc/search"
        result = BiliAPI._request(url, params=get_params(mid, params={
            "ps": ps,
            "tid": tid,
            "pn": pn,
            "order": order,
            "keyword": keyword,
        }))

https://api.bilibili.com/x/space/wbi/acc/info
我在用户这个接口测试了好几次都没问题,但换到上面那个,就必定会触发风控校验失败,返回v_voucher

        url = "https://api.bilibili.com/x/space/wbi/acc/info"
        result = BiliAPI._request(url, params=get_params(mid))

image

这是调用的相关代码

def get_params(mid: int, web_location: int = 1550101, platform: str = "web", **kwargs) -> dict:
    base = {
        "mid": mid,
        "web_location": web_location,
        "platform": platform,
    }
    base.update(kwargs.get("params", {}))
    
    return base
    def _request(url: str, method: Literal["GET", "POST"] = "GET", **kwargs: Any) -> dict:
        header = Header.new(cookie=config.cookie)
        signed_params = get_signed_params(kwargs.get("params", {}))
        req_url = f"{url}?{urlencode(signed_params)}"

        res = session.request(method=method, url=req_url, headers=header, proxies=config.proxies).json()
        if res is None or res["code"] != 0:
            raise RuntimeError(f"url {url} 请求错误:\r\n {res}. \r\n headers: {header} \r\n params: {signed_params}")

        return res
def get_signed_params(params: dict) -> dict:
    '获取 wbi 签名后的参数'
    img_key, sub_key = getWbiKeys()
    return encWbi(params, img_key, sub_key)

@AnthonyCLZ
Copy link

想问一下,我想获取视频的AI总结,通过的api接口是https://api.bilibili.com/x/web-interface/view/conclusion/get,使用的生成wts和w_rid的方法是在WBI鉴定中给出的python代码,但是使用的时候都是返回403,权限不足,我的代码如下

from functools import reduce
from hashlib import md5
import urllib.parse
import time
import requests

mixinKeyEncTab = [
    46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
    33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
    61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
    36, 20, 34, 44, 52
]

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0'}


def getMixinKey(orig: str):
    '对 imgKey 和 subKey 进行字符顺序打乱编码'
    return reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32]

def encWbi(params: dict, img_key: str, sub_key: str):
    '为请求参数进行 wbi 签名'
    mixin_key = getMixinKey(img_key + sub_key)
    curr_time = round(time.time())
    params['wts'] = curr_time                                   # 添加 wts 字段
    params = dict(sorted(params.items()))                       # 按照 key 重排参数
    # 过滤 value 中的 "!'()*" 字符
    params = {
        k : ''.join(filter(lambda chr: chr not in "!'()*", str(v)))
        for k, v 
        in params.items()
    }
    query = urllib.parse.urlencode(params)                      # 序列化参数
    wbi_sign = md5((query + mixin_key).encode()).hexdigest()    # 计算 w_rid
    params['w_rid'] = wbi_sign
    return params

def getWbiKeys():
    '获取最新的 img_key 和 sub_key'
    resp = requests.get('https://api.bilibili.com/x/web-interface/nav', headers=headers)
    resp.raise_for_status()
    json_content = resp.json()
    img_url: str = json_content['data']['wbi_img']['img_url']
    sub_url: str = json_content['data']['wbi_img']['sub_url']
    img_key = img_url.rsplit('/', 1)[1].split('.')[0]
    sub_key = sub_url.rsplit('/', 1)[1].split('.')[0]
    return img_key, sub_key

img_key, sub_key = getWbiKeys()

signed_params = encWbi(
    params={
        'foo': '114',
        'bar': '514',
        'baz': 1919810
    },
    img_key=img_key,
    sub_key=sub_key
)
query = urllib.parse.urlencode(signed_params)
print(signed_params)
print(signed_params['w_rid'])

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0'}
url = 'https://api.bilibili.com/x/web-interface/view/conclusion/get'
params = {
    'bvid': 'BV1L94y1H7CV',
    'cid': '1335073288',
    'up_mid': '297242063',
    'w_rid': signed_params['w_rid'],
    'wts': signed_params['wts']
}

response = requests.get(url, params=params, headers=headers)
print(response.json())

但是我运行AI Summary当中的例子就可以正常获得总结,想问一下怎么回事,麻烦各位大佬了

@moyu-jie
Copy link

moyu-jie commented Apr 5, 2024

java签名要把URLUtil.encode 换成 URLUtil.encodeAll,要不然有的参数有:冒号,不会进行url编码

@Cliffy2000
Copy link

Cliffy2000 commented Apr 6, 2024

想问一下,我想获取视频的AI总结,通过的api接口是https://api.bilibili.com/x/web-interface/view/conclusion/get,使用的生成wts和w_rid的方法是在WBI鉴定中给出的python代码,但是使用的时候都是返回403,权限不足,我的代码如下

from functools import reduce
from hashlib import md5
import urllib.parse
import time
import requests

mixinKeyEncTab = [
    46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
    33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
    61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
    36, 20, 34, 44, 52
]

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0'}


def getMixinKey(orig: str):
    '对 imgKey 和 subKey 进行字符顺序打乱编码'
    return reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32]

def encWbi(params: dict, img_key: str, sub_key: str):
    '为请求参数进行 wbi 签名'
    mixin_key = getMixinKey(img_key + sub_key)
    curr_time = round(time.time())
    params['wts'] = curr_time                                   # 添加 wts 字段
    params = dict(sorted(params.items()))                       # 按照 key 重排参数
    # 过滤 value 中的 "!'()*" 字符
    params = {
        k : ''.join(filter(lambda chr: chr not in "!'()*", str(v)))
        for k, v 
        in params.items()
    }
    query = urllib.parse.urlencode(params)                      # 序列化参数
    wbi_sign = md5((query + mixin_key).encode()).hexdigest()    # 计算 w_rid
    params['w_rid'] = wbi_sign
    return params

def getWbiKeys():
    '获取最新的 img_key 和 sub_key'
    resp = requests.get('https://api.bilibili.com/x/web-interface/nav', headers=headers)
    resp.raise_for_status()
    json_content = resp.json()
    img_url: str = json_content['data']['wbi_img']['img_url']
    sub_url: str = json_content['data']['wbi_img']['sub_url']
    img_key = img_url.rsplit('/', 1)[1].split('.')[0]
    sub_key = sub_url.rsplit('/', 1)[1].split('.')[0]
    return img_key, sub_key

img_key, sub_key = getWbiKeys()

signed_params = encWbi(
    params={
        'foo': '114',
        'bar': '514',
        'baz': 1919810
    },
    img_key=img_key,
    sub_key=sub_key
)
query = urllib.parse.urlencode(signed_params)
print(signed_params)
print(signed_params['w_rid'])

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0'}
url = 'https://api.bilibili.com/x/web-interface/view/conclusion/get'
params = {
    'bvid': 'BV1L94y1H7CV',
    'cid': '1335073288',
    'up_mid': '297242063',
    'w_rid': signed_params['w_rid'],
    'wts': signed_params['wts']
}

response = requests.get(url, params=params, headers=headers)
print(response.json())

但是我运行AI Summary当中的例子就可以正常获得总结,想问一下怎么回事,麻烦各位大佬了

我今天在抓取评论区的数据用的python也报了403 error,具体api是这个https://api.bilibili.com/x/v2/reply/wbi/main。我找到了b站网页自己发起的请求去测试了一下,用那里面的param和wts死活凑不出它那个w_rid。有点不知道该怎么办。

以及生成评论区的js代码里包含了这个const BASIC_WBI_KEYS={wbiImgKey:"d569546b86c252:db:9bc7e99c5d71e5",wbiSubKey:"557251g796:g54:f:ee94g8fg969e2de"},虽然说现在api里的和这组img/sub key都不行,但是放在这里万一能帮别人(

@cxw620
Copy link
Contributor Author

cxw620 commented Apr 7, 2024

See here for func which converts BASIC_WBI_KEYS into normal one:

      , formatString$1 = tt=>{
        let rt = "";
        for (let Vt = 0; Vt < tt.length; Vt++)
            rt += String.fromCharCode(tt.charCodeAt(Vt) - 1);
        return rt

EXAMPLE:
IMG: d569546b86c252:db:9bc7e99c5d71e5 -> c458435a75b1419ca98ab6d88b4c60d4
SUB: 557251g796:g54:f:ee94g8fg969e2de -> 446140f6859f439e9dd83f7ef858d1cd

Such convertion may NOT be enabled., now still using the public one:

image

image

@intzaaa
Copy link

intzaaa commented Apr 12, 2024

刻意地语言隔阂似乎与开源软件和自由软件之精神相悖

@cxw620
Copy link
Contributor Author

cxw620 commented Apr 12, 2024

刻意地语言隔阂似乎与开源软件和自由软件之精神相悖

  1. 这不是传统意义上的开源, 更多地是分享.
  2. 作为程序员, 无论业余与否, 英语水平是必备的.
  3. 接第一条. 风控相关一直是敏感话题, 为避免黑产以较低成本即可获知 B 站最新风控解决方案, 涉及技术细节我习惯均以英文分享, 减少搜索引擎收录相关中文关键词. 分享 WBI 签名方式只是因为此风控影响到了包括合理利用的所有人.
  4. 日常编程以英语写文档为主, 习惯如此, 冒犯到你请见谅.
  5. 接第三条. 不欢迎伸手党, 惭愧地, 相当一部分中文为主要交流语言的仓库充斥着低质量 issue / comment, 使用英语一定程度上提高门槛, 毕竟代码会说明一切, 能看懂贴出来的代码片段足以理解. 我希望来到这的人都是进行技术细节的讨论而不是 "怎么又不行了" 云云而不提供有效的测试样例.

@intzaaa
Copy link

intzaaa commented Apr 13, 2024

刻意地语言隔阂似乎与开源软件和自由软件之精神相悖

  1. 这不是传统意义上的开源, 更多地是分享.
  2. 作为程序员, 无论业余与否, 英语水平是必备的.
  3. 接第一条. 风控相关一直是敏感话题, 为避免黑产以较低成本即可获知 B 站最新风控解决方案, 涉及技术细节我习惯均以英文分享, 减少搜索引擎收录相关中文关键词. 分享 WBI 签名方式只是因为此风控影响到了包括合理利用的所有人.
  4. 日常编程以英语写文档为主, 习惯如此, 冒犯到你请见谅.
  5. 接第三条. 不欢迎伸手党, 惭愧地, 相当一部分中文为主要交流语言的仓库充斥着低质量 issue / comment, 使用英语一定程度上提高门槛, 毕竟代码会说明一切, 能看懂贴出来的代码片段足以理解. 我希望来到这的人都是进行技术细节的讨论而不是 "怎么又不行了" 云云而不提供有效的测试样例.

理解。感谢你会这么善意地回复我,相信你这么做是经过你的深思熟虑的。👍

@FishZe
Copy link

FishZe commented May 21, 2024

wbi接口中的img_urlsub_url字段,和 bili_ticket 返回的 nav 字段中的img sub字段是否具有相同的意义?我请求的结果是相同的。

如果有相同意义,那么是否说明 wbi 的有效时间和 ticket 一样具有三天的有效期?是否也能说明之后 ticket 字段在客户端会和 wbiweb 具有相同的风控等级?

@cxw620
Copy link
Contributor Author

cxw620 commented May 23, 2024

wbi接口中的img_urlsub_url字段,和 bili_ticket 返回的 nav 字段中的img sub字段是否具有相同的意义?我请求的结果是相同的。

应该是一致的

如果有相同意义,那么是否说明 wbi 的有效时间和 ticket 一样具有三天的有效期?是否也能说明之后 ticket 字段在客户端会和 wbiweb 具有相同的风控等级?

有效期不清楚,蛮久没见到img_key和sub_key有变化。是的。

做这个 WBI 签名大抵是提高第三方 API 调用的难度罢了,通过实时去替换 img_key, sub_key 来识别非官方的访问,不过前提是得有刷新这两个key的方法。通过API刷新?那就没啥意义。写死在代码里?那就更新不及时。

bili_ticket 纯粹是上传设备信息后发放一个jwt,表示当前设备已经验证过。如果遇到官方认为设备异常就会进一步搜集设备各种指纹信息然后上传,这些都在native层实现,很难对抗。

虽然吧黑产该有还是少不了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
更新/Update 更新过时内容
Projects
None yet
Development

No branches or pull requests