前言
最近巡检nginx日志,发现图片有异常的站外访问。显然有人未经我同意,抄袭我原创的内容,整篇复制HTML内容,连<img>标签里的src都不会改。他网站上访客浏览内容时,图片消耗的是我服务器的流量。
即日起,本站所有配图均添加了防盗链的配置。本文介绍一下防盗链原理和实践经验。
如何防盗链
在http协议的header里,有一个Referer字段,用于标识访问来源。Referer实际上是 "referrer" 误拼写,意思是“推荐人”。对于加载静态资源(图片、JS、CSS等),浏览器会自动带上当前页面的网址。服务器可以用这个作为判断,来禁止本站以外的访问。
以本博客为例,在nginx里增加如下配置:
location ~* \.(gif|jpg|png)$ {
valid_referers blocked *.oonne.com;
if ($invalid_referer) {
return 403;
}
}
此处用到的是nginx的ngx_http_referer_module模块。外面用location路由匹配图片资源,valid_referers表示可信的referer。当本次访问的referer可信时,内置变量$invalid_referer的值为空字符串;不可信时,内置变量$invalid_referer的值为1。以此做if条件判断,对不可信的请求返回状态码403。
valid_referers可选的参数有这些:
- none:允许缺少Referer请求头。在浏览器里直接输入网址,或者盗链网站是https协议、而图片链接是http情况下,浏览器并不会发送Referer参数。
- blocked: 浏览器有带Referer请求头,但是它的值被防火墙或者代理服务器删除。 这些值都不以“http://” 或者 “https://”字符串作为开头。
- server_names:Referer请求头包含某个主机名,不区分大小写。
- 正则表达式:必须以“~”符号作为开头。 表达式会从“http://”或者“https://”之后的文本开始匹配。
如何绕过防盗链
为了方便下载图片,很多网站的防盗链机制不会限制空的Referer。因此盗链者只需要制造一个空的Referer就行了:
- 如果图片地址是http协议的,盗链网址是https协议的,浏览器出于安全考虑,不会带Referer。
- 可以在HTML的头部添加 <meta name="referrer" content="no-referrer" />,全站请求不带Referer。或者给图片标签加上 <img referrer="no-referrer"/> ,单个请求不带Referer。(注意,此处的referrer竟然拼对了)
- 新建一个iFrame,把图片放到iFrame中显示。
- 虽然浏览器限制了XMLHttpRequest不能设置自定义的Referer,但是fetch还是可以将referrer置空。
如果在浏览器以外的场景,没有了浏览器的限制,伪造Referer就更容易了,直接改http header即可。因此,上述防盗链方案仅仅是提高了技术门槛,限制多数普通用户,增加盗链的难度。
如何防止被绕过
禁止为空的Referer:在浏览器里右键打开图片,或者右键下载图片,都会带上原有的Referer。真实用户直接复制图片链接去访问的情况非常少。因此推荐不允许图片的Referer为空。
禁止被iFrame嵌套:可以在http header中配置 X-Frame-Options 为 “deny” 来防止网站和资源被嵌到别人的站点里。即使迫不得已需要用到iFrame,也建议在 X-Frame-Options 中配置可信的域名。
CSP配置也可以对iFrame嵌套做限制,但是旧的浏览器支持并不完整,建议两者配合使用。
如果想对网站的资源做更严格的限制,比如登录之后才能看到图片,可以先种一个cookie。静态资源的请求也是会带上cookie的,服务端再做校验即可。
还可以使用动态文件名,图片资源每次访问的链接地址都是随机生成的,每次的地址都有时效性。很多CDN都有这样的功能。
总结
道高一尺,魔高一丈。防盗链机制做得再好,也防不住直接下载图片再发到别的网站上。还是希望各位看官能尊重一下原创内容。
参考文献
- https://tools.ietf.org/html/rfc7231#section-5.5.2
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。