1.前言
在这个流量为王的时代,掌握了流量密码,就相当于掌握了一切皆有可能的机遇,但是,越是流量为王,越该保持敬畏之心。
资源在获得流量的过程中扮演着极其重要的角色,如果我们的资源没有适当的保护机制,就会造成很大的损失。为了保护资源,我们可以添加防盗链机制。
Nginx对实现静态资源防盗链机制提供了很好的支持,接下来我们进入实战阶段。主要分为以下两种方式:
Referer信息校验
secure_link资源保护
2.Referer信息校验
在【Nginx静态资源防盗链】一文中,已经详细介绍过实现方法,我们这里简单回顾一下。
2.1 实现原理
当浏览器向web服务器发起资源请求时,会在请求头中带上Referer
字段,告诉服务器请求是从哪个页面过来的,服务器根据该信息进行校验并响应。
Nginx为我们提供了ngx_http_referer_module
模块,可以获取请求头中Referer
字段,根据字段值返回不同的响应,就可以达到如果不是从我们自己网站发起的请求,就直接返回403禁止访问。
2.2 配置指令
作用域:server, location
语法:valid_referers none | blocked | server_names | string …;
1)valid_referers none;
表示请求头中不存在Referer字段。
2)valid_referers blocked;
表示请求头中存在Referer字段,且其值不以http://
或https://
开头。
3)valid_referers server_names;
表示请求头中存在Referer字段,且其值包含nginx配置文件中server_name的其中一个。
4)任意字符串
表示请求头中存在Referer字段,且定义了服务器名称和可选的URI前缀。服务器名称的开头或结尾可以有一个“*”。在检查过程中,“Referer”字段中的服务器端口被忽略。例如*.example.com example.* www.example.org/galleries/
5)正则表达式
表示请求头中存在Referer字段,且第一个符号应该是“~”(Nginx解析正则表达式规范)。需要注意的是,表达式将从http://
或https://
之后开始的文本相匹配。
6)配置示例
valid_referers none blocked server_names *.example.com test.example.* ~\.example\.com
2.3 实战
除了使用valid_referers
指令外,我们还需要用到一个内置变量$invalid_referer
,如果Referer
请求标头字段值被认为有效,则$invalid_referer
的值为空字符串,否则为“1”。
# 如果获取图片时,不是从www.example.com页面发起的请求,则禁止访问。
location ~^/.*\.(png|jpg|gif|jfif) {
valid_referers www.example.com;
if ($invalid_referer){
return 403;
}
root html;
}
🔔Tips:请求头的Referer字段信息是可以通过程序伪装生成的,因此根据Referer信息来实现防盗链并非100%可靠,但是,它能够限制大部分的盗链。
3.secure_link资源保护
前面我们提到,在使用Referer
信息进行校验的时候,该字段信息可以通过程序伪装生成,并非100%可靠。
Nginx提供了ngx_http_secure_link_module
模块,可以对请求的链接进行真伪校验,并限制链接的有效时间。
官方文档:https://nginx.org/en/docs/http/ngx_http_secure_link_module.html
话不多少,开整。
🔔Tips:该模块默认不构建,构建时需要自行添加configure参数–with-http_secure_link_module。
构建过程,可参照【Nginx基本命令、平滑升级】进行。
2.1 实现原理
资源链接按照约定的规则生成,Nginx服务器对链接进行真伪以及过期时间校验。
2.2 配置指令
ngx_http_secure_link_module
模块为我们提供了两种校验模式:
- 通过
secure_link_secret
指令,检查链接的真实性。 - 通过
secure_link
与secure_link_md5
指令,检查链接的真实性,并限制链接的有效时间。
接下来看看具体配置语法吧。
2.2.1 secure_link_secret
secure_link_secret
指令用于指定需要通过MD5算法加密的字符串,同时需要配合$secure_link
内置变量来对链接进行校验。
作用域:location
语法:secure_link_secret word;
word
:需要进行MD5加密的字符串,例如your_secure_word。
URI格式
:/prefix/hash/link
,例如/img/6b105c468b2a59681b799a7f532505e3/aa.jpg。
- prefix:为不包含
/
的任意字符串,这里为img。 - link:这里为aa.jpg。
- hash:这里为
link
与word
拼接之后的值(aa.jpgyour_secure_word)进行MD5加密后的结果。
$secure_link
:内置变量,如果链接通过了真实性检查,则其值设置为从/prefix/hash/link
中提取link
的值(包含前面的/
),否则将其设置为空字符串。
2.2.2 secure_link
secure_link
指令定义需要从URI中取出的参数,包含MD5值和链接的有效时间。需要与secure_link_md
指令配合使用。
作用域:http, server, location
语法:secure_link expression;
示例:
http://192.168.110.101/img2/test.jfif?signature=vQ5_wrXQ_oxh5c3L9bbf8g&expires=1685003128
# $arg_signature,$arg_expires意味着从URI中取出参数名为signature和expires的值
secure_link $arg_signature,$arg_expires;
2.2.3 secure_link_md5
secure_link_md5
指令定义一个表达式,计算表达式的MD5值,与URI传递的MD5值进行比较。需要配合secure_link
指令和$secure_link_expires
内置变量一起使用。
🔔Tips:URI中MD5值以base64编码的形式传递。
作用域:http, server, location
语法:secure_link_md5 expression;
配置示例:
secure_link $arg_signature,$arg_expires;
secure_link_md5 "$secure_link_expires$uri secret";
$secure_link_expires
用于获取secure_link
中传递的expires的值。
secure_link
与secure_link_md5
指令的校验步骤如下:
- 如果
secure_link
指令从URI参数中提取的signature
值与secure_link_md5
指令计算出的MD5值不匹配,则将$secure_link
设置为空字符串; - 如果匹配,就校验链接的有效期,如果已过期则将
$secure_link
设置为"0",否则设置为"1"。
URI中的signature值可以通过以下方式获取:
1)Ubuntu命令行执行
echo -n '1685003128/img2/test.jfif secret' | \
openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
2)Java代码
引入Apache的commons-codec包
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
public class MD5 {
public static void main(String[] args) {
Base64.encodeBase64URLSafeString(DigestUtils.md5("1685003128/img2/test.jfif secret"));
}
}
2.3 实战
2.3.1 secure_link_secret校验模式
配置示例:
Nginx代理服务器:192.168.110.101
server {
listen 80;
server_name localhost;
location /img {
default_type text/html;
secure_link_secret test;
if ($secure_link = ""){
return 403 "<p>You don't have permission to access the URL on this server.</p>";
}
rewrite ^ /secure$secure_link;
}
location /secure {
proxy_pass http://192.168.110.100/img/;
}
}
服务端:192.168.110.100
location /img {
default_type image/jpeg;
root /home/stone;
}
1)访问http://192.168.110.101/img/test.jfif,不满足链接规则,403禁止访问。
2)访问http://192.168.110.101/img/f5b20551b8d06384734de574dd7930ce/test.jfif,满足链接规则。
2.3.2 secure_link+secure_link_md5
配置示例:
Nginx代理服务器:192.168.110.101
location /img2 {
default_type text/html;
secure_link $arg_signature,$arg_expires;
secure_link_md5 "$secure_link_expires$uri secret";
if ($secure_link = ""){
return 403 "<h1>Forbidden</h1><p>You don't have permission to access the URL on this server.</p>";
}
if ($secure_link = "0"){
return 403 "<h1>Expired</h1><p>You don't have permission to access the URL on this server.</p>";
}
proxy_pass http://192.168.110.100/img/;
}
服务端:192.168.110.100
location /img {
default_type image/jpeg;
root /home/stone;
}
1)访问http://192.168.110.101/img/test.jfif,不满足链接规则,403禁止访问。
2)访问http://192.168.110.101/img2/test.jfif?signature=vQ5_wrXQ_oxh5c3L9bbf8g&expires=1685003128,满足链接规则,且链接未过期。
3)访问http://192.168.110.101/img2/test.jfif?signature=JY7G1brnFeEWcNShV74eNA&expires=1684398328,满足链接规则,但链接已过期。
以上就是Nginx实现静态资源防盗链机制的全部内容,Nginx是多模块化的,还有很多高级功能,我们后面继续探索。