• HTTP 笔记与总结(6)referer 头与防盗链


    在百度贴吧(或 QQ 空间等)中找到一张图片,复制图片地址,在站外通过 img src 引用,会发现:

    此外,在一些统计软件中,统计访客的来路(直接访问、外部链接、搜索引擎),都用到了 HTTP 协议请求头中 Referer 的知识。

    【例】直接访问 www.baidu.com 和 在通过本地页面跳转至 www.baidu.com,观察 HTTP 请求头信息的差异:

    ① 直接访问百度,HTTP 请求头信息:

    ② 通过本地 referer.html 跳转至 www.baidu.com:

    HTTP 协议头信息的 Referer 选项代表网页的来源(上一页的地址),如果是直接访问(直接在浏览器输入地址访问),则没有 Referer 选项。

    配置 Apache 服务器用于图片防盗链

    原理:在 web 服务器层面,根据 http 协议的 referer 头信息来判断网页来源,如果来自站外,则统一重写到一个很小的防盗链题型图片上(URL 重写)。

    步骤:

    ① 打开 apache 重写模块 rewrite_mod。重启 apache。

    ② 在需要防盗链的网站或目录下,写 .htaccess 文件,并指定防盗链规则 —— 分析 referer,如果不是来自本站,则重写

    例如在 127.0.0.17/php/http/ 目录下新建 .htaccess 文件,写入重写规则:

    当是 jpg/jpeg/gif/png 图片 ,且 referer 头与 127.0.0.17 不匹配时重写,统一 Rewrite 到某个防盗链图片

    重写方式参见:apache 手册

    D:practisephphttp.htaccess:

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} .*.(jpg|jpeg|gif|png) [NC]
    RewriteCond %{HTTP_REFERER} !127.0.0.17 [NC]
    RewriteRule .* http://127.0.0.17/php/logo.jpg

    第 1 行:开启重写功能;

    第 2 行:当请求的文件名以 .jpg,.jpeg,.gif,.png 结尾时

    第 3 行:当 HTTP 的 Referer 头 与 服务器地址 127.0.0.17 不匹配时

    第 4 行:重写规则,把符合条件的文件重写到 http://127.0.0.17/php/http/logo.jpg 上

    实例:

    D:practisephplogo.jpg:

    D:practisephphttpa.jpg:

    站内地址(127.0.0.17,D:practise),站外地址(127.0.0.16,D:practisephp)。也就是说,127.0.0.16 上的文件外链 127.0.0.17 上的 a.jpg 时,会被重写至 logo.jpg。

    http://127.0.0.17/php/http/referer.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Document</title>
    </head>
    <body>
    	<img src="./a.jpg" alt="">
    </body>
    </html>
    

    输出:

      

    http://127.0.0.16/http/referer.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Document</title>
    </head>
    <body>
    	<img src="./http/a.jpg" alt="">
    </body>
    </html>
    

    输出:

    防盗链功能实现。

    如果把 .htaccess 放在根目录,则网站所有的图片都会受到影响;如果只要对某个目录生效,则在 .htaccess 中加上:

    Rewrite Base /php/http

    采集图片功能(通过设置 Referer 头信息,绕过防盗链)

    先测试通过 HTTP GET 请求图片

    caiji.php:

    <?php
    require './http.class.php';
    
    $http = new Http('http://127.0.0.17/php/http/a.jpg');
    
    echo $res = $http->get();
    

    输出:

      

    修改 http.class.php line:72(增加判断 $url['query'],防止出现 Notice 级别的错误):

      1 <?php
      2 /*
      3     PHP + socket 编程
      4     @发送 HTTP 请求
      5     @模拟下载
      6     @实现注册、登录、批量发帖
      7 */
      8 
      9 //http 请求类的接口
     10 interface Proto{
     11     //连接 url 
     12     function conn($url);
     13 
     14     //发送 GET 请求
     15     function get();
     16 
     17     //发送 POST 请求
     18     function post();
     19 
     20     //关闭连接
     21     function close();
     22 }
     23 
     24 class Http implements Proto{
     25 
     26     //换行符
     27     const CRLF = "
    ";
     28 
     29     //fsocket 的错误号与错误描述
     30     protected $errno = -1;
     31     protected $errstr = '';
     32 
     33     //响应内容
     34     protected $response = '';
     35 
     36     protected $url = null;
     37     protected $version = 'HTTP/1.1';
     38     protected $fh = null;
     39 
     40     protected $line = array();
     41     protected $header = array();
     42     protected $body = array();
     43 
     44     public function __construct($url){
     45         $this->conn($url);
     46         $this->setHeader('Host:' . $this->url['host']);
     47     }
     48 
     49     //写请求行
     50     protected function setLine($method){
     51         $this->line[0] = $method . ' ' . $this->url['path'] . '?' . $this->url['query'] . '  ' . $this->version;
     52     }
     53 
     54     //写头信息
     55     public function setHeader($headerline){
     56         $this->header[] = $headerline;
     57     } 
     58 
     59     //写主体信息
     60     protected function setBody($body){
     61         //构造 body 的字符串
     62         $this->body[] = http_build_query($body);
     63     }
     64 
     65     //连接 url 
     66     public function conn($url){
     67         $this->url = parse_url($url);
     68         //判断端口
     69         if(!isset($this->url['port'])){
     70             $this->url['port'] = 80;
     71         }
     72         //判断 query
     73         if(!isset($this->url['query'])){
     74             $this->url['query'] = '';
     75         }        
     76         $this->fh = fsockopen($this->url['host'], $this->url['port'], $this->errno, $this->errstr, 3);
     77     }
     78 
     79     //构造 GET 请求的数据
     80     public function get(){
     81         $this->setLine('GET');
     82         //发送请求
     83         $this->request();
     84         return $this->response;
     85     }
     86 
     87     //构造 POST 请求的数据
     88     public function post($body = array()){
     89         //构造请求行
     90         $this->setLine('POST');
     91 
     92         //设置 Content-type 和 Content-length
     93         $this->setHeader('Content-type: application/x-www-form-urlencoded');
     94         
     95         //构造主体信息, 和 GET 请求不一样的地方
     96         $this->setBody($body);
     97         
     98         $this->setHeader('Content-length: ' . strlen($this->body[0]));
     99 
    100         //发送请求
    101         $this->request();
    102         return $this->response;        
    103     }
    104 
    105     //发送请求
    106     public function request(){
    107         //把请求行、头信息、主体信息拼接起来
    108         $req = array_merge($this->line, $this->header, array(''), $this->body, array(''));
    109         $req = implode(self::CRLF, $req);
    110         //echo $req;exit;
    111 
    112         fwrite($this->fh, $req);
    113 
    114         while(!feof($this->fh)){
    115             $this->response .= fread($this->fh, 1024);
    116         }
    117 
    118         //关闭连接
    119         $this->close();
    120     }
    121 
    122     //关闭连接
    123     public function close(){
    124         fclose($this->fh);
    125     }
    126 }
    View Code

    测试防盗链:

    caiji.php

     1 <?php
     2 require './http.class.php';
     3 
     4 $http = new Http('http://127.0.0.17/php/http/a.jpg');
     5 
     6 $res = $http->get();
     7 
     8 //取出 图片二进制码(和 HTTP 头信息中间有一个空行 
    ,加上空行之前的换行
    ,一共4个字节)
     9 $res = file_put_contents('./b.jpg', substr(strstr($res, "
    
    "), 4));
    10 echo 'complete';

    运行之后,因为没有 Referer 头信息,因此被认为是盗链,生成的 b.jpg 变成了 “不要盗链”:

    在 caiji.php 中加入 Referer 头信息后:

    caiji.php

     1 <?php
     2 require './http.class.php';
     3 
     4 $http = new Http('http://127.0.0.17/php/http/a.jpg');
     5 $http->setHeader('Referer:127.0.0.17');
     6 $res = $http->get();
     7 
     8 //取出 图片二进制码(和 HTTP 头信息中间有一个空行 
    ,加上空行之前的换行
    ,一共4个字节)
     9 $res = file_put_contents('./b.jpg', substr(strstr($res, "
    
    "), 4));
    10 echo 'complete';

    此时采集到的 b.jpg 绕过了防盗链正常的显示了:

     

    待完善:应该判断 response 的 MIME 头信息,确定图片的类型,再把文件写入相应类型的文件中。  

  • 相关阅读:
    设计模式-观察者模式
    获取ubuntu中软件包的有用地址
    vim 简单命令
    adb logcat 日志过滤
    shell编程——
    shell编程——参数传递
    Chromecast
    linux 广播
    【转】Git命令解说
    linux 多播
  • 原文地址:https://www.cnblogs.com/dee0912/p/4646739.html
Copyright © 2020-2023  润新知