在百度贴吧(或 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 到某个防盗链图片
重写方式参见:
D:\practise\php\http\.htaccess:
RewriteEngine OnRewriteCond %{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:\practise\php\logo.jpg:
D:\practise\php\http\a.jpg:
站内地址(127.0.0.17,D:\practise),站外地址(127.0.0.16,D:\practise\php)。也就是说,127.0.0.16 上的文件外链 127.0.0.17 上的 a.jpg 时,会被重写至 logo.jpg。
http://127.0.0.17/php/http/referer.html:
Document
输出:
http://127.0.0.16/http/referer.html:
Document
输出:
防盗链功能实现。
如果把 .htaccess 放在根目录,则网站所有的图片都会受到影响;如果只要对某个目录生效,则在 .htaccess 中加上:
Rewrite Base /php/http
采集图片功能(通过设置 Referer 头信息,绕过防盗链)
先测试通过 HTTP GET 请求图片
caiji.php:
get();
输出:
修改 http.class.php line:72(增加判断 $url['query'],防止出现 Notice 级别的错误):
1 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 }
测试防盗链:
caiji.php
1 get(); 7 8 //取出 图片二进制码(和 HTTP 头信息中间有一个空行 \r\n,加上空行之前的换行\r\n,一共4个字节) 9 $res = file_put_contents('./b.jpg', substr(strstr($res, "\r\n\r\n"), 4));10 echo 'complete';
运行之后,因为没有 Referer 头信息,因此被认为是盗链,生成的 b.jpg 变成了 “不要盗链”:
在 caiji.php 中加入 Referer 头信息后:
caiji.php
1 setHeader('Referer:127.0.0.17'); 6 $res = $http->get(); 7 8 //取出 图片二进制码(和 HTTP 头信息中间有一个空行 \r\n,加上空行之前的换行\r\n,一共4个字节) 9 $res = file_put_contents('./b.jpg', substr(strstr($res, "\r\n\r\n"), 4));10 echo 'complete';
此时采集到的 b.jpg 绕过了防盗链正常的显示了:
待完善:应该判断 response 的 MIME 头信息,确定图片的类型,再把文件写入相应类型的文件中。