首页 / 实战手册 / 安全与合规
敏感数据被打进前端包,公网可下载
先看结论
例行安全检查发现,构建产物里能直接搜到设施名称与坐标,对象存储的瓦片目录不需要登录就能访问。
现象
安全扫描报告给出两条高危:
[高危] 前端静态资源包含敏感地理信息
文件: /assets/index-3e8a21.js
命中: "变电站","管线走向",116.39,39.90
[高危] 对象存储桶允许匿名读取
GET https://oss.example.cn/gis/secret/12/3412/1420.pbf
HTTP/1.1 200 OK
x-oss-object-acl: public-read
这两个问题都指向同一件事:数据没有分级,处理流程里也没有隔离边界。
原因
| 环节 | 错误做法 | 后果 |
|---|---|---|
| 前端数据 | import data from './secret.geojson' | 原始坐标进 bundle,任何人可解包查看 |
| 瓦片发布 | 切片直接上传公共桶 | URL 可猜测,无需鉴权即可批量下载 |
| 截图外发 | 在公开底图上叠加敏感图层后截图 | 图像本身即泄露载体 |
| 测试环境 | 用生产数据做演示 | 测试账号权限宽,扩散面大 |
排查过程
- 在构建产物中全文搜索敏感字段名与坐标数字。
- 检查
src下是否有.geojson、.shp、.gpkg被直接引用。 - 用未登录的
curl请求瓦片与数据接口,确认是否返回 200。 - 检查对象存储桶策略与 CDN 回源配置。
grep -r "secret\|classified\|管线" dist/assets/ | head
curl -I "https://oss.example.cn/gis/secret/12/3412/1420.pbf"
# 期望 403;返回 200 即为公开可读
解决方案
敏感数据不进入前端源码。改为运行时从受控接口获取,接口做身份与权限校验:
// 构建时排除数据目录,避免被打包
// vite.config.js
export default {
build: {
rollupOptions: {
external: [],
output: { manualChunks: undefined }
}
},
assetsInclude: ['**/*.geojson'] // 仍需确认没有被 import
};
// 运行时按权限请求
fetch('/api/secure/features', {
headers: { Authorization: 'Bearer ' + token },
credentials: 'include'
}).then(function (r) {
if (r.status === 403) throw new Error('无权限');
return r.json();
});
对外展示的数据先做脱密处理:删减敏感要素、降低精度、偏移坐标, 并保留可追溯的脱密记录:
function generalize(features, tolerance) {
return features.map(function (f) {
return {
type: 'Feature',
properties: { category: f.properties.category }, // 去掉名称与编号
geometry: simplify(f.geometry, tolerance) // 降低几何精度
};
});
}
瓦片与附件放私有桶,通过签名 URL 或反向代理鉴权后转发,禁止匿名读取:
# 对象存储桶策略:仅允许指定角色读取
{
"Version": "1",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "oss:GetObject",
"Resource": "acs:oss:*:*:gis/secret/*"
}
]
}
Nginx 侧加一层鉴权,未通过时直接拒绝,不把请求转发到存储:
location /secure-tiles/ {
auth_request /auth;
proxy_pass https://oss.example.cn/gis/secure/;
add_header Cache-Control "private, no-store";
}
location = /auth {
internal;
proxy_pass http://127.0.0.1:8080/api/auth/check;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
截图与外发场景加可见水印与操作审计,便于追溯:
viewer.scene.postRender.addEventListener(function () {
var ctx = overlayCanvas.getContext('2d');
ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
ctx.fillStyle = 'rgba(255,255,255,0.25)';
ctx.font = '16px sans-serif';
ctx.fillText('内部使用 · ' + operatorId + ' · ' + Date.now(), 24, 40);
});
验证
构建产物全文搜索不再命中敏感字段与精确坐标。 未登录请求瓦片返回 401 或 403,登录后按权限返回对应范围。 对象存储桶策略显示为私有,CDN 不再直接回源公开路径。 脱密后的展示数据与原始数据在要素数量、精度上有明确差异,并有记录可查。
先分级再谈技术:把数据按敏感级别分类,决定哪些可以进前端、 哪些只能服务端渲染、哪些根本不能出内网。技术手段只是执行分类结果。
经纬志(2026)。《敏感数据被打进前端包,公网可下载》。https://www.jingweizhi.com/cookbook/sensitive-data-leak.html(访问日期 2026-11-03)。