围绕http请求头中Referer展开的一些知识

2023-11-05

1. 什么是referer?

<点击以获取跳转信息 >跳转过去记得按一下f12点击网络请求详情,再刷新一下,就可以看见referer字段:
在这里插入图片描述

当我们尝试在浏览器内部直接输入这熟悉的网址时,此时刷新后则是这样一番景象:
在这里插入图片描述
于是你就明白了referer的基本用途,它是存在于http请求头内部的用于标识访问者来源网页的标识字段。通常在普通用户的访问下是不会出现的,常常出现于各个网页之间的相互跳转。

说到这里你想到了什么,各个网页?嗯…记得这块在网页里面引用别人的东西好像还挺多的。直接把人家的图片地址写下来,就能显示,可方便了。对,这种行为就是盗图,当然只要是可以在网页上访问的网络资源,基本上都会面临这样一种情况:被盗取资源。盗取链接与防止盗取链接形成了一个经久不衰的话题。那么今天我们就通过几个小例子来体会一下盗图与防盗图的斗争吧。好好的理解一下关于referer字段的故事。

Referer请求头包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。服务端一般使用Referer(注:正确英语拼写应该是referrer,由于早期HTTP规范的拼写错误,为了保持向后兼容就一直延续下来)请求头识别访问来源,可能会以此统计分析、日志记录以及缓存优化等。
真有人为了这事情发博客吐槽…哈哈 详细历史见吐槽内容

2. Referrer-policy

言归正传,学习!显然,注意刚刚访问百度的同学可以细心的发现referrer-policy这个引用者策略,其规定了referer的具体使用规则。不同的设置如下给出:
在这里插入图片描述

  • no-referrer : 整个referee首部会被移除,访问来源信息不随着请求一起发送。
  • no-referrer-when-downgrade : 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP).
  • origin: 在任何情况下,仅发送文件的源作为引用地址。例如 https://example.com/page.html 会将 https://example.com/ 作为引用地址。
  • 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(移除参数信息之后)作为引用地址。(最不安全了)

3.设置referrer

可以在HTML里面设置meta标签

<meta name="referrer" content="origin">

也可以用<a>、<area>、<img>、<iframe>、<script> 或者<link> 元素上的 referrerpolicy 属性为其设置独立的请求策略。

比如:

<script src='/javascripts/test.js' referrerpolicy="no-referrer"></script>

注意,如果不对页面进行处理的话,默认的referer-policy的数值是 strict-origin-when-cross-origin

4.绕过防盗链

前面我们说过防盗链的工作原理,其就是通过Referer或者签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以追踪到显示它的网页地址 一旦检测到来源不是本站,即进行阻止或者返回指定的页面。

那么要绕过它就至少的满足下面的三个条件之一:

  1. 本网站。
  2. 无referer信息的情况。(服务器认为是从浏览器直接访问的图片URL,所以这种情况下能正常访问)
  3. 授权的网址。

显然,方法1、3均无法实现。留给我们的方法就只有想办法去除自己在访问时的referer字段了。

4.1 利用https降级访问http资源网站

当我们的目标被盗网站的策略采用默认策略时,我们可以利用访问主动降级的方式将目标图片盗链下来。也就是说我们可以在https的网页中用http请求另一个https网站的资源。此时可以不发送我们的referer字段,达到绕过防盗链的效果。(由于浏览器的升级,现在这种操作已经被禁止了。)虽然如此,我们还是尝试这复现一下这一操作。

示例环境:centos7 安装nodejs环境

用nodejs同时模拟出”盗图人“和“拥图人”。我们使用古老的浏览器尝试访问网页见证这一古老的偷图方法。

1.生成自签名证书

如何在本地为本地的web服务创建自签名成了我们要解决的第一个问题。按照以下步骤创建对应的证书以及签名。在此之前应当创建对应的文件夹:

[root@blackstone ceshi]# mkdir demo01
[root@blackstone ceshi]# mkdir -p ./demo01/server/keys
[root@blackstone ceshi]# mkdir -p ./demo01/server/src
[root@blackstone ceshi]# mkdir -p ./demo01/client/src
[root@blackstone ceshi]# mkdir -p ./demo01/client/keys
[root@blackstone ceshi]# mkdir -p ./demo01/ca
[root@blackstone ceshi]# tree demo01
demo01
├── ca
├── client
│   ├── keys
│   └── src
└── server
    ├── keys
    └── src

好,接下来到这个demo01目录下,进行证书的构造

#1.生成私钥
// 生成服务器端私钥   
openssl genrsa -out server/keys/server.key 1024   
// 生成客户端私钥  
openssl genrsa -out client/keys/client.key 1024

#2.生成公钥
openssl rsa -in server/keys/server.key -pubout -out server/keys/server.pem
openssl rsa -in client/keys/client.key -pubout -out client/keys/client.pem

#3.CA证书自签名
#3.1 创建CA私钥
openssl genrsa -out ca/ca.key 1024

#3.2 生成CA的CSR文件与crt
#生成csr
openssl req -new -key ca/ca.key -out ca/ca.csr
#生成crt
openssl x509 -req -in ca/ca.csr -signkey ca/ca.key -out ca/ca.crt

#3.3 为server发放证书
#生成csr文件
openssl req -new -key server/keys/server.key -out server/keys/server.csr

#签名过程需要CA的证书和私钥参与, 最终颁发一个带有CA签名的证书
openssl x509 -req -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial -in server/keys/server.csr -out server/keys/server.crt

#3.4 为client发放证书
#生成CSR文件
openssl req -new -key client/keys/client.key -out client/keys/client.csr
#签名过程需要CA的证书和私钥参与, 最终颁发一个带有CA签名的证书
openssl x509 -req -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial -in client/keys/client.csr -out client/keys/client.crt

关于CA证书:为了得到签名证书,服务器端需要通过自己的私钥生成CSR(Certificate Signing Request,证书签名请求)文件。CA机构通过这个文件颁发属于该服务器端的签名证书,只要通过CA机构就能验证证书是否合法。
上面用是自签名证书来构建安全网络的。所谓自签名证书,就是自己扮演CA机构,给自己得服务器端颁发签名证书。其过程包括了生成CA私钥、生成CSR文件、通过私钥自签名生成证书

经过上面一通创建之后,我们使用tree确认生成无误
在这里插入图片描述

2. 配置server端

server目录下创建server.js用于建立服务

let https = require("https");
let fs = require("fs");
let url = require("url");
let path = require("path");
// 白名单
const whiteList = ["192.168.2.169:80"];

const options = {
  key: fs.readFileSync("./keys/server.key"),
  cert: fs.readFileSync("./keys/server.crt"),
};


https
  .createServer(options, 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);

监听的是9999端口,用于模拟被偷服务器,在其对应的src目录下放上相应的资源
在这里插入图片描述

3.配置client端

服务端的nodejs文件client.js

let https = require("https");
let fs = require("fs");
let url = require("url");
let path = require("path");


var options = {
  hostname: "localhost",
  port: 8000,
  path: "/",
  method: "GET",
  rejectUnauthorized: false,
  key: fs.readFileSync("./keys/client.key"),
  cert: fs.readFileSync("./keys/client.crt"),
  ca: [fs.readFileSync("../ca/ca.crt")],
};

// 创建服务器
https.createServer(options, 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, "OK");
      res.write(content, "binary");
      res.end();
    }
  });

}).listen(8080);

配置client首页文件

[root@blackstone client]# cat ./src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>client</title>
</head>
<body>
    <h1>client页面</h1>
    <div id="container">
        <!-- <img src="https://192.168.2.169:9999/" referrerpolicy="no-referrer"> -->
        <img src="http://192.168.2.169:9999">
    </div>
    <!-- <script src="js/fetchImg.js"></script> -->
</body>
</html>

4.在浏览器上测试盗链情况

依次分别运行server和client

[root@blackstone server]# node server.js
[root@blackstone client]# node client.js

使用火狐浏览器测试访问:
在这里插入图片描述

可以看到,尽管我们尝试进行盗链,但是因为浏览器的安全限定,无法显示偷出来的图片,我们掏出超低版本的浏览器试试。。。经测试暂时无果,这个古老的方案大抵是被禁用掉了。
点击此处获取老版本浏览器
在这里插入图片描述

4.2 使用meta

<meta name="referrer" content="no-referrer" />

在这里插入图片描述
可以清晰的看到,设置后发出的https请求已经没了referer字段,正常请求到了图片

4.3 设置referrerpolicy=“no-referrer”

在标签旁设置上这个属性就行

<img src="https://192.168.2.169:9999/" referrerpolicy="no-referrer">

4.4 利用iframe伪造请求referer

function showImg(src, wrapper ) {
    let url = new URL(src);
    let frameid = 'frameimg' + Math.random();
    window.img = `<img id="tmpImg" width=400 src="${url}" alt="图片加载失败,请稍后再试"/> `;

    // 构造一个iframe
    iframe = document.createElement('iframe')
    iframe.id = frameid
    iframe.src = "javascript:parent.img;" // 通过内联的javascript,设置iframe的src
    // 校正iframe的尺寸,完整展示图片
    iframe.onload = function () {
        var img = iframe.contentDocument.getElementById("tmpImg")
        if (img) {
            iframe.height = img.height + 'px'
            iframe.width = img.width + 'px'
        }
    }
    iframe.width = 10
    iframe.height = 10
    iframe.scrolling = "no"
    iframe.frameBorder = "0"
    wrapper.appendChild(iframe)
}

showImg('https://192.168.2.169:9999', document.querySelector('#container'))

在这里插入图片描述

4.5 客户端在请求时修改header头部

4.5.1 利用XMLHttpRequest

XMLHttpRequest中setRequestHeader方法,用于向请求头添加或修改字段。我们能不能手动将修改 referer字段呢?
演示代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>client</title>
</head>
<body>
    <h1>client页面</h1>
    <div id="container">
    </div>
</body>
<script src="./03.js"></script>
</html>
// 通过ajax下载图片
function loadImage(uri) {
    return new Promise(resolve => {
        let xhr = new XMLHttpRequest();
        xhr.responseType = "blob";
        xhr.onload = function() {
            resolve(xhr.response);
        };

        xhr.open("GET", uri, true);
        // 通过setRequestHeader设置header不会生效
        // 会提示 Refused to set unsafe header "Referer"
        xhr.setRequestHeader("Referer", ""); 
        xhr.send();
    });
}
  

// 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
function handleBlob(blob) {
    let reader = new FileReader();
    reader.onload = function(evt) {
        let img = document.createElement('img');
        img.src = evt.target.result;
        document.getElementById('container').appendChild(img)
    };
    reader.readAsDataURL(blob);
}

const imgSrc = "https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg";

loadImage(imgSrc).then(blob => {
    handleBlob(blob);
});

在这里插入图片描述
可以看见setRequestHeader设置referer响应头是无效的,这是由于浏览器为了安全起见,无法手动设置部分保留字段,不幸的是Referer恰好就是保留字段之一,详情列表参考Forbidden header name

可见使用xmlhttprequest提供的方法用AJAX同源请求无法完成这一操作。使用fetch可以解决这一问题。

4.5.2 利用fetch

// 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
function handleBlob(blob) {
    let reader = new FileReader();
    reader.onload = function(evt) {
        let img = document.createElement('img');
        img.src = evt.target.result;
        document.getElementById('container').appendChild(img)
    };
    reader.readAsDataURL(blob);
}

const imgSrc = "https://192.168.2.169:9999";


function fetchImage(url) {
    return fetch(url, {
        headers: {
            // "Referer": "", // 这里设置无效
        },
        method: "GET",  
        referrer: "", // 将referer置空
        // referrerPolicy: 'no-referrer', 
    }).then(response => response.blob());
}

fetchImage(imgSrc).then(blob => {
    handleBlob(blob);
});

可以看到这里的请求明显没了referer字段

在这里插入图片描述

4.6 服务器做图片中转

更加"刑"的方法就是直接搭建一个中转服务器,代理盗链者对目标资源进行正常请求,并将获取到的资源进行转发。

5.应对策略

当然,在上面罗列了那么多的绕过防盗链手法中,大部分的方法就是人为或者自动的取消掉自己的referer头部伪装成普通用户的正常访问去获取资源。要解决这个问题可以从以下几个方面去开展:

1.动态文件名,定期更换文件名称或者路径

2.判定引用地址,一般是判断浏览器请求时HTTP头的Referer字段的值

3.使用登录验证,cookie

4.图片加水印

5.可以购买一些安全服务对服务器的请求进行过滤

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

围绕http请求头中Referer展开的一些知识 的相关文章

随机推荐

  • EFK6.3+kafka+logstash日志分析平台集群

    转载来源 EFK6 3 kafka logstash日志分析平台集群 https www jianshu com p f956ebbb2499 架构解读 第一层 数据采集层 安装filebeat做日志采集 同时把采集的日志发送给kafka
  • JS中的块级作用域

    JS中是没有块级作用域作用域这个概念 为实现块级作用域可通过闭包的形式实现 下面通过一个例子来说一下JS中的块级作用域 function outputName count for var i 0 i
  • 大数据电商数据仓库系统搭建(CDH数仓) 附离线安装包

    一 Cloudera Manager 概述 1 CM 简介 Cloudera Manager是一个拥有集群自动化安装 中心化管理 集群监控 报警功能的一个工具 使得安装集群从几天的时间缩短在几个小时内 运维人员从数十人降低到几人以内 极大的
  • Qt在Windows上简单调用opencv摄像头Demo

    OpenCV也和Qt一样 有很多版本 有各种平台版本 Android ios Windows Linux等 同样在Windows上 也分vc10 vc11 vc12版 做一个简单的demo 用Qt调用opencv摄像头接口显示当前摄像头画面
  • STM32 Keil中关于stlink的调试 下载设置

    1 首先找到魔法棒 或者右键项目 选择第一个Options of 2 找到Debug 选择stlink下载 点击setting 3 选择模式为SW模式 点击确定 进入下一步 4 点击Utilities选项卡 先取消use debug dri
  • 线性同余法生成随机数Matlab_生成安全的随机数

    0x01 生产随机数的方式 Math random 0到1之间随机数 java util Random伪随机数 线性同余法生成 java security SecureRandom真随机数 java util concurrent Thre
  • 【源码&库】在调用 createApp 时,Vue 为我们做了那些工作?

    在使用Vue3时 我们需要使用createApp来创建一个应用实例 然后使用mount方法将应用挂载到某个DOM节点上 那么在调用createApp时 Vue再背后做了些什么事情呢 今天就来扒一扒Vue3的源码 看看调用createApp发
  • android知识点总结

    1 抗锯齿 会占用系统资源 对于线条 mPaint setAntiAlias true 对于图片 canvas setDrawFilter new PaintFlagsDrawFilter 0 Paint FILTER BITMAP FLA
  • phpstudy CRMEB安装配置mysql sql-mode报错及配置redis

    安装CRMEB程序时 数据库报mysql数据库报 请在mysql配置文件修sql mode为NO AUTO CREATE USER NO ENGINE SUBSTITUTION 找了好几个答案都redi是错误的 试错半小时总结最靠谱答案如下
  • AI算法工程师

    文章目录 一 我们身处人工智能的时代 人工智能的时代 人工智能的应用 二 人工智能的流程和基本概念 人工智能常见流程 人工智能基本概念与区别 三 人工智能的常见任务和本质 有监督机器学习任务与本质 无监督机器学习任务与本质 一 我们身处人工
  • docker 卸载镜像_为什么删除docker镜像后依然占用本地空间?

    bin bash Copyright 2017 Th o Chamley Permission is hereby granted free of charge to any person obtaining a copy of this
  • linux高级程序设计

    这本阿里P8撰写的算法笔记 再次推荐给大家 身边不少朋友学完这本书最后加入大厂 Github 疯传 史上最强悍 阿里大佬 LeetCode刷题手册 开放下载了 一网络通信基础 TCP IP协议簇基础 之所以称TCP IP是一个协议簇 是因为
  • java.util.concurrent.locks.ReentrantReadWriteLock 读写锁

    读写锁简介 对共享资源有读和写的操作 且写操作没有读操作那么频繁 在没有写操作的时候 多个线程同时读一个资源没有任何问题 所以应该允许多个线程同时读取共享资源 但是如果一个线程想去写这些共享资源 就不应该允许其他线程对该资源进行读和写的操作
  • MICROSOFT OFFICE MSDT操作系统命令注入漏洞(CVE-2022-30190)

    目录 漏洞概述 受到影响的产品和版本 漏洞复现 1 搭建靶场 2 攻击复现 一 执行系统程序 二 执行系统命令 修复 漏洞概述 Microsoft Windows Support Diagnostic Tool是美国微软 Microsoft
  • 操作系统——中断/异常的处理过程

    第1关 除零异常分析 分析版本 1 1 内核 回答下列问题 1 在函数 main 的语句jiffies jiffies 0 所对应的汇编指令片段中 有一个 idiv 指令 此指令的地址是多少 2 在该 idiv 指令执行之前 当前指令位置
  • 如何在CentOS上配置虚拟IP地址 (VIP)

    目录 介绍 教程 第1步 第2步 第3步 第4步 第5步 本教程将指导您如何在服务器上配置虚拟IP地址 介绍 顾名思义 虚拟IP地址 VIP 是一个没有实际物理网络接口的IP地址 传入的数据包被发送到VIP 但所有数据包都通过真实的网络接口
  • 监控程序可以监控网站的端口流量情况

    https github com louislam uptime kuma
  • PHP BLOB处理 获取BLOB数据

  • 量化投资学习-13:一张图残酷的展现了庄家、量化交易者、散户的盈利空间的对比

    庄家 量化交易者 散户的盈利空间对比 买入 庄家 最先 在股票形态没有发生变化前 提前潜入 收集筹码 量化交易者 起涨开始时 股票的上涨形态形成 满足量化指标买入 散户 大幅上涨显著后 后知后觉 情绪化追涨买入 卖出 庄家 最先 在股票形态
  • 围绕http请求头中Referer展开的一些知识

    1 什么是referer lt 点击以获取跳转信息 gt 跳转过去记得按一下f12点击网络请求详情 再刷新一下 就可以看见referer字段 当我们尝试在浏览器内部直接输入这熟悉的网址时 此时刷新后则是这样一番景象 于是你就明白了refer