PHP使用$_SERVER['HTTP_REFERER']实现防盗链功能
虽然对于网络爬虫之类等机器来说任何防盗链都是不堪一击的,但是如果什么都不做肯定会死的很惨。前段时间写了一个二维码的API,主要用于手机游戏二维码展示的时候调用。
该API主要使用了phpqrcode.php这个库,然后从url中获取参数文本生成相应的二维码,最后嵌入到img标签中即可。
如果不考虑任何影响因素,直接使用以下代码就能搞定:
$text = isset($_GET['text']) ? $_GET['text'] : $default_url; if(strlen($text) <= $max_length) { QRcode::png($text, false, $errorCorrectionLevel, $matrixPointSize); }
可是,网速资源是非常珍贵的,万一被非法盗链该怎么办?当年给cdn配referer规则的时候,记得有以下几种情况:
如果*.zhengshuiguang.com在列表中, 则允许*.zhengshuiguang.com的所有主机, 尤其是针对*.zhengshuiguang.xx.com的引用禁止;
如果只有blog.zhengshuiguang.com在列表中时, 将会阻止除了blog.zhengshuiguang.com之外所有的主机访问, 甚至包括zhengshuiguang.com;
如果通过浏览器直接访问是不会发送referer的,可以判断一下$_SERVER['HTTP_REFERER']是否为空。
于是,封装成以下函数:
<?php // 引入库文件 include __DIR__ . '/phpqrcode.php'; // 请设置白名单主机地址, 可以使用通配符进行匹配 $allow_hosts = array( '*.zhengshuiguang.com', // 通配zhengshuiguang.com下所有域名 '127.0.0.1', // 本地调试时开启 '', // 当referer为空允许引用 ); // 注意: 如果直接通过浏览器访问test.php?text=xxxx将不会发送referer, 也就是说$_SERVER['HTTP_REFERER']为空, 是否提供二维码显示取决于个人看法, 这里允许为空的referer引用 $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; // 默认的二维码展示 $default_url = 'http://blog.zhengshuiguang.com'; // 设置QR二维码的纠错级别 $errorCorrectionLevel = 'L'; // 生成的图片大小 $matrixPointSize = 9; // 检测referer是符合白名单规则, 如果符合则返回true, 否则返回false if(!filter_referer($allow_hosts, $referer)) { QRcode::png($default_url, false, $errorCorrectionLevel, $matrixPointSize); }else{ // 长度限制, 一般网址都不会超过这个限制, 如果超过了生成的二维码识别度肯定会大大降低 $max_length = 1000; // 得到get参数, 由于是本站的下载地址无需过滤 $text = isset($_GET['text']) ? $_GET['text'] : $default_url; if(strlen($text) <= $max_length) { QRcode::png($text, false, $errorCorrectionLevel, $matrixPointSize); } } /** * 检测$referer是否符合$allow_hosts中的主机地址规则 * @param $allow_hosts 白名单主机地址 * @param $referer 引用地址 * @return boolean */ function filter_referer($allow_hosts = array(), $referer = '') { if(empty($allow_hosts)) { return true; } // 非空检测 if(!empty($referer)) { // 获取referer地址并解析 $info = parse_url($referer); // 提取出host $local = isset($info['host']) ? $info['host'] : ''; // 遍历白名单主机地址 foreach($allow_hosts as $host) { if(strpos($host, '*.') === 0) { // 去掉*.得到主域名进行二级匹配 $host = substr($host, 2); // strpos返回值有int或false, 长度差只能为int, 必须使用===进行判断 if(strpos($local, $host) === strlen($local) - strlen($host)) { return true; } }else{ // 完全匹配 if($host === $local) { return true; } } } return false; }else{ return in_array('', $allow_hosts, true); } }
在本地127.0.0.1进行调试的时候可以开启相应的配置,上线后注释掉即可。
然后在html页面直接通过img标签进行引用:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content='text/html;charset=utf-8'/> </head> <body> <img src="test.php?text=http://www.baidu.com"/> </body> </html>
虽然nginx或apache都具备防盗链功能,原理也大致相同,但是使用php针对某一项目实现防盗链还是很有意义的,至少不会被人光明正大地在html中引用。整个项目已经打包,从这里下载:demo.zip
本文地址:http://blog.zhengshuiguang.com/php/filter_referer.html
转载随意,但请附上文章地址:-)
评论已关闭