PHP原生实现远程图片下载与防盗链绕过

Hazel
Hazel
发布时间:2026-10-14| 5人浏览过
原创

在PHP开发中,下载远程资源通常首选 cURL 或 file_get_contents,但在需要伪装 HTTP 头部(如绕过防盗链)且扩展受限的情况下,可直接使用 Socket 接口进行原始通信。

核心原理与方法说明

第1步:理解参数与功能模块

该方案由三个核心函数组成,分别负责获取 Cookie、解析 URL 以及执行带防盗链功能的下载操作:

  • DownImageKeep:主流程函数。负责建立连接、发送带伪造 Referer 的 HTTP GET 请求、处理重定向(自动最多3次),并将二进制流写入本地文件。
  • RefurlCookie:辅助函数。向指定源页面发起轻量级 HTTP 请求,提取响应头中的 Set-Cookie 字段,以便在主请求中携带会话状态。
  • GetHostInfo:工具函数。剥离 URL 中的协议前缀,将域名(Host)和路径(Query)分离,便于构造 HTTP 报文。

第2步:调用方式与关键参数

主要函数 DownImageKeep 的签名如下:

function DownImageKeep($gurl, $rfurl, $filename, $gcookie="", $JumpCount=0, $maxtime=30)

参数说明:

  1. $gurl:目标远程图片的完整 URL。
  2. $rfurl:伪造的来路地址(Referer)。若目标站点启用防盗链,传入此值可模拟从首页或其他允许referer的页面访问。
  3. $filename:本地保存的文件名(建议使用相对路径,避免权限问题)。
  4. $gcookie:可选的 Cookie 字符串。若不传,函数会自动尝试从 $rfurl 获取 Cookie。
  5. $JumpCount:内部递归参数,用于限制重定向次数,防止死循环。
  6. $maxtime:单次下载的超时秒数,默认30秒。

第3步:代码实现细节

以下是经过整理的完整 PHP 代码。代码使用了原生的 fsockopen 和 fwrite/fread 系列函数,完全手工拼接 HTTP/1.1 协议头。

<?php
/**
 * 下载远程图片,支持伪造 Referer 和 Cookie 以绕过防盗链
 * @param string $gurl 图片地址
 * @param string $rfurl 来路地址 (Referer)
 * @param string $filename 保存文件名
 * @param string $gcookie 伪造的Cookie
 * @param int $JumpCount 跳转计数
 * @param int $maxtime 最大等待时间(秒)
 * @return bool
 */
function DownImageKeep($gurl, $rfurl, $filename, $gcookie="", $JumpCount=0, $maxtime=30)
{
    $urlinfos = GetHostInfo($gurl);
    $ghost = trim($urlinfos['host']);
    if($ghost=='') return FALSE;

    $gquery = $urlinfos['query'];
    
    // 如果没有提供Cookie,尝试从ReferUrl获取
    if($gcookie=="" && !empty($rfurl)) {
        $gcookie = RefurlCookie($rfurl);
    }

    // 构造标准 HTTP/1.1 请求头
    $sessionQuery = "GET $gquery HTTP/1.1\r\n";
    $sessionQuery .= "Host: $ghost\r\n";
    $sessionQuery .= "Referer: $rfurl\r\n";
    $sessionQuery .= "Accept: */*\r\n";
    $sessionQuery .= "User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\r\n";
    
    if($gcookie!="" && !preg_match("/[\r\n]/", $gcookie)) {
        $sessionQuery .= "Cookie: " . trim($gcookie) . "\r\n";
    }
    
    $sessionQuery .= "Connection: Keep-Alive\r\n\r\n";

    // 建立TCP连接
    $errno = "";
    $errstr = "";
    $m_fp = fsockopen($ghost, 80, $errno, $errstr, 10);
    if (!$m_fp) {
        return FALSE;
    }
    fwrite($m_fp, $sessionQuery);

    // 读取响应头
    $m_httphead = Array();
    $httpstas = explode(" ", fgets($m_fp, 256));
    $m_httphead["http-edition"] = trim($httpstas[0]);
    $m_httphead["http-state"] = trim($httpstas[1]);

    $lnum = 0;
    while(!feof($m_fp)) {
        $line = trim(fgets($m_fp, 256));
        if ($line == "" || $lnum > 100) break;
        
        // 简单的手动解析Header键值对
        $hkey = "";
        $hvalue = "";
        $v = 0;
        for($i=0; $i<strlen($line); $i++) {
            if($v==1) { $hvalue .= $line[$i]; }
            if($line[$i]==":") { $v = 1; }
            if($v==0) { $hkey .= $line[$i]; }
        }
        $hkey = trim($hkey);
        if($hkey!="") {
            $m_httphead[strtolower($hkey)] = trim($hvalue);
        }
        $lnum++;
    }

    // 处理3xx重定向
    if(preg_match("/^3/", $m_httphead["http-state"])) {
        if(isset($m_httphead["location"]) && $JumpCount < 3) {
            $JumpCount++;
            return DownImageKeep($gurl, $rfurl, $filename, $gcookie, $JumpCount);
        } else {
            fclose($m_fp);
            return FALSE;
        }
    }

    // 非200 OK则失败
    if(!preg_match("/^2/", $m_httphead["http-state"])) {
        fclose($m_fp);
        return FALSE;
    }

    // 获取内容长度并保存文件
    $contentLength = isset($m_httphead['content-length']) ? $m_httphead['content-length'] : 0;
    $fp = fopen($filename, "w") or die("写入文件:{$filename} 失败!");
    
    $i = 0;
    $okdata = "";
    $starttime = time();
    
    // 按字节读取Body数据
    while(!feof($m_fp)) {
        $okdata .= fgetc($m_fp);
        $i++;
        
        // 超时检查
        if(time()-$starttime > $maxtime) break;
        // 达到预期大小检查
        if($contentLength > 0 && $i >= $contentLength) break;
    }

    if($okdata != "") {
        fwrite($fp, $okdata);
    }
    fclose($fp);

    // 如果没下载到数据,删除空文件
    if($okdata=="") {
        @unlink($filename);
        fclose($m_fp);
        return FALSE;
    }
    fclose($m_fp);
    return TRUE;
}

/**
 * 获取ReferUrl返回的Cookie
 */
function RefurlCookie($gurl)
{
    global $gcookie, $lastRfurl;
    $gurl = trim($gurl);
    if(!empty($gcookie) && $lastRfurl==$gurl) return $gcookie;
    if(trim($gurl)=='') return '';

    $urlinfos = GetHostInfo($gurl);
    $ghost = $urlinfos['host'];
    $gquery = $urlinfos['query'];
    
    $sessionQuery = "GET $gquery HTTP/1.1\r\n";
    $sessionQuery .= "Host: $ghost\r\n";
    $sessionQuery .= "Accept: */*\r\n";
    $sessionQuery .= "User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\r\n";
    $sessionQuery .= "Connection: Close\r\n\r\n";

    $errno = "";
    $errstr = "";
    // 注意:此处原代码有die(),实际使用建议改为错误日志记录
    $m_fp = fsockopen($ghost, 80, $errno, $errstr, 10);
    if(!$m_fp) return '';
    
    fwrite($m_fp, $sessionQuery);
    $gcookie = "";
    
    while(!feof($m_fp)) {
        $line = trim(fgets($m_fp, 256));
        if ($line == "") break;
        if(preg_match("/^Set-Cookie:/i", $line)) {
            // 清理多余的换行符等
            $gcookie = trim(substr($line, 12)); 
            break;
        }
    }
    fclose($m_fp);
    return $gcookie;
}

/**
 * 辅助:分离URL的Host和Path
 */
function GetHostInfo($gurl)
{
    $gurl = preg_replace("/^http:\/\//i", "", trim($gurl));
    $garr['host'] = preg_replace("/\/.*$/i", "", $gurl);
    $path_part = preg_replace("/^([^\/]*)(\/.*?)$/is", "$2", $gurl);
    $garr['query'] = "/" . (empty($path_part) ? "" : $path_part);
    return $garr;
}

// 调用示例
// DownImageKeep("http://example.com/image.jpg", "http://example.com", "local_image.jpg", "", 0, 30);
?>

注意事项与优化建议

虽然上述代码能解决特定的防盗链需求,但请注意以下几点:

  1. 性能损耗:逐字节读取(fgetc)在处理大图片时效率极低。若需下载较大大文件,建议改用 fread($m_fp, 4096) 块读取,并在判断 $i >= $contentLength 时累加读取长度而非单次字节计数。
  2. HTTPS支持:当前代码仅硬编码了端口 80,无法支持 HTTPS。如需支持 SSL,应使用 ssl:// 前缀配合端口 443,并使用 OpenSSL 扩展封装套接字,或使用更高级的 cURL 库。
  3. 安全性风险:直接执行用户可控的 URL 下载可能带来 SSRF(服务器端请求伪造)风险或本地文件覆盖漏洞。在生产环境中,务必对 $filename 做严格的白名单或路径校验,防止写入到敏感目录。

以上就是基于PHP原生Socket接口实现远程图片下载与防盗链绕过的详细内容,更多关于PHP网络编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。