经纬志
首页 / 实战手册 / 安全与合规

敏感数据被打进前端包,公网可下载

先看结论

例行安全检查发现,构建产物里能直接搜到设施名称与坐标,对象存储的瓦片目录不需要登录就能访问。

最后验证 2026-11-03 · 多技术栈 · 对象存储 · 更新 2026-10-29 · 引用本文

现象

安全扫描报告给出两条高危:

[高危] 前端静态资源包含敏感地理信息
  文件: /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 可猜测,无需鉴权即可批量下载
截图外发在公开底图上叠加敏感图层后截图图像本身即泄露载体
测试环境用生产数据做演示测试账号权限宽,扩散面大

排查过程

  1. 在构建产物中全文搜索敏感字段名与坐标数字。
  2. 检查 src 下是否有 .geojson、.shp、.gpkg 被直接引用。
  3. 用未登录的 curl 请求瓦片与数据接口,确认是否返回 200。
  4. 检查对象存储桶策略与 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)。