3.1 refer模块防盗
Nginx 用于实现防盗链功能的模块为 refer 模块,其依据的原理是: 如果网站盗用了你的图片,那么用户在点击或者查看这个盗链内容时,发送 http 请求的头部中的 referer 字段将为该盗版网站的 url。这样我们通过获取这个头部信息,知道 http 发起请求的页面,然后判断这个地址是否是我们的合法页面,不是则判断为盗链。Nginx 的 referer 模块中有3个指令,用法分别如下:
Syntax: referer_hash_bucket_size size;
Default: referer_hash_bucket_size 64;
Context: server, location
Syntax: referer_hash_max_size size;
Default: referer_hash_max_size 2048;
Context: server, location
Syntax: valid_referers none | blocked | server_names | string ...;
Default: —
Context: server, location
最重要的是 valid_referers 指令,它后面可以带上多个参数,表示多个 referer 头都是有效的。它的参数形式有:
n
one: 允许缺失 referer 头部的请求访问
blocked: 有 referer 这个字段,但是其值被防火墙或者是代理给删除了
server_names: 若 referer 中的站点域名和 server_names 中的某个域名匹配,则允许访问
任意字符或者正则表达式
Nginx 会通过查看 referer 字段和 valid_referers 后面的 referer 列表进行匹配,如果匹配到了就将内置的变量$invalid_referer值设置为0,否则设置该值为1
Nginx 防盗链配置如下:
...
location / {
valid_referers none blocked *.domain.pub www.domain.com/nginx server_names ~\.baidu\.;
if ($invalid_referer) {
return 403;
}
return 200 "valid\n";
}
...
3.2 secure_link模块防盗
referer 头部值的防盗链方法过于脆弱,盗用者很容易通过伪造 referer 的值轻而易举跳过防盗措施。在 Nginx 中有一种更为高级的防盗方式,即基于 secure_link 模块,该模块能够检查请求链接的权限以及是否过期,多用于下载服务器防盗链。这个模块默认未编译进 Nginx,需要在源码编译时候使用 --with-secure_link_module 添加。
该模块的通过验证 URL 中的哈希值的方式防盗链。它的防盗过程如下:
由服务器或者 Nginx 生成安全的加密后的 URL, 返回给客户端;
客户端使用安全的 URL 访问 Nginx,获取图片等资源,由 Nginx 的 secure_link 变量判断是否验证通过;
secure_link 模块中总共有3个指令,其格式和说明分别如下:
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, secure_link_md5 指令,可实现对链接进行权限以及过期检查判断的功能。
和 referer 模块中的 $invalid_referer 变量一样,secure_link 模块也是通过内置变量 KaTeX parse error: Expected ‘EOF’, got ‘判’ at position 14: secure_link 判̲断验证是否通过。secure_link 的值有如下三种情况:
空字符串: 验证不通过
0: URL 过期
1: 验证通过
通常使用这个模块进行 URL 校验,我们需要考虑的是如何生成合法的 URL ?另外,需要在 Nginx 中做怎样的配置才可以校验这个 URL?
生成合法的 URL 和 指令 secure_link_md5 有关。例如:
secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
如果 Nginx 中secure_link_md5 是上述配置,那么生成合法 url 的命令如下:
# 2020-02-05 21:00:00 转换成时间戳为1580907600
echo -n '1580907600/test.png127.0.0.1 secret' | \
openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
通过上述命令,我们得到了一个 md5 值:cPnjBG9bAZvY_jbPOj13mA,这个非常重要。接下来,构造合的 URL 和指令 secure_link 相关。如果 secure_link 指令的配置如下:
secure_link $arg_md5,$arg_expires;
那么我们的请求的 url 中必须带上 md5 和 expires 参数,例如:
http://180.76.152.113:9008/test.png?md5=cPnjBG9bAZvY_jbPOj13mA&expires=1580907600
对于 Nginx 中的校验配置示例如下:
location ~* .(gif|jpg|png|swf|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 "URL过期,请重新生成";
}
root /root/test;
}
在 Nginx 的配置中,除了前面提到的 secure_link 和 secure_link_md5 指令外,我们对通过校验和校验失败的情况进行了处理。接下来请看实验部分。