Bokeh 3.8.2 安全补丁解析:WebSocket Origin 校验机制与服务器防护配置实践
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
本文聚焦 Bokeh 3.8.2 补丁版本的核心变更——修复 Bokeh 服务器 WebSocket 的 "Incomplete Origin Validation"(不完整的 Origin 校验)安全问题,并结合当前仓库源码,深入讲解 Bokeh 服务器 WebSocket 连接的 Origin 校验实现原理、--allow-websocket-origin与BOKEH_ALLOW_WS_ORIGIN两种放行配置方式,以及白名单匹配规则。读者将掌握 Bokeh 服务器 WebSocket 连接的完整校验链路,并能够为生产环境部署配置正确的跨站访问策略。
版本概览:一次针对 Bokeh 服务器的安全补丁
根据 3.8.2 版本发布说明,Bokeh 3.8.2 发布于 2026 年 1 月,属于补丁版本(patch release),其唯一变更点如下:
Addressed a security issue with Incomplete Origin Validation for WebSockets in Bokeh server applications
即:修复了 Bokeh 服务器应用中 WebSocket 的不完整 Origin 校验(Incomplete Origin Validation)安全漏洞。该问题位于服务器侧的 WebSocket 握手阶段——当浏览器或其他客户端发起 WebSocket 连接时,如果服务器未严格验证Origin请求头,攻击者可能利用跨站 WebSocket 连接(Cross-Site WebSocket Hijacking,CSWSH)窃取会话或执行越权操作。
这一修复发生在 WebSocket 握手校验的关键路径上,具体实现位于服务器端的 WSHandler 中。下面我们结合当前仓库(即修复后的代码状态)逐一拆解其机制。
Origin 校验的核心实现:WSHandler.check_origin
Bokeh 服务器基于 Tornado 构建,WebSocket 连接由 WSHandler 处理。该类继承自tornado.websocket.WebSocketHandler,并通过重写check_origin()方法(ws.py 第 99-128 行)对每个入站 WebSocket 连接的来源进行校验:
def check_origin(self, origin: str) -> bool: ''' Implement a check_origin policy for Tornado to call. The supplied origin will be compared to the Bokeh server allowlist. If the origin is not allow, an error will be logged and ``False`` will be returned. ''' from ..util import check_allowlist parsed_origin = urlparse(origin) origin_host = parsed_origin.netloc.lower() allowed_hosts = self.application.websocket_origins if settings.allowed_ws_origin(): allowed_hosts = set(settings.allowed_ws_origin()) if check_allowlist(origin_host, list(allowed_hosts)): return True log.error("Refusing websocket connection from Origin '%s'; \ use --allow-websocket-origin=%s or set BOKEH_ALLOW_WS_ORIGIN=%s to permit this; currently we allow origins %r", origin, origin_host, origin_host, allowed_hosts) return False该校验逻辑的要点:
- 来源解析:通过
urlparse(origin)提取Origin头中的netloc(主机名加端口部分)并转为小写,作为待校验的origin_host; - 双重来源:允许的来源集合取自两处——由
BokehTornado应用层维护的websocket_origins(tornado.py 第 557-561 行),以及全局设置中的BOKEH_ALLOW_WS_ORIGIN环境变量(settings.allowed_ws_origin())。当环境变量非空时,环境变量配置优先; - 拒绝即断连:一旦校验不通过,立即记录 error 级日志并返回
False,Tornado 将拒绝本次 WebSocket 握手,连接被终止。日志会明确提示使用者通过--allow-websocket-origin或BOKEH_ALLOW_WS_ORIGIN放行指定来源,便于运维排障。
从实现顺序看,check_origin是 WebSocket 握手的第一个安全关口——只有通过 Origin 校验的连接,才会进入后续的 token 签名校验(check_token_signature)与 session 创建流程(ws.py 第 130-167 行)。因此,本次补丁所修复的 "Incomplete Origin Validation" 正是这一最外层防线。
白名单匹配引擎:check_allowlist 与 match_host
check_origin调用的check_allowlist定义在 src/bokeh/server/util.py 中,它负责把待校验主机与白名单模式逐条比对:
def check_allowlist(host: str, allowlist: Sequence[str]) -> bool: if ':' not in host: host = host + ':80' if host in allowlist: return True return any(match_host(host, pattern) for pattern in allowlist)匹配规则由match_host(util.py 第 157-246 行)具体实现,从源码与文档字符串中可归纳出以下判定规则:
| 场景 | 示例 | 结果 |
|---|---|---|
| 完全相等(含端口) | 192.168.0.1:80vs192.168.0.1:80 | 匹配 |
未指定端口视为:80 | 192.168.0.1:80vs192.168.0.1 | 匹配 |
| 端口不同 | 192.168.0.1:80vs192.168.0.1:8080 | 不匹配 |
通配符*(匹配任意主机、任意端口) | alicevs* | 匹配 |
| 通配符按点分片段匹配 | 192.168.0.1vs192.168.*.* | 匹配 |
| 片段数必须一致(防子域误匹配) | example.com.bad.comvsexample.com | 不匹配 |
| 通配符端口 | alice:8080vs*:80 | 不匹配 |
值得注意的细节:
- 默认拒绝跨站:
match_host要求主机片段数完全一致,例如example.com.bad.com不会被example.com模式匹配,从机制上杜绝了子域名前缀绕过; - 通配符警告:
create_hosts_allowlist(util.py 第 94-155 行)在处理含*的主机时会输出警告日志,提示通配符将允许来自多个(甚至全部)主机名的连接,建议仅在不关心来源限制的场景使用; - 默认放行本机:当未配置任何
--allow-websocket-origin时,create_hosts_allowlist返回['localhost:<port>'],即默认仅允许来自本机地址的连接——这正是 "默认禁止跨站" 安全基线的落地实现。
配置放行来源:命令行参数与环境变量
Bokeh 提供两种方式为 WebSocket 连接放行额外的 Origin,二者在运行时都会汇入上述校验链。
方式一:--allow-websocket-origin 命令行参数
bokeh serve子命令支持--allow-websocket-origin参数,定义于 src/bokeh/command/subcommands/serve.py 第 573-579 行,元变量为HOST[:PORT],可重复指定:
bokeh serve app_script.py --allow-websocket-origin foo.com:8081 bokeh serve app_script.py \ --allow-websocket-origin foo.com:8081 \ --allow-websocket-origin bar.com:8081命令行帮助信息(serve.py 第 175-186 行)明确指出:
By default, cross site connections to the Bokeh server websocket are not allowed. You can enable websocket connections originating from additional hosts by specifying them with the
BOKEH_ALLOW_WS_ORIGINenvironment variable or the--allow-websocket-originoption.
在服务器启动时,该参数值经由 src/bokeh/server/server.py 第 490-502 行 的处理:若指定了 unix socket,则create_hosts_allowlist(opts.allow_websocket_origin, None)(端口留空);常规 TCP 监听下则传入实际端口self.port,最终以extra_websocket_origins参数注入BokehTornado,成为 tornado.py 第 398-405 行 中self._websocket_origins的初始集合。
方式二:BOKEH_ALLOW_WS_ORIGIN 环境变量
全局设置 src/bokeh/settings.py 第 620-622 行 定义了同名设置项:
allowed_ws_origin = PrioritizedSetting[list[str]]( "allowed_ws_origin", "BOKEH_ALLOW_WS_ORIGIN", default=[], convert=convert_str_seq, help="A comma-separated list of allowed websocket origins for Bokeh server applications." )使用方式为设置逗号分隔的主机列表:
export BOKEH_ALLOW_WS_ORIGIN="foo.com:8081,bar.com:8081" bokeh serve app_script.py从前述check_origin的实现可见:环境变量设置优先于命令行参数——当settings.allowed_ws_origin()返回非空集合时,会直接覆盖websocket_origins(即命令行来源)作为唯一判定依据。因此若两种方式同时配置,请确保它们语义一致,避免误放行或误拦截。
修复后的安全边界与最佳实践
结合 3.8.2 补丁修复的目标与当前源码的完整校验链路,可以归纳出生产部署时的安全建议:
- 默认保持拒绝跨站:不要为图省事配置
--allow-websocket-origin=*或全通配白名单。create_hosts_allowlist会就通配符输出警告日志,且match_host中*匹配任意主机与端口,属于高风险配置,仅适用于纯本地或完全无敏感数据的场景; - 精确列出前端域名与端口:当 Bokeh 应用被嵌入其他站点页面时,应把承载页面的确切
host[:port]逐条加入白名单,端口必须与实际访问端口一致(默认 80 端口可省略); - 多层防护协同:Origin 校验只是第一道防线。部署在公网时还应配合 serve.py 中说明 的注意点——
--use-xheaders不应直接暴露给公网;建议让 Bokeh 服务器位于反向代理(如 Nginx)之后,由代理层做 SSL 终止与请求头清洗,再配合--session-ids signed(签名会话 ID)与BOKEH_SECRET_KEY保护会话身份(见 serve.py 第 228-274 行); - 关注错误日志:当浏览器控制台出现 WebSocket 连接失败时,服务器端会输出形如
Refusing websocket connection from Origin '...'的 error 日志,其中包含了被拒的 Origin 与当前允许列表,这是排查放行配置最直接的依据; - 及时升级:包含安全修复的 3.8.2 属于补丁版本,建议所有使用 3.8.x 系列部署 Bokeh 服务器(
bokeh serve)的团队尽快升级,以消除 Incomplete Origin Validation 带来的跨站 WebSocket 风险。
小结
Bokeh 3.8.2 以一次精准的补丁修复了 Bokeh 服务器 WebSocket 的 Origin 校验缺陷,其修复落实在WSHandler.check_origin→check_allowlist/match_host的完整链路中:默认仅放行本机来源、拒绝跨站连接、支持命令行与环境变量两种精细化的白名单配置。理解这条校验链与匹配规则,是安全部署 Bokeh 服务器、正确配置--allow-websocket-origin与BOKEH_ALLOW_WS_ORIGIN的前提,也是本次安全补丁真正发挥防护作用的关键。
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考