首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 防止盜鏈實作:Cookie 驗證與 Token 簽名方案

     編輯:PHP 綜合知識

文中方案分析:Cookie Referer 檢查

<?php
// dl.php
$id = $_GET['id'] ?? '';
$act = $_GET['act'] ?? 'display';

switch ($act) {
    case 'display':
        setcookie('visited', 'true', time() + 3600, '/');
        // 輸出下載頁面 HTML
        break;
    case 'down':
        if (!isset($_COOKIE['visited'])) die('盜鏈?');
        // 執行下載邏輯
        break;
}

缺陷:

  • Cookie 易偽造、刪除、跨域不送出
  • 無法防止熱連結(直接在論壇貼 <img src="dl.php?act=down&id=1">,瀏覽器會攜帶 Cookie)
  • 無法支援 API/非瀏覽器客戶端

進階方案一:Referer 檢查(簡單但不可靠)

function checkReferer(array $allowedDomains): bool {
    $referer = $_SERVER['HTTP_REFERER'] ?? '';
    if (!$referer) return false; // 無 Referer 視策略決定
    $host = parse_url($referer, PHP_URL_HOST);
    return in_array($host, $allowedDomains, true);
}
// 使用
if (!checkReferer(['example.com', 'www.example.com'])) {
    http_response_code(403);
    exit('Forbidden: Hotlinking not allowed');
}

局限:Referer 可偽造、隱私模式/防火牆可移除、HTTPS→HTTP 不送 Referer。

進階方案二:簽名 URL(Signed URL)—— 推薦

原理:下載連結包含時效簽名,伺服器驗證簽名與過期時間,無需 Session/Cookie。

<?php
// 生成下載連結(頁面渲染時)
function generateDownloadUrl(string $fileId, int $ttl = 300): string {
    $secret = 'your-256-bit-secret-key'; // 從配置讀取
    $expire = time() + $ttl;
    $payload = $fileId . '|' . $expire;
    $signature = hash_hmac('sha256', $payload, $secret);
    return "download.php?file=$fileId&expire=$expire&sig=$signature";
}

// 驗證下載請求
function verifyDownloadUrl(): ?string {
    $fileId  = $_GET['file'] ?? '';
    $expire  = (int)($_GET['expire'] ?? 0);
    $sig     = $_GET['sig'] ?? '';
    if (!$fileId || !$expire || !$sig) return null;

    if (time() > $expire) return null; // 過期

    $secret = 'your-256-bit-secret-key';
    $payload = $fileId . '|' . $expire;
    $expected = hash_hmac('sha256', $payload, $secret);
    if (!hash_equals($expected, $sig)) return null; // 簽名不符

    return $fileId; // 合法
}

// download.php
$fileId = verifyDownloadUrl();
if (!$fileId) { http_response_code(403); exit('連結無效或已過期'); }

// 取得檔案路徑(資料庫查詢,防 ID 遍歷)
$stmt = $pdo->prepare('SELECT path, mime, size FROM files WHERE id = ? AND deleted_at IS NULL');
$stmt->execute([$fileId]);
$file = $stmt->fetch();
if (!$file) { http_response_code(404); exit('檔案不存在'); }

// 輸出檔案(X-Sendfile/X-Accel-Redirect 最佳)
header('X-Accel-Redirect: /protected-files/' . basename($file['path']));
header('Content-Type: ' . $file['mime']);
header('Content-Length: ' . $file['size']);
header('Content-Disposition: attachment; filename="' . basename($file['path']) . '"');
exit;

進階方案三:Nginx secure_link 模組(最高效能)

完全在 Web Server 層驗證,零 PHP 開銷:

# nginx.conf
location /download/ {
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "your-secret$uri$arg_expires";

    if ($secure_link = "") { return 403; }
    if ($secure_link = "0") { return 410; } # 過期

    # 內部重寫至實際檔案路徑
    internal;
    alias /var/www/files/;
}

# PHP 生成連結
function generateNginxSecureLink(string $filePath, int $ttl = 300): string {
    $secret = 'your-secret';
    $expire = time() + $ttl;
    $md5 = base64_encode(hash_hmac('sha1', "$filePath$expire", $secret, true));
    $md5 = strtr($md5, '+/', '-_'); // URL-safe
    return "/download/$filePath?md5=$md5&expires=$expire";
}

綜合防盜鏈策略表

層級方案適用場景優點缺點
應用層簽名 URL (HMAC)API、私有下載、跨域分享無狀態、可攜帶、精確控制 TTL、支援撤銷需管理密鑰、連結較長
應用層Token/Session 綁定會員專屬內容、需登入下載可結合權限系統、支援動態決策需 Session、難防熱連結、CSRF 風險
Web ServerNginx secure_link大流量靜態檔案、影音串流極高效能、零 PHP、原生支援需 Nginx、撤銷需配合 Lua/Redis
Web ServerReferer 檢查圖片防盜鏈、簡單場景配置簡單、無代碼變更易繞過、誤傷隱私用戶
CDN/邊緣簽名 URL / Token 驗證全球分發、降低源站壓力最接近用戶、DDoS 緩解廠商綁定、配置複雜
儲存層S3/OSS 預簽名 URL物件儲存直傳/直讀無伺服器、自動 HTTPS、細粒度權限依賴雲廠商、URL 長

最佳實踐建議

  1. 預設拒絕:所有私有資源需驗證才能存取
  2. 最小權限:下載連結僅包含必要資訊(檔案 ID、過期時間)
  3. 審計日誌:記錄下載 IP、User-Agent、檔案 ID、用戶 ID
  4. 速率限制:IP/用戶維度限制下載頻率(Redis 滑動窗口)
  5. HTTPS 強制:防止中間人竊取簽名連結
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved