资讯动态

Backstage 云存储 URL Reader 的输入校验与路径处理加固解析

发布时间:2026/9/10 8:35:53 来源:尧图企业网站定制
Backstage 云存储 URL Reader 的输入校验与路径处理加固解析【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstageBackstage 的backstage/backend-defaults包内置了一组云存储 URL ReaderAWS S3、Google GCS、Azure Blob Storage 等负责把软件目录Software Catalog中通过 location 引用的远程文件读回来。近期针对该包的变更说明 tidy-cloud-reader-paths-2.md 声明了本次 patch 级更新的核心内容“Improved input validation and path handling for cloud storage URL readers”——即强化了云存储 URL Reader 的输入校验与路径处理。本文围绕这条变更结合仓库源码讲清楚这套校验到底做了什么、为什么重要以及它在各个 Reader 中的具体落地方式。变更背景URL Reader 是 Backend 的安全边界在 Backstage 中用户或自动化流程可以向软件目录注册 location例如把一个 TechDocs 站点指向 S3 桶中的某个对象、把 scaffolder 模板指向 GCS 上的文件。当后端需要读取这些 URL 时UrlReaderService的实现统称 URL Reader会被调用直接以配置的凭证向云存储发起请求。这就意味着 URL Reader 的输入校验不是普通的健壮性问题而是一道安全边界URL 路径中的恶意片段如..路径穿越序列如果未被拦截可能导致请求跳出预期前缀、访问到本不该暴露的对象编码混淆例如把..写成%2e%2e或对分隔符做变体编码可以绕过只做字符串表面匹配的检查readTree场景下Reader 会对某个前缀下的所有对象逐个拉取如果列出结果里混入了包含.或..段的路径同样需要被过滤。本次 changeset 声明的“input validation and path handling”改进正对应了上述三类场景。核心实现util.ts 中的两个路径校验函数所有云存储 Reader 共用的校验逻辑集中在 urlReader/lib/util.ts其中两个函数是本次改进的关键// 判断对象路径key/blob 名中是否含 . 或 .. 段 export function hasDotPathSegments(name: string): boolean { return name.split(/[/\\]/).some(segment { try { const decoded decodeURIComponent(segment); return decoded .. || decoded .; } catch { return false; } }); }hasDotPathSegments用于已解析出的存储对象路径它按/或\拆分路径对每一段先做decodeURIComponent再与./..比较。这里有两点值得注意同时处理正斜杠和反斜杠防止以\为分隔符的编码变体绕过先解码再比较能识破..被百分号编码如%2e%2e的混淆解码失败时返回false即对无法解码的段不误杀。另一个函数作用于原始 URL 字符串// 判断 URL 路径部分是否不含 . 或 .. 段 export function isUrlPathWithoutDotSegments(url: string): boolean { const authoritySeparator url.indexOf(://); if (authoritySeparator -1) { return false; } const urlRemainder url.slice(authoritySeparator 3); const pathStart urlRemainder.search(/[\\/]/); const suffixStart urlRemainder.search(/[?#]/); let rawPathname ; if (pathStart ! -1 (suffixStart -1 || pathStart suffixStart)) { rawPathname urlRemainder.slice(pathStart).split(/[?#]/, 1)[0]; } try { return !decodeURIComponent(rawPathname) .split(/[\\/]/) .some(segment segment . || segment ..); } catch { return false; } }从源码结构看这个实现有若干防御性细节先手工定位://之后的部分再提取路径而不是完全依赖URL解析器new URL()在解析时会自动做路径规范化例如把/a/../b折叠为/b这会让基于URL.pathname的检查失去意义。手工提取则保留了原始路径形态能真正发现被编码或夹带的..段提取时剔除?与#之后的 query 和 fragment避免把查询参数误判为路径段对整个 raw pathname 做decodeURIComponent因此%2e%2e这类编码穿越也会被还原出来解码失败非法百分号序列时返回false即按“不安全”处理拒绝请求而不是放行。AWS S3 Reader解析入口的强制校验AwsS3UrlReader.ts 是本改进最直接的受益者之一。它的parseUrl函数是所有读取操作read/readUrl/readTree/search的第一道关卡export function parseUrl( url: string, config: AwsS3IntegrationConfig, ): { path: string; bucket: string; region: string } { if (!isUrlPathWithoutDotSegments(url)) { throw new Error(Invalid AWS S3 URL ${url}); } // ... 解析 host / pathname区分 path style 与 virtual hosted style }校验不通过会立即抛出Invalid AWS S3 URL错误后续不再构建 S3 Client、不发起任何网络请求。此外S3 Reader 在readTree中对列出的对象键也做了同样的防御。readTree使用ListObjectsV2Command分页拉取指定前缀下的所有对象在逐对象GetObject之前先执行for (let i 0; i allObjects.length; i) { if (hasDotPathSegments(String(allObjects[i]))) { continue; } // GetObjectCommand 拉取对象... responses.push({ data: s3ObjectData, path: relative(path, String(allObjects[i])), lastModifiedAt: response?.LastModified ?? undefined, }); }也就是说即便桶中真实存在 key 含..段的对象例如被人人为上传的a/../../secret.ymlReader 也会跳过它不会将其内容混入返回的文件树而返回给上层的path字段则由node:path/posix的relative计算保证是相对请求前缀的干净路径。值得注意的是S3 Reader 的 URL 解析本身支持多种形态标准 path style、旧式s3-region域名、virtual hosted style以及VPC_ENDPOINT_HOST_RE匹配的 PrivateLink 端点格式parseUrl的改进是在这些分支逻辑之上统一加上的前置校验对现有合法 URL 的行为没有破坏。Google GCS 与 Azure Blob Storage Reader 的对应处理GoogleGcsUrlReader.ts 的readTree采用了hasDotPathSegments过滤器从桶中按前缀列出的文件里剔除含./..段的对象const responses files .filter(file !hasDotPathSegments(file.name)) .map(file ({ data: file.createReadStream(), path: relative(key, file.name), lastModifiedAt: file.metadata.updated ? new Date(file.metadata.updated as string) : undefined, }));GoogleGcsUrlReader.test.ts 中存在与该校验相关的测试用例验证含点段路径的对象会被排除在树读取结果之外。AzureBlobStorageUrlReader.ts 的parseUrl则与 S3 Reader 一样把isUrlPathWithoutDotSegments放在解析入口export function parseUrl(url: string): { path: string; container: string } { if (!isUrlPathWithoutDotSegments(url)) { throw new Error(Invalid Azure Blob Storage URL format: ${url}); } const parsedUrl new URL(url); const pathSegments parsedUrl.pathname .split(/) .filter(Boolean) .map(decodeURIComponent); // First segment is the container name, rest is the blob path const container pathSegments[0]; const path pathSegments.slice(1).join(/); return { path, container }; }这里还能看到“path handling”的另一层含义路径段在解析过程中被统一decodeURIComponent且首段被明确约定为容器名其余段拼接为 blob 路径配合入口校验共同保证了 URL 到容器路径映射的确定性。对应的行为在 AzureBlobStorageUrlReader.test.ts 中有覆盖。校验策略小结为什么这样设计把util.ts与三个云存储 Reader 的实现放在一起看可以归纳出本次改进采用的统一策略场景使用的函数行为请求 URL 解析入口S3 / Azure BlobisUrlPathWithoutDotSegments含./..段含编码形式直接抛错拒绝readTree列出的对象键S3 / GCShasDotPathSegments含点段的对象被静默跳过不进入结果集非法百分号编码两者均按“不安全”处理解码失败返回false拒绝或跳过这套策略的几个设计点值得借鉴在解析前校验原始字符串避开URL构造器的自动路径规范化防止..在到达检查点前就被折叠掉双通道防御入口拒绝read 类操作 结果过滤readTree 类操作前者阻止恶意 URL 进入后者兜底桶内已有脏数据的情形失败关闭fail closed无法安全解析时选择拒绝而不是猜测后放行。如何验证这些行为如果你想在自己的环境中验证这套校验可以从两条路径入手运行单元测试packages/backend-defaults下的 AwsS3UrlReader.test.ts、GoogleGcsUrlReader.test.ts、AzureBlobStorageUrlReader.test.ts 均使用 mock 的存储客户端不依赖真实云账号可以离线运行用例中包含了非法路径输入与点段对象过滤的预期行为阅读变更说明该仓库使用 changesets 管理版本.changeset 目录下与backstage/backend-defaults相关的 patch 说明如 tidy-cloud-reader-paths.md 与本条 tidy-cloud-reader-paths-2.md记录了这两次围绕云存储 Reader 输入校验的改进可用于确认你部署的版本是否已包含对应加固。对运营 Backstage 实例的开发者而言这类改动意味着即使外部用户能提交包含路径穿越意图的 location URL后端 Reader 也会在发起云存储请求之前将其拒绝软件目录摄取链路对恶意/畸形 URL 的抵抗能力得到进一步加强且对既有合法 URL 的读取行为保持兼容。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

免费获取报价