在互联网应用中,静态资源的安全至关重要。如果不加以保护,他人可以轻易地盗链你的静态资源,这不仅会增加你的服务器带宽成本,还可能影响正常用户的访问体验。今天,我们就来聊聊如何利用 Nginx 实现静态资源的防盗链,主要通过 referer 与 token 验证这两种方式。
一、什么是静态资源防盗链
在深入了解具体的配置方法之前,我们得先弄清楚什么是静态资源防盗链。简单来说,静态资源指的是那些不经常变化的文件,像图片、CSS、JavaScript 文件等。而盗链就是指其他网站未经授权,直接引用你网站上的这些静态资源。比如说,A 网站有一张很吸引人的图片,B 网站直接在自己的页面里引用了这张图片的链接,这样用户访问 B 网站时,实际上是从 A 网站的服务器上加载这张图片。这就会导致 A 网站的带宽被大量占用,增加成本。静态资源防盗链就是要防止这种情况的发生,确保只有授权的访问才能获取这些资源。
二、基于 referer 的防盗链配置
原理
Referer 是 HTTP 请求头的一部分,它会记录请求是从哪个页面发起的。我们可以通过检查这个 Referer 字段,来判断请求是否来自合法的来源。如果 Referer 不在我们允许的列表中,就拒绝该请求。
Nginx 配置示例
以下是一个基于 Nginx 的 referer 防盗链配置示例(使用 Nginx 技术栈):
server {
listen 80;
server_name example.com;
# 定义允许的 referer 列表
valid_referers none blocked example.com *.example.com;
location /static/ {
# 检查 referer
if ($invalid_referer) {
return 403; # 如果 referer 不合法,返回 403 禁止访问
}
root /var/www/html; # 静态资源的根目录
}
}
代码解释
valid_referers:这一行定义了允许的 referer 列表。none表示没有 referer 的请求是允许的,blocked表示请求头中带有 Referer 字段,但该字段被防火墙或代理服务器过滤掉的请求是允许的,example.com和*.example.com表示来自example.com及其子域名的请求是允许的。$invalid_referer:这是一个 Nginx 变量,如果请求的 referer 不在valid_referers列表中,该变量的值为 1,否则为 0。return 403:当$invalid_referer为 1 时,返回 403 状态码,表示访问被禁止。
优缺点分析
优点
- 配置简单:只需要在 Nginx 配置文件中添加几行代码,就可以实现基本的防盗链功能。
- 不需要额外的服务器资源:只依赖于 Nginx 自身的功能,不需要额外的服务器资源来处理。
缺点
- 容易被绕过:Referer 字段可以被伪造,攻击者可以通过修改请求头来绕过防盗链机制。
- 灵活性有限:只能根据请求的来源进行判断,无法对请求进行更细粒度的控制。
注意事项
- 确保
valid_referers列表的准确性:如果列表中包含了错误的域名,可能会导致合法的请求被拒绝。 - 考虑没有 referer 的请求:有些情况下,请求可能没有 referer 字段,比如直接在浏览器地址栏中输入资源的 URL。在配置时,需要根据实际情况决定是否允许这类请求。
三、基于 token 的防盗链配置
原理
Token 是一种身份验证的方式。我们可以为每个请求生成一个唯一的 token,并将其包含在请求的 URL 中。服务器在接收到请求时,会验证这个 token 的有效性。如果 token 有效,就允许访问资源;否则,拒绝请求。
实现步骤
1. 生成 token
在服务器端,我们可以使用一些算法(如 HMAC-SHA256)来生成 token。以下是一个使用 Python(Flask 框架)生成 token 的示例:
import hmac
import hashlib
import time
SECRET_KEY = "your_secret_key"
def generate_token(path, expire=3600):
timestamp = str(int(time.time() + expire))
message = f"{path}{timestamp}"
signature = hmac.new(SECRET_KEY.encode(), message.encode(), hashlib.sha256).hexdigest()
return f"{timestamp}-{signature}"
代码解释
SECRET_KEY:这是一个用于生成 token 的密钥,需要妥善保管。generate_token函数:接受资源路径path和过期时间expire作为参数。首先,计算当前时间加上过期时间的时间戳timestamp。然后,将资源路径和时间戳拼接成一个字符串message。最后,使用 HMAC-SHA256 算法对message进行签名,生成signature。将时间戳和签名用-连接起来,就得到了最终的 token。
2. 在 URL 中添加 token
在生成资源的 URL 时,将生成的 token 添加到 URL 中。例如:
path = "/static/image.jpg"
token = generate_token(path)
url = f"http://example.com{path}?token={token}"
print(url)
3. Nginx 验证 token
在 Nginx 中,我们可以使用 Lua 脚本来验证 token。以下是一个使用 OpenResty(基于 Nginx 和 Lua)验证 token 的示例:
server {
listen 80;
server_name example.com;
location /static/ {
access_by_lua_block {
local ngx = ngx
local args = ngx.req.get_uri_args()
local token = args["token"]
if not token then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Token is missing")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
local parts = {}
for part in string.gmatch(token, "[^-]+") do
table.insert(parts, part)
end
local timestamp = tonumber(parts[1])
local signature = parts[2]
if not timestamp or not signature then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Invalid token format")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
if timestamp < ngx.time() then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Token has expired")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
local path = ngx.var.uri
local message = path .. tostring(timestamp)
local secret_key = "your_secret_key"
local calculated_signature = ngx.hmac_sha256(secret_key, message)
if calculated_signature ~= signature then
ngx.status = ngx.HTTP_FORBIDDEN
ngx.say("Invalid token signature")
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
root /var/www/html;
}
}
代码解释
access_by_lua_block:这是 OpenResty 的一个指令,用于在处理请求时执行 Lua 脚本。- 首先,从请求的 URL 中获取 token。如果没有 token,返回 403 错误。
- 然后,将 token 拆分为时间戳和签名两部分。
- 检查时间戳是否过期,如果过期,返回 403 错误。
- 最后,重新计算签名,并与请求中的签名进行比较。如果不匹配,返回 403 错误。
优缺点分析
优点
- 安全性高:Token 难以伪造,因为它是使用密钥和特定算法生成的。
- 灵活性强:可以对每个请求进行细粒度的控制,比如设置不同的过期时间。
缺点
- 实现复杂:需要在服务器端和客户端都进行相应的开发,增加了开发成本。
- 性能开销:验证 token 需要一定的计算资源,可能会对服务器性能产生一定的影响。
注意事项
- 保管好密钥:密钥是生成和验证 token 的关键,必须妥善保管,防止泄露。
- 合理设置过期时间:过期时间设置得太短,可能会导致用户在操作过程中频繁需要重新获取 token;设置得太长,会增加 token 被滥用的风险。
四、结合 referer 和 token 的防盗链配置
为了提高防盗链的安全性,我们可以将 referer 和 token 验证结合起来。以下是一个示例配置:
server {
listen 80;
server_name example.com;
# 定义允许的 referer 列表
valid_referers none blocked example.com *.example.com;
location /static/ {
# 检查 referer
if ($invalid_referer) {
return 403;
}
access_by_lua_block {
-- 验证 token 的 Lua 脚本,同上面的示例
}
root /var/www/html;
}
}
优点
- 双重保障:结合了 referer 和 token 的优点,既可以根据请求来源进行初步筛选,又可以通过 token 进行更严格的身份验证,大大提高了防盗链的安全性。
注意事项
- 配置复杂度增加:需要同时配置 referer 和 token 验证,增加了配置的复杂度。在配置过程中,需要确保两个验证机制的逻辑正确,避免出现冲突。
五、应用场景
图片分享网站
对于图片分享网站来说,图片是核心资源。如果不进行防盗链,其他网站可能会大量盗链这些图片,导致网站的带宽成本大幅增加。通过 Nginx 的防盗链配置,可以有效防止这种情况的发生,保护网站的资源和利益。
视频网站
视频网站的带宽消耗非常大,盗链视频会给网站带来巨大的成本压力。使用 referer 和 token 验证的防盗链机制,可以确保只有合法的用户和网站才能访问视频资源,减少不必要的带宽浪费。
软件下载网站
软件下载网站的静态资源主要是各种软件安装包。盗链这些安装包会影响网站的下载统计和收入。通过防盗链配置,可以控制只有授权的用户才能下载软件,提高网站的安全性和收益。
六、文章总结
通过本文的介绍,我们了解了如何使用 Nginx 实现基于 referer 和 token 的静态资源防盗链配置。Referer 验证简单易行,但容易被绕过;Token 验证安全性高,但实现复杂。在实际应用中,我们可以根据具体的需求和场景,选择合适的防盗链方式,或者将两者结合起来使用,以达到最佳的防盗链效果。同时,在配置过程中,要注意各种细节和注意事项,确保防盗链机制的有效性和稳定性。
评论