哔哩哔哩-API收集整理:API签名验证机制深度剖析

哔哩哔哩-API收集整理:API签名验证机制深度剖析

在哔哩哔哩(B站)的开放生态中,API(Application Programming Interface,应用程序接口)是连接第三方开发者与平台服务的重要桥梁。随着平台安全需求的提升,API签名验证机制成为保障接口调用合法性和数据安全性的关键环节。本文将深入剖析B站API的签名验证机制,重点讲解WBI签名的工作原理、实现步骤及多语言示例,帮助开发者轻松应对接口调用中的签名难题。

WBI签名机制概述

自2023年3月起,B站Web端部分接口开始采用WBI签名鉴权,表现为在REST API请求的Query参数中添加了w_ridwts字段。WBI签名鉴权独立于APP鉴权与其他Cookie鉴权,是一种Web端风控手段。

当请求采用WBI签名鉴权的接口时,若签名参数w_rid与时间戳wts缺失或错误,会返回v_voucher,示例如下:

{"code":0,"message":"0","ttl":1,"data":{"v_voucher":"voucher_******"}}

WBI签名错误返回示例

WBI签名机制主要包括获取实时口令、打乱重排生成mixin_key、计算签名w_rid以及添加签名参数四个步骤,下面将详细介绍每个步骤的具体实现。

WBI签名算法详解

1. 获取实时口令img_keysub_key

img_keysub_key是WBI签名的核心参数,可通过以下两种方式获取:

注意img_urlsub_url看似是图片URL,实则是经过伪装的实时Token,无需访问这些URL。例如:

{"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"}}}

截取URL中的文件名,即可得到img_keysub_key,如上述例子中的7cd084941338484aae1ad9425b84077c4932caff0ff746eab6f01bf08b70ac45。这两个参数全站统一使用,每日更替,建议做好缓存和刷新处理。

2. 生成mixin_key

sub_key拼接在img_key后面得到raw_wbi_key,然后遍历重排映射表MIXIN_KEY_ENC_TAB,取出raw_wbi_key中对应位置的字符拼接成新字符串,截取前32位即为mixin_key

重排映射表MIXIN_KEY_ENC_TAB如下:

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
];

例如,img_key7cd084941338484aae1ad9425b84077csub_key4932caff0ff746eab6f01bf08b70ac45,经过上述操作后得到mixin_keyea1db124af3c7062474693fa704f4ff8

3. 计算签名w_rid

首先,在原始请求参数中添加wts字段(当前Unix时间戳,秒级),然后按键名升序排序并进行URL编码,拼接mixin_key后计算MD5值,即为w_rid

需要注意的是,参数值中的中文或特殊字符编码时字母应大写,空格编码为%20。例如,参数{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_ridwts追加到原始请求参数的URL Query中,即可完成签名。例如,最终得到bar=514&foo=114&zab=1919810&w_rid=8f6f2b5b3d485fe1886cec6a0be8c5d4&wts=1702204169

APPKey与签名机制

除了WBI签名,B站API还使用APPKey进行鉴权。APPKey是应用的唯一标识,与APPSEC(密钥)配合使用,用于生成请求签名。以下是部分已知的APPKey及其应用场景:

APPKEYAPPSECplatformAPP类型备注
1d8b6e7d45233436560c52ccd288fed045859ed18bffd973android粉版获取资源通用
783bbb7264451d822653583c8873dea268ab9386918b1d65android粉版仅获取用户信息时使用(7.X及更新版本)
07da50c9a0bf829f25bdede4e1581c836cab73a48790ca6eandroid概念版
YvirImLGlLANCLvMJNlZNgfNGKZEpaDTkCdPQVXntXhuiJEMios-视频取流专用

详细的APPKey列表可参考APPKey文档。不同的APPKey适用于不同的平台和场景,开发者需根据实际需求选择合适的APPKey。

B站API签名机制流程图

多语言签名实现示例

Python实现

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
]

def getMixinKey(orig: str):
    return reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32]

def encWbi(params: dict, img_key: str, sub_key: str):
    mixin_key = getMixinKey(img_key + sub_key)
    curr_time = round(time.time())
    params['wts'] = curr_time
    params = dict(sorted(params.items()))
    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()
    params['w_rid'] = wbi_sign
    return params

def getWbiKeys() -> tuple[str, str]:
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3', 'Referer': 'https://www.bilibili.com/'}
    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({'foo': '114', 'bar': '514', 'baz': 1919810}, img_key, sub_key)
print(urllib.parse.urlencode(signed_params))

JavaScript实现

import md5 from 'md5'

const 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
]

const getMixinKey = (orig) => mixinKeyEncTab.map(n => orig[n]).join('').slice(0, 32)

function encWbi(params, img_key, sub_key) {
  const mixin_key = getMixinKey(img_key + sub_key),
    curr_time = Math.round(Date.now() / 1000),
    chr_filter = /[!'()*]/g

  Object.assign(params, { wts: curr_time })
  const query = Object.keys(params)
    .sort()
    .map(key => {
      const value = params[key].toString().replace(chr_filter, '')
      return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
    })
    .join('&')

  const wbi_sign = md5(query + mixin_key)
  return query + '&w_rid=' + wbi_sign
}

async function getWbiKeys() {
  const res = await fetch('https://api.bilibili.com/x/web-interface/nav', {
    headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3', 'Referer': 'https://www.bilibili.com/' }
  })
  const { data: { wbi_img: { img_url, sub_url } } } = await res.json()
  return {
    img_key: img_url.slice(img_url.lastIndexOf('/') + 1, img_url.lastIndexOf('.')),
    sub_key: sub_url.slice(sub_url.lastIndexOf('/') + 1, sub_url.lastIndexOf('.'))
  }
}

async function main() {
  const { img_key, sub_key } = await getWbiKeys()
  const query = encWbi({ foo: '114', bar: '514', baz: 1919810 }, img_key, sub_key)
  console.log(query)
}

main()

更多语言的实现示例(如Golang、C#、Java等)可参考WBI签名文档

总结与注意事项

WBI签名机制是B站Web端API的重要安全措施,开发者在调用相关接口时需严格按照签名步骤生成w_ridwts参数。同时,应注意以下几点:

  1. img_keysub_key每日更新,需定期获取并缓存。
  2. 参数排序和编码方式需严格遵循规范,否则会导致签名错误。
  3. 不同平台和接口可能采用不同的签名机制(如APPKey),需根据实际情况选择合适的鉴权方式。

通过本文的介绍,相信开发者对B站API的签名验证机制有了深入的了解。合理使用API签名机制,不仅能保证接口调用的合法性,还能提升应用的安全性。如需更多API相关信息,可查阅项目中的文档目录

B站API生态

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值