起因
前阵子有个朋友问我:
"我们公司的 PDF 文档被客户右键另存为全都下载走了,想防一下,有什么办法?"
我给了他两行 CSS。他半小时后回复我:"成了。但为什么第三天又泄漏了?"
这篇就是答案。
第一层:2 行代码挡新手
先给代码,再解释:
<iframe src="/static/doc.pdf"
style="pointer-events: none;"></iframe>
<script>document.oncontextmenu = () => false;</script>
两行做完三件事:
1. pointer-events: none → PDF 插件接收不到鼠标事件,右键菜单出不来
2. oncontextmenu = false → 整个页面的右键菜单被禁用
3. iframe 包裹 → PDF 的 URL 不会出现在浏览器地址栏
这能挡住 90% 的普通用户。他们右键了,发现没反应,以为"文档就是下不了",就算了。
但这 90% 里没有一个真正想下文档的人。
第二层:工具党的绕过
稍微懂点的人,会用这三招里的任何一招:
- 打开 DevTools,从 Network 面板找到 PDF 的真实 URL,直接下载
- 禁用 JavaScript,所有 oncontextmenu 防御直接失效
- 用浏览器插件(比如 Enable Right Click),强制恢复右键
这时候你要加第二层:
GET /pdf?token=abc123&ts=1719000000 → 校验 token 是否绑定当前 session → 校验 ts 是否在 60s 内 → 校验 Referer 是否来自自己的预览页这招的核心不是"挡",而是"让每个 PDF URL 只能用一次,且只能在预览上下文里用"。
DevTools 拿到的 URL,复制到别的地方打开,直接 403。
第三层:截屏党怎么办
当对方打开手机拿另一台设备拍屏幕时,你就接受"技术上防不住"这个事实。
但你可以做两件事降低它的价值:
1. 动态水印:每次渲染时,在 PDF 上方叠一层半透明的 canvas,写入"用户 ID + 时间戳 + IP"。截屏出去后,可以溯源到人。
2. 审计埋点:记录"谁在什么时候打开过这份文档多久"。截屏流出后,用水印+日志组合定位泄漏源。
// 极简版水印实现 const canvas =
document.createElement('canvas'); canvas.style.cssText = 'position:absolute;
top:0; left:0; pointer-events:none; opacity:0.08;'; const ctx =
canvas.getContext('2d'); ctx.rotate(-Math.PI / 6); ctx.font = '14px
sans-serif'; const watermark =
`${userId} · ${new Date().toLocaleString()} · ${clientIP}`; for (let y = 0; y
< canvas.height; y += 80) { for (let
x = 0; x < canvas.width; x += 200) {
ctx.fillText(watermark, x, y); }
} previewContainer.appendChild(canvas);
我朋友第三天漏的是什么
回到开头那个问题。
他发的是招标文件,竞争对手拿到了原始 PDF——不是从网页扒的,是从邮箱转发链泄漏的。
这就是这类防御的本质:
你防得住浏览器里的人,防不住浏览器外的人。
真要做全链路防护,得接着做:
- 邮件水印
- 下载行为日志
- DLP(数据防泄漏)策略
- 关键文档只给"一次性查看链接"
一句话总结
防 PDF 泄漏这件事,2 行代码能挡 90% 的人,但挡不住 10% 真正想要它的人。
所以防御策略永远不是"堵死",而是"提高到让攻击方放弃"。
你防御的不是泄漏本身,你防御的是泄漏的成本。
下一篇《BotD 凭什么抓爬虫?又凭什么抓不住我?》,从防御到攻击的完整拆解,下周见。
这篇如果对你有用,点个赞让更多人看到。评论区欢迎讨论你踩过的 PDF 防泄漏坑。