Referrer-policy
在页面引入图片、JS 等资源,或者从一个页面跳到另一个页面,都会产生新的 HTTP 请求,浏览器一般都会给这些请求头加上表示来源的 Referrer 字段。Referrer 在分析用户来源时很有用,有着广泛的使用。但 URL 可能包含用户敏感信息,如果被第三方网站拿到很不安全。之前浏览器会按自己的默认规则来决定是否加上 Referrer。
包含以下值:
no-referrer : 整个referee首部会被移除,访问来源信息不随着请求一起发送。
no-referrer-when-downgrade : 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP).
origin: 在任何情况下,仅发送文件的源作为引用地址。
origin-when-cross-origin: 对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅发送文件的源。
same-origin: 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。
strict-origin: 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。
strict-origin-when-cross-origin: 对于同源的请求,会发送完整的URL作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。
unsafe-url: 无论是同源请求还是非同源请求,都发送完整的 URL(作为引用地址。
常见防盗链方法
防盗链一般有下面几种方式:
- 动态文件名,或者定期修改文件名称或路径
- 判定引用地址,一般是判断浏览器请求时HTTP头的Referer字段的值
- 使用登录验证,cookie
- 图片加水印
- …
利用nginx
ngx_http_referer_module用于阻挡来源非法域名的请求 nginx指令valid_refers,全局变量$invalid_refer 对资源的防盗链nginx配置为
location ~.*\.(gif|jpg|png|bmp|flv|swf|rar|zip)$
{
valid_referers none blocked test.com *.test.com; // 加none的目的是确保浏览器可以直接访问资源
if($invalid_referer)
{
#return 403; // 直接返回403
rewrite ^/ http://www.test.com/403.jpg; // 返回指定提示图片
}
}
这种方法是在server或者location段中加入:valid_referers
。这个指令在referer头的基础上为 $invalid_referer
变量赋值,其值为0或1。如果valid_referers
列表中没有Referer头的值, $invalid_referer
将被设置为1。
如果 $invalid_referer
等于 1,在if语句中返回一个 403 给用户,这样用户便会看到一个 403 的页面, 如果使用下面的rewrite,那么盗链的图片都会显示 403.jpg。
该指令支持none和blocked:
防止网址被 iframe
在页面底部或其它公用部位加入如下代码:
// 用js方法检测地址栏域名是不是当前网站绑定的域名,如果不是,则跳转到绑定的域名上来,这样就不怕网站被别人iframe了
if(window!=parent) {
window.top.location.href = window.location.href;
}
服务器端判断referer
我们能通过对比req.headers[‘referer’]和req.url中的host来确认资源请求是否是别的站点发来的。 接着,当我们知道了资源请求的来源,我们就能通过一系列手段来决定是否响应请求以及怎样响应。 通常的做法是设置一个白名单,在白名单内的请求我们就响应,否则就不响应。
let http = require("http");
let fs = require("fs");
let url = require("url");
let path = require("path");
// 白名单
const whiteList = ["localhost:8080"];
/**
* 三种情况下允许引用图片:
* 1. 本网站
* 2. 无referer信息的情况。(服务器认为是从浏览器直接访问的图片URL,所以这种情况下能正常访问)
* 3. 授权的网址。(配置白名单)
*/
http
.createServer(function (req, res) {
let refer = req.headers["referer"] || req.headers["refer"];
console.log('refer----', refer, req.url);
res.setHeader("Access-Control-Allow-Origin", "*");
if (refer) {
let referHostName = url.parse(refer, true).host;
let currentHostName = url.parse(req.url, true).host;
console.log(referHostName, currentHostName, '--==')
// 当referer不为空, 但host未能命中目标网站且不在白名单内时, 返回错误的图
if (
referHostName != currentHostName &&
whiteList.indexOf(referHostName) == -1
) {
res.setHeader("Content-Type", "image/jpeg");
fs.createReadStream(path.join(__dirname, "/src/img/403.jpg")).pipe(res);
return;
}
}
// 当referer为空时, 返回正确的图
res.setHeader("Content-Type", "image/jpeg");
fs.createReadStream(path.join(__dirname, "/src/img/1.jpg")).pipe(res);
})
.listen(9999);
利用http启动一个客户端:
client.js
let http = require("http");
let fs = require("fs");
let url = require("url");
let path = require("path");
// 创建服务器
http.createServer(function (req, res) {
let staticPath = path.join(__dirname, "src");
let pathObj = url.parse(req.url, true);
if (pathObj.pathname === "/") {
pathObj.pathname += "index.html";
}
// 读取静态目录里面的文件,然后发送出去
let filePath = path.join(staticPath, pathObj.pathname);
fs.readFile(filePath, "binary", function (err, content) {
if (err) {
res.writeHead(404, "Not Found");
res.end("<h1>404 Not Found</h1>");
} else {
res.writeHead(200, "Not Found");
res.write(content, "binary");
res.end();
}
});
}).listen(8080);
index.html
<div id="container">
<img src="http://localhost:9999">
</div>