feat:日志敏感信息过滤
This commit is contained in:
1 parent
2c1bf9b967
commit
26d968ec55
45 files changed
+2515
-147
No files matched your search
@@ -1,7 +1,9 @@
|
||||
# 文件访问签名接入与发布
|
||||
# 文件读取与上传签名接入及发布
|
||||
|
||||
本次保护 `/admin/sys-file/oss/file` 的全部文件。默认 `observe` 只记录签名校验结果;只有加载完整配置并切到 `enforce` 后,普通裸链接才会被拒绝。旧 Dart 图片兼容是独立开关。客户端密钥可提取、UA 可伪造、有效接口地址仍可被模拟请求使用,这是一层应用级校验,不是用户或租户文件授权。
|
||||
|
||||
上传入口 `/admin/sys-file/upload`、`/admin/sys-file/inner-upload`、`/admin/sys-file/qn/upload` 同步使用应用签名和服务端文件白名单。上传复用这套配置和密钥,不增加环境密钥组;读取签名不能用于上传。
|
||||
|
||||
## 配置准备
|
||||
|
||||
服务端配置见 `config.example.yml`。密钥为至少 32 字节随机数据的 Base64 字符串,HMAC 使用 **Base64 解码后的字节**。不要复用 OAuth 客户端信息或密码加密密钥。
|
||||
@@ -62,6 +64,51 @@ Base64URL无填充编码的UTF-8文件名
|
||||
|
||||
后台会截获本系统文件 `<a>` 链接,通过已有 Blob 下载工具处理;H5/小程序富文本文件链接改走图片预览或下载后打开本地文件。复制出来的接口地址不再承诺可直接在浏览器打开。Flutter 原生下载、Feign 等没有浏览器 Fetch Metadata 的请求仍执行签名/旧 Dart 规则;不支持这些请求头的旧浏览器同样不能靠本规则识别。这项规则用于限制普通浏览器导航,不能防止脚本携带有效签名并模拟请求,也不能阻止已经获准显示的图片被保存。
|
||||
|
||||
## 上传身份与文件类型
|
||||
|
||||
现有登录鉴权及 `@Inner` 内部调用限制保留。每次 POST multipart 上传只允许一个名为 `file` 的文件部分,通过下列请求头传递身份,不把上传凭据放进 URL 或业务字段:
|
||||
|
||||
| 请求头 | 内容 |
|
||||
| --- | --- |
|
||||
| `X-File-App-Id` | `admin/h5/mini/flutter/service`,`package` 不可上传 |
|
||||
| `X-File-Key-Id` | 现有 kid,如 `v1` |
|
||||
| `X-File-Timestamp` | Unix 秒级时间戳 |
|
||||
| `X-File-Nonce` | 16~64 位字母、数字、`_`、`-`,每次请求重新生成 |
|
||||
| `X-File-Signature` | HMAC-SHA256 的 64 位小写十六进制 |
|
||||
|
||||
签名原文按 LF 连接,无末尾换行;密钥仍使用 Base64 解码后的字节:
|
||||
|
||||
```text
|
||||
v1
|
||||
oss-file-upload
|
||||
POST
|
||||
target
|
||||
appId
|
||||
kid
|
||||
ts
|
||||
nonce
|
||||
```
|
||||
|
||||
`target` 分别为 `upload`、`inner-upload`、`qn-upload`,三种签名不能互换。`inner-upload` 仅接受 `service`。签名用于应用身份和本次上传操作校验,文件内容由服务端独立检查。默认有效 300 秒,允许客户端时间领先最多 60 秒;复用现有匿名校时接口。严格模式下 Redis 原子 `SET NX` 消耗 nonce,重复请求返回 403;Redis 或配置不可用返回 500,不自动放行。不增加 Dart UA 上传兼容。
|
||||
|
||||
`file-access.upload` 默认策略如下,省略配置时 Java 使用相同默认值:
|
||||
|
||||
| 文件类别 | 白名单后缀 |
|
||||
| --- | --- |
|
||||
| 图片 | jpg/jpeg/png/gif/webp/bmp/ico/svg |
|
||||
| 文档和安装包 | pdf/apk/doc/docx/xls/xlsx/ppt/pptx |
|
||||
| 文本和压缩包 | txt/csv/zip |
|
||||
| 音视频 | mp4/mov/webm/avi/mp3/wav/m4a/aac/ogg/amr/flac |
|
||||
| 仅内部上传额外允许 | log/gz,供终端日志使用 |
|
||||
|
||||
默认单文件最大 200 MiB,仍受现有网关、反向代理和 Servlet 更低限制约束。SVG 最大 1 MiB,只接受静态内容,拒绝脚本、事件、外部引用和实体。服务层在写存储或文件记录前统一校验后缀、文件头及适用的图片尺寸/ZIP 容器结构;APK 检查二进制 AndroidManifest,Office 新格式检查对应容器入口。存储后缀和 MIME 由服务端规范化,不信任客户端 Content-Type。这里的格式检查不等于病毒或文档宏扫描。
|
||||
|
||||
缺少或错误身份、过期、重放为 403;重复或畸形头、多个文件部分为 400;不支持类型或内容不符为 415;超过本策略大小为 413。错误响应禁止缓存。文件类型检查在 `observe` 下仍执行,观察模式只放宽应用签名准入。监控为 `file.upload.requests`,标签 `outcome/app/mode`,不记录密钥、签名和登录 Token。
|
||||
|
||||
后台 Axios、通用 el-upload 和富文本上传,H5/小程序的 `uni.uploadFile` 拦截器,以及 Flutter `HttpManage` 为本系统上传路径生成新请求头;导入接口和第三方上传地址不追加本协议。`RemoteFileService` 专属 Feign 拦截器为每次内部上传生成 service 签名。上传成功返回值和保存的文件引用继续为稳定原始地址。
|
||||
|
||||
**已有读取严格模式的环境,先发布客户端及调用文件服务的 Java 服务,再发布 UPMS。** 旧 UPMS 忽略新增头;新版 UPMS 在 `enforce` 下会拒绝没有上传签名的旧网页/服务请求。Java 调用方须随公共依赖一同更新,避免只升级 UPMS。旧 Flutter 当前通过硬件业务接口上传取证和日志,再由 Java 调用文件中心,这条链路无需旧设备立即升级;不要把这一结论扩展到其他直接调用文件中心的旧客户端。必要时切回 `observe` 恢复旧身份请求,文件白名单仍然生效。
|
||||
|
||||
## 入口与数据约束
|
||||
|
||||
| 部分 | 接入位置与行为 |
|
||||
@@ -101,17 +148,17 @@ Base64URL无填充编码的UTF-8文件名
|
||||
三个前端分别执行:
|
||||
|
||||
```bash
|
||||
node --test tests/file-access.test.cjs
|
||||
node --test tests/file-access.test.cjs tests/file-upload.test.cjs
|
||||
```
|
||||
|
||||
后台执行 `pnpm build:prod`,H5 执行 `pnpm build:h5`,小程序执行 `pnpm build:mp-weixin`。跨语言固定向量在各项目 `test-vectors/file-access.json`,其中的密钥是公开测试数据,不能用于部署。
|
||||
后台执行 `pnpm build:prod`,H5 执行 `pnpm build:h5`,小程序执行 `pnpm build:mp-weixin`。跨语言固定向量在各项目 `test-vectors/file-access.json` 和 `test-vectors/file-upload.json`,其中的密钥是公开测试数据,不能用于部署。配置生成器回归执行 `python3 -m unittest discover -s scripts/tests`。
|
||||
|
||||
Java 定向回归:
|
||||
|
||||
```bash
|
||||
mvn -pl pjcloud-upms/pjcloud-upms-biz,pjcloud-integrated/pjcloud-integrated-biz,pjcloud-wallet/pjcloud-wallet-biz,pjcloud-hardware/pjcloud-hardware-biz,pjcloud-canteen/pjcloud-canteen-biz \
|
||||
-am test \
|
||||
-Dtest=FileAccessTest,FileReadTest,AppVersionFileAccessTest,WalletManageBatchExportTest,WalletLedgerBatchExportTest,HardBatchExportFileTest,HardBatchExportQueryTest \
|
||||
-Dtest=FileAccessTest,FileReadTest,FileUploadGuardTest,FileUploadPolicyTest,FileUploadControllerTest,AppVersionFileAccessTest,WalletManageBatchExportTest,WalletLedgerBatchExportTest,HardBatchExportFileTest,HardBatchExportQueryTest \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false
|
||||
```
|
||||
|
||||
@@ -119,7 +166,7 @@ Flutter 使用项目 FVM 3.27.4:
|
||||
|
||||
```bash
|
||||
fvm flutter test --no-pub --dart-define-from-file=.file-access/config.json \
|
||||
test/file_access_test.dart test/package_download_test.dart \
|
||||
test/file_access_test.dart test/file_upload_test.dart test/package_download_test.dart \
|
||||
test/face_sync_plan_test.dart test/face_sync_pipeline_test.dart \
|
||||
test/face_sync_failure_store_test.dart test/face_readiness_cache_test.dart \
|
||||
test/customer_display_balance_test.dart test/customer_display_balance_widget_test.dart
|
||||
|
||||
@@ -11,6 +11,11 @@ file-access:
|
||||
legacy-dart:
|
||||
enabled: true
|
||||
extensions: [jpg, jpeg, png, gif, webp, bmp, svg, ico]
|
||||
upload:
|
||||
ttl-seconds: 300
|
||||
max-file-bytes: 209715200 # 200 MiB; gateway/servlet limits may be lower
|
||||
allowed-extensions: [jpg, jpeg, png, gif, webp, bmp, ico, svg, pdf, apk, doc, docx, xls, xlsx, ppt, pptx, txt, csv, zip, mp4, mov, webm, avi, mp3, wav, m4a, aac, ogg, amr, flac]
|
||||
internal-extensions: [log, gz] # service-signed inner-upload only
|
||||
clients:
|
||||
admin:
|
||||
active-kid: v1
|
||||
|
||||
@@ -92,3 +92,26 @@ Java 定向测试及各端构建命令见 [接入与发布说明](README.md)。
|
||||
- 后台生产、H5、微信小程序构建通过;Flutter 使用统一配置执行签名和安装包定向测试,8 项通过。
|
||||
|
||||
后续构建与配置准备以 README 中的统一路径为准。本文前面提到的分环境配置路径属于历史验证记录,不再作为当前操作指引。
|
||||
|
||||
## 上传身份与文件白名单补充
|
||||
|
||||
2026-10-29 在现有读取协议和同一套固定密钥上增加上传身份校验。普通、内部、七牛三个文件中心上传入口均接入,读取签名不能用于上传;上传签名绑定 POST、目标入口、appId、kid、时间和一次性 nonce。默认 5 分钟有效,严格模式通过 Redis 原子占用 nonce 防止重放;内部上传仅接受 service 身份,Dart UA 不作为上传凭据。
|
||||
|
||||
文件白名单覆盖图片、PDF、APK、Office、TXT/CSV、ZIP 和常用音视频;仅内部上传额外允许 LOG/GZ。服务层在存储前检查后缀、文件头及适用的容器结构、图片尺寸和静态 SVG 内容,并规范存储扩展名与 MIME。默认最大 200 MiB,SVG 最大 1 MiB,现有网关/Servlet 更低限制仍适用。观察模式只放宽签名准入,文件类型检查始终执行。格式识别不代表病毒或文档宏扫描。
|
||||
|
||||
| 验证 | 本次结果 |
|
||||
| --- | --- |
|
||||
| Java 上传测试 | 17 项通过,覆盖跨语言向量、入口隔离、重复头、过期、Dart UA 不放行、40 个并发请求只有一次 nonce 成功、Redis/错误配置失败关闭、实际 Feign multipart 编码、MockMvc 存储前拒绝及观察模式白名单 |
|
||||
| 文件格式 | 覆盖常见图片、PDF、文本、Office 容器、ZIP、APK;拒绝改后缀/伪造 MIME、路径穿越、恶意 SVG、外部实体及过深 XML;最新构建的实际 Flutter Release APK 通过格式校验 |
|
||||
| Java 相关回归 | 52 项通过,覆盖原文件读取、版本地址、日志导出、钱包导出、硬件导出;UPMS、wallet、hardware、integrated、canteen 及所需公共模块编译通过 |
|
||||
| 后台 | 31 项通过,执行实际 Axios 拦截器、通用上传和富文本上传 setup;保留登录/租户头和 multipart 业务字段,失败文件不阻塞同批成功回填,签名不写进保存的富文本 |
|
||||
| H5、小程序 | 各 21 项通过,执行实际 uni.uploadFile 拦截器;覆盖上传向量、每次请求新 nonce、校时及第三方/导入路径不签名 |
|
||||
| Flutter | 签名、上传、安装包和兼容模式定向回归 21 项通过、2 项条件跳过;本次修改的文件工具、HTTP 管理器和上传测试定向 analyze 无诊断 |
|
||||
| 构建 | 后台生产、H5、微信小程序及 Android Release 均通过;Flutter 使用 FVM 3.27.4 和 `.file-access/config.json`,APK 约 101.9 MB |
|
||||
| 配置生成器 | 4 项通过,确认只有一套环境共用密钥、原密钥不变、默认上传策略补齐、已有定制白名单和大小限制不被覆盖 |
|
||||
| TypeScript | 后台全量仍有原有 6 条解析错误,H5 仍有原有 4 条解析错误,小程序仍有原有 17 条 TS6504;后台上传相关组件及其依赖与 HEAD 基线比较均为 115 条既有诊断,无新增诊断,不能表述为全量类型检查通过 |
|
||||
| 源码与密钥 | 五个项目 Git 候选文件未发现当前活动密钥,保留并发的日志导出、Flutter 调试兼容等改动;Diff 空白检查通过 |
|
||||
|
||||
日志保存在本机 `/tmp/pj-upload-security-work/`。本次上传改造没有发布到运行中的 Java 服务,也没有修改 Nacos 或更换密钥。发布后的真实 multipart 联调、微信真机和旧 Flutter 设备验证仍需执行;MockMvc、内存 Feign 和本地构建不能代替现场验收。
|
||||
|
||||
已有 `enforce` 环境先发布客户端及携带新公共依赖的 Java 调用服务,再发布 UPMS,避免仅更新 UPMS 后拒绝旧上传请求。当前旧 Flutter 取证/日志先经过硬件业务接口,再由 Java 调用文件中心,服务端补签后不要求这些旧设备立即升级。`observe` 会允许旧上传身份请求,切到 `enforce` 才真正拦截未签名上传;白名单在两种模式下均有效。发布配置与详细头协议见 README 的“上传身份与文件类型”。
|
||||
Reference in new issue
Block a user