首页 > PHP > PHP使用$_SERVER['HTTP_REFERER']实现防盗链功能

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

转载随意,但请附上文章地址:-)

标签:防盗链 referer

相关文章

评论已关闭