Bokeh Secret Key 完整指南:使用 `bokeh secret` 命令为 Bokeh Server 生成会话签名密钥
2026/9/13 17:47:57 网站建设 项目流程

Bokeh Secret Key 完整指南:使用bokeh secret命令为 Bokeh Server 生成会话签名密钥

【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh

导读

在 Bokeh 生产部署中,Bokeh Server 使用密钥(secret key)对会话 ID(session ID)进行加密签名,以防止用户互相篡改或伪造会话。本指南围绕 Bokeh 官方子命令bokeh secret展开,讲解其用法、输出格式、底层实现原理,以及如何通过BOKEH_SECRET_KEY环境变量接入bokeh serve,并深入解析unsigned/signed/external-signed三种会话 ID 模式的差异。读完本文,你将掌握为 Bokeh Server 安全部署生成、配置和保管签名密钥的完整方案。

一、bokeh secret子命令是什么

bokeh secret是 Bokeh 命令行工具(bokeh)内置的一个子命令,其官方定位是:

Generate new secret keys that can be used by the Bokeh server to cryptographically sign session IDs.(生成可用于 Bokeh Server 对会话 ID 进行加密签名的新密钥。)

它的注册位置与命令元数据定义在 src/bokeh/command/subcommands/secret.py:

class Secret(Subcommand): ''' Subcommand to generate a new secret key.''' #: name for this subcommand name = "secret" help = "Create a Bokeh secret key for use with Bokeh server"
  • name = "secret":子命令名,即命令行中的bokeh secret
  • help:子命令帮助文本,可通过bokeh secret --help查看;
  • 该子命令不接收任何参数,其args为空元组,这一点由单元测试 tests/unit/bokeh/command/subcommands/test_secret.py 中的test_args()明确断言(assert scsecret.Secret.args == ())。

该子命令对应的 API 参考文档位于 docs/bokeh/source/docs/reference/command/subcommands/secret.rst,通过automodule指令自动生成模块文档。要查看完整的 Bokeh CLI 子命令清单,可以运行bokeh --help,其中会列出build, info, init, json, secret, serve, settings, static等子命令。

二、基本用法与输出格式

2.1 生成密钥

在命令行直接执行:

bokeh secret

密钥会被打印到标准输出(stdout):

OdGMOEhOMV3O39KW2q9SxxN9TVucajjJxUJ3pGFNlm8

2.2 输出格式说明

Secret.invoke()的实现非常简洁,直接调用bokeh.util.token.generate_secret_key()并打印结果:

def invoke(self, args: Namespace) -> None: key = generate_secret_key() print(key)

其输出格式可由单元测试精确验证(见 tests/unit/bokeh/command/subcommands/test_secret.py 的test_run()):

def test_run(capsys: Capture) -> None: main(["bokeh", "secret"]) out, err = capsys.readouterr() assert err == "" # 无错误输出 assert len(out) == 45 # 44 个字符 + 1 个换行符 assert out[-1] == '\n' # 以换行结尾

也就是说:

  • 密钥本体为44 个字符
  • 输出以单个换行符\n结尾,便于脚本直接捕获;
  • 标准错误输出为空。

2.3 将密钥交给 Bokeh Server

生成的密钥通过环境变量BOKEH_SECRET_KEY提供给bokeh serve

export BOKEH_SECRET_KEY="OdGMOEhOMV3O39KW2q9SxxN9TVucajjJxUJ3pGFNlm8" bokeh serve app_script.py --session-ids signed

也可以写在一行内:

BOKEH_SECRET_KEY="OdGMOEhOMV3O39KW2q9SxxN9TVucajjJxUJ3pGFNlm8" bokeh serve app_script.py --session-ids signed

2.4 安全警告(来自官方文档)

模块 docstring 中带有明确的警告:

Warning:你必须保守这个秘密!像保护 root 密码一样保护它。

密钥一旦泄露,任何持有它的人都能生成合法的会话 ID 与访问令牌,因此绝对不要把它提交到版本库、写进公开配置或打印到日志中。

三、底层实现原理:密钥是如何生成的

bokeh secret的全部安全性都来自 src/bokeh/util/token.py 中的generate_secret_key()

def generate_secret_key() -> str: ''' Generate a new securely-generated secret key appropriate for SHA-256 HMAC signatures.''' return _get_random_string()

3.1 随机字符串生成器_get_random_string

核心生成逻辑是_get_random_string()(同样位于 token.py):

def _get_random_string( length: int = 44, allowed_chars: str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', secret_key: bytes | None = settings.secret_key_bytes()) -> str: """ Return a securely generated random string. With the a-z, A-Z, 0-9 character set: Length 12 is a 71-bit value. log_2((26+26+10)^12) =~ 71 Length 44 is a 261-bit value. log_2((26+26+10)^44) = 261 """

关键参数:

参数默认值说明
length44输出字符串长度,固定为 44 个字符
allowed_charsa-zA-Z0-9(共 62 个字符)URL 安全字符集,不包含特殊符号
secret_keysettings.secret_key_bytes()用于不可用场景下的 PRNG 重播种(re-seed)

文档注释直接给出了熵的计算:44 位字符在 62 字符集下,其信息熵为log_2(62^44) ≈ 261 bit,远超暴力破解的可行范围。

3.2 安全随机数来源:系统 PRNG

_get_random_string()依赖_get_sysrandom()初始化模块级随机源:

def _get_sysrandom() -> tuple[Any, bool]: import random try: sysrandom = random.SystemRandom() using_sysrandom = True return sysrandom, using_sysrandom except NotImplementedError: from .warnings import warn warn('A secure pseudo-random number generator is not available ' 'on your system. Falling back to Mersenne Twister.') ...
  • 正常情况下使用 Python 标准库的random.SystemRandom(),它基于操作系统提供的加密安全随机源(如 Linux 的/dev/urandom);
  • 仅当系统不支持时才会回退到 Mersenne Twister,此时会打印警告;源码注释指出,这种适配实现参考了 Django 项目的django/utils/crypto.py
  • 在回退场景下,如果同时设置了BOKEH_SECRET_KEY_reseed_if_needed()会用密钥、当前状态与时间戳的 SHA-256 摘要重新播种,缓解可预测性风险。

3.3 每次生成都不同

单元测试 tests/unit/bokeh/util/test_token.py 的test_generate_secret_key()验证了密钥的长度与唯一性:

def test_generate_secret_key(self) -> None: key = generate_secret_key() assert 44 == len(key) key2 = generate_secret_key() assert 44 == len(key2) assert key != key2

即:每次调用都产生一个新的 44 字符密钥,两次生成的密钥互不相同。这意味着不要依赖记忆或硬编码,每次部署都应重新生成并妥善保管。

四、密钥在会话签名体系中的角色

4.1 配置入口:BOKEH_SECRET_KEY环境变量

Bokeh 的全局配置中心 src/bokeh/settings.py 中定义了该环境变量:

secret_key = PrioritizedSettingstr | None sign_sessions = PrioritizedSettingbool

两个相关的环境变量:

环境变量默认值作用
BOKEH_SECRET_KEYNone部署专属的加密随机密钥;一旦设置即被secret_key_bytes()缓存为 UTF-8 字节串
BOKEH_SIGN_SESSIONSFalse是否只允许带密钥签名的会话

此外,settings.py 模块加载时还会做两项校验并给出警告:

  • 若已设置BOKEH_SECRET_KEY但其长度小于32 字节,会警告:"BOKEH_SECRET_KEY is recommended to have at least 32 bytes of entropy chosen with a cryptographically-random algorithm"—— 即官方建议密钥至少包含 32 字节(256 bit)的熵,而bokeh secret生成的 261-bit 密钥完全满足这一要求;
  • BOKEH_SIGN_SESSIONS=TrueBOKEH_SECRET_KEY未设置,会警告:"BOKEH_SECRET_KEY must be set if BOKEH_SIGN_SESSIONS is set to True"

4.2 三种会话 ID 模式

bokeh serve通过--session-ids参数控制会话 ID 的签名策略,完整的模式说明位于 src/bokeh/command/subcommands/serve.py 的 “Session ID Options” 一节,可用模式为:unsignedsignedexternal-signed

模式命令示例行为说明
unsigned(默认)bokeh serve app.py --session-ids unsigned服务器接受 URL 中任意会话 ID(如?bokeh-session-id=foo);未提供时服务器生成不可猜测的随机 ID。适合本地开发,无需配置密钥
signedbokeh serve app.py --session-ids signed会话 ID 必须是经密钥签名的特殊格式,无效 ID 会被拒绝;未提供?bokeh-session-id=时服务器自动生成新的已签名会话 ID。任何人可连接,但只能使用安全会话 ID
external-signedbokeh serve app.py --session-ids external-signed会话 ID 必须由外部进程签名(服务器自身不生成),?bokeh-session-id=参数为必填。外部进程(如另一个 Web 应用)需调用bokeh.util.token.generate_session_id()生成合法 ID,并与 Bokeh Server 共享同一个BOKEH_SECRET_KEY

serve.py 中同样强调:

The secret key should be set in aBOKEH_SECRET_KEYenvironment variable and should be a cryptographically random string with at least 256 bits (32 bytes) of entropy. Thebokeh secretcommand can generate new secret keys.

(密钥应通过BOKEH_SECRET_KEY环境变量设置,且应为至少 256 bit / 32 字节熵的加密随机字符串,bokeh secret命令即可生成这样的密钥。)

两种签名模式下,密钥都必须严格保密——任何持有密钥的人都能生成有效的会话 ID(见 serve.py 中的明确表述)。

4.3 签名与校验的算法细节

token.py 中会话 ID 与 JWT 令牌的签名流程如下:

  1. 生成随机会话 IDgenerate_session_id(secret_key, signed)先产生随机字符串,若开启签名则拼接为session_id.signature形式;
  2. HMAC-SHA256 签名_signature()使用hmac.new(secret_key, base_id_encoded, hashlib.sha256)计算摘要,再经 URL-safe Base64 编码(去除=填充)得到签名;
  3. JWT 令牌generate_jwt_token()session_id与过期时间session_expiry(默认 300 秒)封装进 payload,可附带经 zlib 压缩的额外字段(键名为__bk__zlib_),最后同样附上签名;
  4. 防时序攻击校验check_token_signature()check_session_id_signature()均使用hmac.compare_digest()做常量时间比较。源码注释明确指出:"hmac.compare_digest() uses a string compare algorithm that doesn't short-circuit so we don't allow timing analysis"——即不会因比较提前短路而泄露时序信息。

测试 tests/unit/bokeh/util/test_token.py 验证了:用"abc"签名、用"qrs"校验会失败(assert not check_token_signature(token, secret_key="qrs", signed=True)),只有相同密钥才能通过;空字符串与含连字符的非法令牌均会被拒绝。

五、完整的生产部署流程示例

结合以上内容,一个典型的签名会话生产部署流程如下:

# 1. 生成密钥(输出 44 字符,务必保存到安全位置) bokeh secret # 2. 导出为环境变量 export BOKEH_SECRET_KEY="OdGMOEhOMV3O39KW2q9SxxN9TVucajjJxUJ3pGFNlm8" # 3. 以签名会话模式启动服务器 bokeh serve app_script.py --session-ids signed

如需external-signed模式,外部认证进程需要与服务器共享同一个BOKEH_SECRET_KEY,并通过bokeh.util.token.generate_session_id()/generate_jwt_token()生成会话 ID 后,将用户重定向到带?bokeh-session-id=<id>的 URL——未获得合法 ID 的用户将无法加载应用(详见 serve.py 中external-signed模式说明)。

六、常见问题与最佳实践小结

  1. 为什么是 44 个字符?因为 62 字符集下 44 位长度对应约 261 bit 熵,满足 settings.py 中"至少 32 字节熵"的建议,同时保持 URL 安全字符集(无+/=等特殊符号),可直接嵌入环境变量与 URL。
  2. 密钥能复用或硬编码吗?不建议。每次部署都应重新bokeh secret生成;密钥属于部署级机密,应当像 root 密码一样对待(官方文档原话)。
  3. 本地开发是否必须配置?不需要。unsigned模式无需任何密钥,是官方推荐的本地开发方式;密钥与签名模式主要用于生产环境,用于限制只有安全、合法的会话 ID 才能访问应用。
  4. 忘记设置密钥会怎样?如果BOKEH_SIGN_SESSIONS=True而未设置BOKEH_SECRET_KEY,Bokeh 会在启动时发出警告(settings.py 中的启动校验逻辑)。

参考与延伸阅读

  • 子命令 API 文档:docs/bokeh/source/docs/reference/command/subcommands/secret.rst
  • 子命令实现:src/bokeh/command/subcommands/secret.py
  • 密钥与签名算法实现:src/bokeh/util/token.py
  • 环境变量与校验逻辑:src/bokeh/settings.py
  • bokeh serve会话 ID 模式完整说明:src/bokeh/command/subcommands/serve.py
  • 子命令单元测试:tests/unit/bokeh/command/subcommands/test_secret.py
  • 密钥/签名算法单元测试:tests/unit/bokeh/util/test_token.py

【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询