一、referer模块防盗
Nginx 用于实现防盗链功能的模块为 refer 模块,其依据的原理是: 如果网站盗用了你的图片,那么用户在点击或者查看这个盗链内容时,发送 http 请求的头部中的 referer 字段将为该盗版网站的 url。这样我们通过获取这个头部信息,知道 http 发起请求的页面,然后判断这个地址是否是我们的合法页面,不是则判断为盗链。
Nginx 的 referer 模块中有3个指令,用法分别如下:(引自Nginx doc)
Syntax: referer_hash_bucket_size size;
Default:
referer_hash_bucket_size 64;
Context: server, location
This directive appeared in version 1.0.5.
Syntax: referer_hash_max_size size;
Default:
referer_hash_max_size 2048;
Context: server, location
This directive appeared in version 1.0.5.
Syntax: valid_referers none | blocked | server_names | string …;
Default: —
Context: server, location
valid_referers 指令,它后面可以带上多个参数,表示多个 referer 头都是有效的。它的参数形式有:
- none : 允许缺失 referer 头部的请求访问
- blocked : 有 referer 这个字段,但是其值被防火墙或者是代理给删除了
- server_names : 若 referer 中的站点域名和 server_names 中的某个域名匹配,则允许访问
任意字符或者正则表达式
Nginx 会通过查看 referer 字段和 valid_referers 后面的 referer 列表进行匹配,如果匹配到了就将内置的变量$invalid_referer值设置为0,否则设置该值为1
官方示例如下:
valid_referers none blocked server_names
*.example.com example.* www.example.org/galleries/
~\.google\.;
if ($invalid_referer) {
return 403;
}
接下来我们开始配置,首先在html目录下创建一个img文件夹,放入一张test.jpg图片(由于我是docker运行的nginx,所以我的目录位置在/Users/docker/nginx/html/img/下)
配置好nginx.conf,localhost/test.jpg我们是可以正常访问的,如下:
接下来配置referer模块
location ~* \.(gif|jpg|jpeg|png|bmp|webp) {
#因为我们是通过浏览器直接发起请求,所以referer为空,所以这里不能配置none参数,也就是不允许确实referer头部
valid_referers blocked server_names domain.com ~\.baidu\.;
if ($invalid_referer) {
add_header Content-Type 'text/html; charset=utf-8';
return 200 "Hi bro , you don't have access";
#return 403;
}
root $doc_root/img;
}
重启测试

进入if语句,成功拦截请求 或者我们打开控制台进行如下操作:
~/ curl -H 'referer: http://domain.com/test.jpg' http://localhost/test.jpg --output /Users/yangkai/Downloads/123.jpg
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 60674 100 60674 0 0 3063k 0 --:--:-- --:--:-- --:--:-- 4232k
~/ curl -H 'referer: http://www.baidu.com' http://localhost/test.jpg --output /Users/yangkai/Downloads/baidu.jpg
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 60674 100 60674 0 0 2154k 0 --:--:-- --:--:-- --:--:-- 2693k

可以看到第一个http请求 referer 的值存在,并且匹配成功domain.com,第二个http请求匹配了后面的域名正则表达,都通过了 referer 校验,所以都下载成功图片。所以从侧面也应证了我们可以通过构造不同的referer头部字段来绕过nginx的referer模块校验,说明这种防盗方式极不靠谱。
二、secure_link 防盗链测试
在使用nginx的secure_link_module模块前可以通过nginx -V查看是否安装模块,如下:

Nginx 的 secure_link_module 模块中有3个指令,用法分别如下:(引自Nginx doc)
Syntax: secure_link expression;
Default: —
Context: http, server, location
Syntax: secure_link_md5 expression;
Default: —
Context: http, server, location
Syntax: secure_link_secret word;
Default: —
Context: location
通过文档中的例子可以得知,secure_link_secret word;这个参数 是用于非日期校验的,可以先不管,这里配置好nginx.conf
location ~* .(jpg|png|flv|mp4)$ {
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
# 空字符串,校验不通过
if ($secure_link = "") {
return 403;
}
# 时间过期
if ($secure_link = "0") {
return 410;
}
# 校验通过,访问对的静态资源
root $doc_root/img;
}
重启nginx,这个时候我们访问test.jpg,可以发现403 Forbidden,说明安全模块生效。
当前时间为2022年10月29日02:38:43,我们找一个三点的过期时间,得到相应的时间戳为1666983600。按照 secure_link_md5 指令格式,使用如下 shell 命令生成 md5 值:
#这里的172.17.0.1是我docker容器中的网关,这里其实就是$remote_addr的值,
#因为我的nginx在dokcer容器中启动的,相当于走了一层代理,所以ip是172.17.0.1,
#如果是部署在linux下,这里的值应当为127.0.0.1
echo -n '1666983600/test.jpg172.17.0.1 secret' | \
openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
1kfMlSx5Y1pGKF6JyIyo3g
这样可以得到我们的安全访问 URL 为:
localhost/test.jpg?md5=1kfMlSx5Y1pGKF6JyIyo3g&expires=1666983600
再次到浏览器上访问时候,我就可以看到静态图片了。

此外,我们还可以等到3点之后,测试过期后的结果。在过期之后再用这个 URL 访问时无法查看图片,而且返回的是 410 的状态码,这说明 Nginx 成功检测到这个密钥值已经过期。