一行代码解决 Chrome 对 HTTP 站点 Office 文件的下载拦截

Chrome 拦截 HTTP 站点的文件下载?一行代码改掉它的判断逻辑

问题:用户点导出,Chrome 弹"不安全"

一个内网系统通过 HTTP 提供服务,导出功能生成 .docx 文件。Chrome 每次下载都弹:

此文件可能不安全/可能已被篡改

非技术用户不敢点"保留",以为系统坏了。

排查:代码明明已经用 Blob 下载了

前端代码是这样写的:

// API 层:用 responseType: 'blob' 获取二进制
const response = await request.download({ url: '/api/report/export' })

// 前端层:提取文件名,用 Blob URL 触发下载
download.word(response.data, fileName)

download.word() 内部走的就是 URL.createObjectURL(blob) + <a download> 点击。早就不是浏览器原生下载了——为什么还拦?

后来发现,Chrome 的判断不看下载方式,看服务器说了什么

根因:一个 HTTP 头引发的拦截

后端 Controller 是这样写响应头的:

response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setHeader("Content-Disposition", "attachment; filename=\"工作周报.docx\"");

Chrome 的处理链:

响应头含 Content-Disposition: attachment
  → Chrome 判定:"这是个要下载的文件"
  → 检查:来源是 HTTP?
  → 检查:文件类型在危险名单里?(docx ✅、zip ✅、exe ✅)
  → 弹警告

Content-Disposition: attachment 就是触发 Chrome 下载安全检查的开关。 你关掉它,Chrome 就不再走那条检查链路。

我们试过的三个方案

方案 A:搭 HTTPS(失败)

  • 给测试服务器绑了 mkcert 自签证书
  • 每个开发者/用户都要手动装根证书
  • 非技术用户:"什么叫证书?怎么装?"
  • 放弃

方案 B:data: URI 下载(未遂)

想把 Blob 读成 base64,用 data:application/vnd... 格式触发下载,绕过 HTTP 来源追踪。

data: URI 对大文件不友好(base64 编码膨胀 33%),且逻辑上还是在绕安全机制,不够干净。

方案 C:让服务器闭嘴(成功)

核心思路:既然 Content-Disposition: attachment 是你拦截的判断依据,那我就不发这个头。

文件内容照样传、文件名照样给、下载照样触——只是"这是下载文件"这句话不让服务器说,改由 JS 说。

最终方案:一行改动的全部代码

后端

// 改前
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

// 改后——去掉 Content-Disposition: attachment
response.setHeader("X-Filename", URLEncoder.encode(fileName, StandardCharsets.UTF_8));

前端

// 改前
const disposition = response?.headers?.['content-disposition'] || ''
const match = /filename\*=UTF-8''([^;]+)/i.exec(disposition)
fileName = match ? decodeURIComponent(match[1]) : fallback

// 改后——优先读 X-Filename,兼容旧版 Content-Disposition
const xFilename = response?.headers?.['x-filename'] || ''
if (xFilename) {
  fileName = decodeURIComponent(xFilename)
} else {
  // 兼容旧版
  const match = /filename\*=UTF-8''([^;]+)/i.exec(response?.headers?.['content-disposition'] || '')
  if (match) fileName = decodeURIComponent(match[1])
}

改完部署,用户点导出——不弹警告了。

为什么这样能绕过 Chrome?

改前流程:
  服务器 → 浏览器:"接住,这是个要下载的 .docx 文件"
  浏览器 → "HTTP 来的 .docx?不安全!" → 弹警告

改后流程:
  服务器 → 浏览器:"这是数据流"(没有 attachment 头)
  JS     → 浏览器:"帮我把这段数据保存为文件"(Blob URL + <a download>)
  浏览器 → "JS 让我存数据,不管来源"  → 正常下载

Chrome 不会对 JS 发起的 Blob URL 下载做安全检查——那是用户代码的行为,不是网络层的文件传输。

意外发现:为什么 Excel 不弹警告?

测试过程中发现,同系统的 Excel 导出从不触发警告。看了代码,Excel 和 Word 用的是完全相同的下载链路(responseType: 'blob' + URL.createObjectURL)。

最后查 Chrome 源码确认:Chrome 对 .docx.xlsx 的安全策略不同。 .docx 历史上是宏病毒的常见载体,被列为"高风险"类型;.xlsx 虽然也在危险名单里,但 Chrome 的拦截阈值对 Word 文档更严格。

如果你的系统同时有 Excel 和 Word 导出,别奇怪——不是你代码的问题,是 Chrome 的区别对待。

一句话总结

不要让服务器对 Chrome 说"这是下载文件"(Content-Disposition: attachment)。让 JS 自己下载。Chrome 不拦自己人。

适用场景

  • 内网 HTTP 站点导出 Office 文档(.docx.xlsx.pptx
  • 导出 ZIP 压缩包
  • 任何 Chrome 标记为"不安全下载"的 HTTP 文件传输

局限性

这个方案的本质是绕过 Chrome 的检查,不是修复 HTTP 的安全问题。如果你的系统面向公网或有合规要求,长期方案仍然是上 HTTPS + 域名 + 公网 CA 证书。

posted @ 2026-07-29 15:47  e3tB8Wz7  阅读(9)  评论(0)    收藏  举报