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

PHP 指定網址跳轉實作:查詢字串導向對應目標完整範例

     編輯:PHP 相關知識

需求場景

根據 URL 查詢字串 (`?url=target`) 動態導向不同目標網址,常用於短網址、廣告追蹤、外部連結中介頁。

基本實作 (redirect.php)

<?php
// 允許的目標網域白名單 (安全性關鍵)
$allowedDomains = [
    'example.com',
    'partner.com',
    'api.service.com'
];

// 取得查詢字串
$query = $_SERVER['QUERY_STRING'] ?? '';

// 解析目標 URL 參數
parse_str($query, $params);
$targetUrl = $params['url'] ?? '';

// 驗證 URL 格式
if (!filter_var($targetUrl, FILTER_VALIDATE_URL)) {
    http_response_code(400);
    die('無效的 URL 參數');
}

// 解析目標主機
$targetHost = parse_url($targetUrl, PHP_URL_HOST) ?? '';

// 白名單檢查 (防開放式重導向攻擊)
if (!in_array($targetHost, $allowedDomains, true)) {
    http_response_code(403);
    die('不允許的目標網域: ' . htmlspecialchars($targetHost));
}

// 記錄點擊 (資料庫/日誌/分析)
// log_click($targetUrl, $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']);

// 執行重導向
header("Location: $targetUrl", true, 302); // 302 暫時重導向
exit;
?>

進階版:支援多參數、Base64 編碼、統計追蹤

<?php
class Redirector {
    private $allowedDomains;
    private $db;
    
    public function __construct(array $allowedDomains, PDO $db = null) {
        $this->allowedDomains = $allowedDomains;
        $this->db = $db;
    }
    
    public function handle(): void {
        // 1. 取得編碼後的目標參數
        $encoded = $_GET['u'] ?? $_GET['url'] ?? '';
        if (!$encoded) $this->fail(400, '缺少目標參數');
        
        // 2. 解碼 (支援 Base64URL)
        $targetUrl = $this->decodeUrl($encoded);
        if (!$targetUrl) $this->fail(400, '參數解碼失敗');
        
        // 3. 驗證
        if (!$this->isValidUrl($targetUrl)) $this->fail(400, '無效 URL');
        if (!$this->isAllowedDomain($targetUrl)) $this->fail(403, '禁止的網域');
        
        // 4. 記錄統計
        $this->logClick($targetUrl);
        
        // 5. 重導向
        header("Location: $targetUrl", true, 302);
        exit;
    }
    
    private function decodeUrl(string $encoded): ?string {
        // 嘗試 Base64URL 解碼
        $decoded = base64_decode(strtr($encoded, '-_', '+/'));
        if ($decoded !== false && filter_var($decoded, FILTER_VALIDATE_URL)) {
            return $decoded;
        }
        // 回退原始 URL
        return filter_var($encoded, FILTER_VALIDATE_URL) ? $encoded : null;
    }
    
    private function isValidUrl(string $url): bool {
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }
    
    private function isAllowedDomain(string $url): bool {
        $host = parse_url($url, PHP_URL_HOST) ?? '';
        // 支援子網域比對
        foreach ($this->allowedDomains as $domain) {
            if ($host === $domain || str_ends_with($host, '.' . $domain)) {
                return true;
            }
        }
        return false;
    }
    
    private function logClick(string $url): void {
        if (!$this->db) return;
        try {
            $stmt = $this->db->prepare(
                "INSERT INTO redirect_logs (target_url, ip, user_agent, referer, created_at) 
                 VALUES (?, ?, ?, ?, NOW())"
            );
            $stmt->execute([
                $url,
                $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
                $_SERVER['HTTP_REFERER'] ?? 'direct'
            ]);
        } catch (Exception $e) {
            error_log("Redirect log failed: " . $e->getMessage());
        }
    }
    
    private function fail(int $code, string $msg): void {
        http_response_code($code);
        header('Content-Type: text/plain; charset=utf-8');
        echo $msg;
        exit;
    }
}

// 使用
$allowed = ['example.com', 'shop.example.com', 'partner.org'];
$db = new PDO('mysql:host=localhost;dbname=stats', 'user', 'pass');
(new Redirector($allowed, $db))->handle();
?>

前端產生跳轉連結

<?php
function generateRedirectUrl(string $targetUrl, array $allowedDomains): string {
    $host = parse_url($targetUrl, PHP_URL_HOST);
    if (!$host || !in_array($host, $allowedDomains)) return '#';
    
    // Base64URL 編碼 (URL 安全)
    $encoded = rtrim(strtr(base64_encode($targetUrl), '+/', '-_'), '=');
    return "/redirect.php?u=$encoded";
}

// 使用
$link = generateRedirectUrl('https://shop.example.com/product/123', ['shop.example.com']);
// 輸出: /redirect.php?u=aHR0cHM6Ly9zaG9wLmV4YW1wbGUuY29tL3Byb2R1Y3QvMTIz
?>
<a href="<?= $link ?>" target="_blank" rel="noopener">前往商品頁</a>

安全性檢查清單

  • [ ] **嚴格白名單驗證**目標網域 (防開放式重導向 CWE-601)
  • [ ] 記錄完整存取日誌 (IP、UA、Referer、時間、目標 URL)
  • [ ] 限流保護 (Redis 計數器、Token Bucket)
  • [ ] HTTPS 強制 (目標與中介頁皆為 HTTPS)
  • [ ] CSP 標頭限制 `frame-ancestors 'none'` 防嵌入
  • [ ] 定期稽核日誌偵測異常流量

常見變體

  • 短網址服務:資料庫對應 `short_code` → `target_url`
  • 廣告追蹤:附加 `utm_source`、`campaign_id` 參數再導向
  • 外部連結中介頁:顯示「您即將離開本站」確認頁 (需 JS 延遲導向)
  • 登入後導向:儲存 `redirect_to` 在 Session,登入成功後導回
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved