资讯动态

Label Studio Enterprise 2.4.9-7 发布解析:file-proxy URL 双重编码修复与 SSRF 防御增强

发布时间:2026/9/13 11:34:41 来源:尧图企业网站定制
Label Studio Enterprise 2.4.9-7 发布解析file-proxy URL 双重编码修复与 SSRF 防御增强【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio本篇技术指南以 docs/source/guide/release_notes/onprem/2.4.9-7.md 中的 Label Studio Enterprise 2.4.9-7 发布说明为主体深入解析该版本的两项关键变更file-proxy 代理 URL 的双重编码double encoding问题修复以及针对 SSRF服务器端请求伪造攻击的既有防御机制加固。通过阅读本文你将理解 Label Studio 中 file-proxy 端点的完整编码/解码链路、SSRF 防御的底层实现URL 校验、IP 封禁子网、环境变量开关并掌握如何验证与配置这些安全机制。版本概览与变更清单Label Studio Enterprise 2.4.9-7 发布于 2023 年 8 月 22 日属于Bug fixes缺陷修复性质的小版本。完整的官方变更清单如下类别变更内容Bug fixes修复 file-proxy 代理 URL 的双重编码double encoding问题Security参考 Label Studio 仓库 GH 4483使既有的 SSRF 防御机制更加健壮尽管变更条目只有两条但两者都触及 Label Studio 架构中的核心安全与数据通路file-proxy 是 Label Studio 对外提供云存储文件访问的统一入口而 SSRF 防御则是保护服务器不被打入内网的关键屏障。下文分别从源码层面拆解这两个修复点。file-proxy 双重编码修复代理链路的前世今生file-proxy 端点在架构中的位置Label Studio 中标注任务的数据往往存放在外部云存储S3、GCS、Azure Blob、Redis 或本地文件系统中。为了让浏览器端的标注界面能够访问这些文件系统提供了一条“代理”通路URL 生成侧在 label_studio/tasks/models.py 的Task.resolve_uris()中当项目配置了task_data_login/task_data_password受保护的任务数据时任务数据里所有形如 URL 的字段值都会被改写成 file-proxy 代理地址if project.task_data_login and project.task_data_password: protected_data {} for key, value in task_data.items(): if isinstance(value, str) and string_is_url(value): path ( reverse(projects-file-proxy, kwargs{pk: project.pk}) ?url base64.urlsafe_b64encode(value.encode()).decode() ) value urljoin(settings.HOSTNAME, path) protected_data[key] value return protected_data可以看到原始文件 URL 会先经过base64.urlsafe_b64encode编码再作为?url查询参数拼接到 file-proxy 路径后最终以settings.HOSTNAME为前缀生成完整代理地址。这样既避免了在任务数据中直接暴露内部存储 URL也统一了鉴权入口。URL 消费侧浏览器请求该代理地址后由 label_studio/io_storages/proxy_api.py 中的TaskResolveStorageUri与ProjectResolveStorageUri两个视图分别对应任务级与项目级代理处理。两者的核心逻辑都在ResolveStorageUriAPIMixin.resolve()中# Attempt to base64 decode the fileuri try: fileuri base64.urlsafe_b64decode(fileuri.encode()).decode() # For backwards compatibility, try unquote if this fails except Exception as exc: logger.debug( fFailed to decode base64 {fileuri} for {model_name} {instance.id}: {exc} falling back to unquote ) fileuri unquote(fileuri)双重编码问题的成因与修复意义从上述两端代码的对照可以推断出双重编码问题的根源file-proxy 端点在生成侧对原始 URL 做了一次 base64 编码在消费侧先尝试 base64 解码、失败后回退到urllib.parse.unquote。这条“编码—解码”链路上存在多个可叠加的编码层次URL 查询参数本身需要经过一次 URL 编码query string 转义原始 URL 中若已包含%、、等字符会与 base64 的结果再次互相干扰不同的调用方前端直接拼接、反向代理改写、二次跳转等可能对url参数重复应用编码/解码解码端同时兼容 base64 与unquote两种格式一旦中间层多做了一次 URL 解码最终还原出的fileuri就与原地址不一致导致存储对象定位失败返回 404 或错误的文件。2.4.9-7 版本正是修复了这类“编码/解码不对称”导致代理地址被二次转义、最终取不到正确文件的问题。修复后生成侧保持 base64 编码输出消费侧优先按 base64 解码、并保留unquote作为向后兼容回退见 label_studio/io_storages/proxy_api.py确保代理 URL 在整条链路上只被规范处理一次。测试用例印证仓库中的单元测试 label_studio/tasks/tests/test_models.py 对该代理行为有直接覆盖override_settings(HOSTNAMEhttp://localhost:8080) patch(tasks.models.reverse, return_value/projects/1/file-proxy/) def test_credential_based_proxy(self, _mock_reverse): When the project has task_data_login/password set, all URL values are proxied through the file-proxy endpoint instead. self.project.task_data_login user self.project.task_data_password pass try: task_data {url: https://example.com/file.jpg} result self.task.resolve_uris(task_data, self.project) assert http://localhost:8080 in result[url] finally: self.project.task_data_login None self.project.task_data_password None该用例验证了当项目设置了任务数据访问凭据后所有 URL 字段都会经由 file-proxy 端点代理而非直连且最终地址以配置的HOSTNAME为前缀。这正是 file-proxy 通路的基本契约也是双重编码修复所保护的行为边界。SSRF 防御增强从 URL 校验到子网封禁什么是 SSRF为什么与 Label Studio 相关SSRFServer-Side Request Forgery服务器端请求伪造指攻击者诱导服务器去请求本不该访问的内部地址如127.0.0.1、内网网段。Label Studio 天然面临此类风险标注数据可来自任意云存储端点管理员在配置 S3/GCS 等存储时需要填写endpoint地址若不加校验恶意配置者可让服务器向内网发请求进而探测或攻击内部服务。发布说明中提到的 GH 4483 即是对这一防御面的加固。核心校验函数Label Studio 的 SSRF 防线集中在 label_studio/core/utils/io.py 的validate_url_for_ssrf()def validate_url_for_ssrf(url, block_local_urlsTrue): Utility function for defending against SSRF attacks. Raises - SsrfBlockedUrlError if the url is not HTTP[S], or if block_local_urls is enabled and the URL resolves to a local address. - LabelStudioApiException if the hostname cannot be resolved parsed_url parse_url(url) if parsed_url.scheme not in (http, https): raise SsrfBlockedUrlError domain parsed_url.host try: ip socket.gethostbyname(domain) except socket.error: from core.utils.exceptions import LabelStudioAPIException raise LabelStudioAPIException(fCant resolve hostname {domain}) if block_local_urls: validate_ip(ip)其校验策略分三层协议白名单仅允许http/https协议其他 scheme 直接抛出SsrfBlockedUrlError域名可解析性对主机名执行 DNS 解析解析失败则抛出LabelStudioAPIException这要求校验过程真实发起 DNS 查询IP 归属检查若开启block_local_urls将解析出的 IP 交给validate_ip()与封禁子网比对。默认封禁子网与可配置项validate_ip()label_studio/core/utils/io.py内置了一份default_banned_subnets列表涵盖回环地址、私有网段、链路本地、组播、文档网段、IPv4/IPv6 翻译地址与 IPv6 特殊前缀等常见危险地址例如default_banned_subnets [ 0.0.0.0/8, # current network 10.0.0.0/8, # private network 127.0.0.0/8, # loopback 169.254.0.0/16, # link-local 172.16.0.0/12, # private network 192.168.0.0/16, # private network 224.0.0.0/4, # multicast ::1/128, # loopback fc00::/7, # unique local fe80::/10, # link-local # ... 其余保留网段省略 ]最终生效的封禁集合由三个环境变量控制label_studio/core/settings/base.pySSRF_PROTECTION_ENABLED get_bool_env(SSRF_PROTECTION_ENABLED, True) USE_DEFAULT_BANNED_SUBNETS get_bool_env(USE_DEFAULT_BANNED_SUBNETS, True) USER_ADDITIONAL_BANNED_SUBNETS get_env_list(USER_ADDITIONAL_BANNED_SUBNETS, default[])环境变量默认值作用SSRF_PROTECTION_ENABLEDTrueSSRF 保护总开关关闭后跳过本地地址校验USE_DEFAULT_BANNED_SUBNETSTrue是否启用内置默认封禁子网列表关闭是有风险的操作USER_ADDITIONAL_BANNED_SUBNETS[]追加自定义封禁网段逗号分隔的列表源码注释明确警告关闭默认子网校验是危险操作仅应在完全清楚后果时进行见 label_studio/core/utils/io.py 中的说明。在 S3 存储配置中的实际落地SSRF 校验并非只存在于工具函数中而是真正挂在存储配置的 API 校验链路上。label_studio/io_storages/s3/serializers.py 中的validate_s3_endpoint()在创建/更新 S3 导入、导出存储时执行def validate_s3_endpoint(self, value): # on the mixin: both import and export storages accept s3_endpoint if value and settings.SSRF_PROTECTION_ENABLED: validate_url_for_ssrf(value, block_local_urlsTrue) return value即只要SSRF_PROTECTION_ENABLED为真配置 S3 存储时提交的s3_endpoint就会被强制做协议白名单 DNS 解析 子网封禁三重校验任何指向内网或保留地址的端点都会被拒绝返回 400/403。测试用例验证仓库针对该防线提供了专门的测试文件 label_studio/io_storages/tests/test_s3_ssrf_validation.py其设计非常能说明修复后的行为边界使用字面量公网 IPhttp://93.184.216.34:9000作为“合法”端点避免测试触发真实 DNS 查询使用http://127.0.0.1:9000作为“非法”内网端点在override_settings(SSRF_PROTECTION_ENABLEDTrue)下S3 导入存储创建、S3 导出存储创建、以及/api/storages/s3/validate与/api/storages/export/s3/validate两个校验接口均断言本地端点被拒绝且validate_connection不被调用即校验发生在真实连接建立之前见 test_s3_ssrf_validation.py对照组则验证公网端点可以正常通过test_s3_ssrf_validation.py。这组测试同时覆盖了导入、导出与校验 API 三条路径说明 GH 4483 加固后SSRF 校验已前置到“任何涉及端点地址的入口”而不仅仅是实际下载数据的那一刻。升级与验证建议如果你是 Label Studio Enterprise 2.4.9-7 的使用者或希望复现验证可以按以下思路操作验证 file-proxy 修复为项目配置task_data_login/task_data_password后导入带外部图片 URL 的任务检查前端标注页面中图片地址是否正常加载即/projects/id/file-proxy/?urlbase64能够正确解析。可对照 label_studio/tasks/tests/test_models.py 的用例逻辑。验证 SSRF 防线尝试通过 S3 存储 API 提交指向http://127.0.0.1:9000的s3_endpoint预期被拒绝提交公网端点则正常。若需临时关闭校验排查问题可通过环境变量SSRF_PROTECTION_ENABLEDfalse控制生产环境不建议。自定义封禁范围如内网中存在需要放行/额外封禁的网段请优先使用USER_ADDITIONAL_BANNED_SUBNETS追加而非关闭USE_DEFAULT_BANNED_SUBNETS。相关源码索引发布说明原文docs/source/guide/release_notes/onprem/2.4.9-7.mdfile-proxy URL 生成侧label_studio/tasks/models.pyfile-proxy 解码与代理视图label_studio/io_storages/proxy_api.pySSRF 校验函数与封禁子网label_studio/core/utils/io.pySSRF 相关设置项label_studio/core/settings/base.pyS3 端点校验落地label_studio/io_storages/s3/serializers.pySSRF 测试用例label_studio/io_storages/tests/test_s3_ssrf_validation.pyfile-proxy 行为测试用例label_studio/tasks/tests/test_models.py【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价