単純なプロキシ実装例

提供: svg2wiki
2025年10月31日 (金) 13:14時点におけるAdmin (トーク | 投稿記録)による版
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
移動先: 案内検索

この例は、PHPによる かなり単純なCORSプロキシの実装例です。よりセキュリティに配慮した実装が必要なケースがありますので、クロスオリジンアクセスの他の実装を参照の上利用を検討してください。


svgMap.jsの初期化

<!doctype html>
<html>
...
<script type="module">
import { CorsProxy } from 'https://cdn.jsdelivr.net/gh/svgmap/svgmapjs@latest/CorsProxyModule.js';
import { svgMap } from 'https://cdn.jsdelivr.net/gh/svgmap/svgmapjs@latest/SVGMapLv0.1_r18module.js';
window.svgMap=svgMap
var corsProxy = new CorsProxy();

// プロキシの設定
var proxyPath = "https://..url..of../simpleCORSproxy.php?file=";

corsProxy.setService(proxyPath, null, true, true); // 第4パラメータをtrueにするとアクセス先URLをエンコードする
window.corsProxy = corsProxy;
svgMap.setProxyURLFactory(null,null,null, corsProxy.getURLfunction(), true); // ビットイメージ非線形図法変換の時のみプロキシを使う
</script>
...
</html>
  • corsProxy.jsはクロスオリジンアクセス#corsProxy.jsを参照
  • corsProxy.setServiceの、requireEncoding フラグはtrueにします。
    • 本php実装ではfileクエリに記載される取得先URLがURLエンコードされていることを想定しているため

ソースコード

  • simpleCORSproxy.php
    • >PHP7.4
    • GETリクエストのみ対応
  • proxyへのアクセスに際して、refererリクエストヘッダが必要なコードになっています。
<?php

// 許可するリファラードメインの正規表現リスト
// 必要に応じてここにドメインを編集・追加してください。
$allowedReferers = [
    '#^https?://www\.svgmap\.org/#',
    // '#^https?://example\.com/#',
];

if (isset($_GET['file'])) {
    $fileUrl = ($_GET['file']);
    $referer = $_SERVER['HTTP_REFERER'] ?? '';

    // 許可されたリファラーからのアクセスかチェック
    $isAllowed = false;
    foreach ($allowedReferers as $pattern) {
        if (preg_match($pattern, $referer)) {
            $isAllowed = true;
            break;
        }
    }

    // リファラーが無効な場合はアクセスを拒否
    if (!$isAllowed) {
        header("HTTP/1.1 403 Forbidden");
        echo "Error: Access denied. Invalid or missing Referer.";
        exit();
    }

    // cURLセッションの初期化と設定
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_URL => $fileUrl,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    ]);

    $contentData = curl_exec($ch);

    if ($contentData === false) {
        header("HTTP/1.1 500 Internal Server Error");
        echo "Error: Could not retrieve content.";
        exit();
    }

    // MIMEタイプを自動で判定して設定
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $contentType = finfo_buffer($finfo, $contentData);
    finfo_close($finfo);

    // テキスト系ファイルの場合はUTF-8を指定
    if (strpos($contentType, 'text/') === 0 || $contentType === 'application/json') {
        header("Content-Type: " . $contentType . "; charset=UTF-8");
    } else {
        header("Content-Type: " . $contentType);
    }
    
    echo $contentData;
    curl_close($ch);

} else {
    header("Content-Type: text/html; charset=UTF-8");
    echo "<h1>Content Proxy</h1><p>This proxy requires a 'file' GET parameter with a URL-encoded content URL.</p>";
}


  • simpleCORSproxyPost.php (POSTリクエストにも対応版:開発・検証中)
<?php

// 許可するリファラードメインの正規表現リスト (プロキシ自体のセキュリティ)
// ここにプロキシを使う自サイトのドメインを設定してください。
$allowedReferers = [
    '#^https?://www\.svgmap\.org/#',
    // '#^https?://example\.com/#',
];

if (isset($_GET['file'])) {
    $fileUrl = $_GET['file'];
    
    $referer = $_SERVER['HTTP_REFERER'] ?? ''; 
    $requestMethod = $_SERVER['REQUEST_METHOD']; // GET, POSTなどを取得
    $transferHeaders = [];
    $postData = '';

    // クライアントからのプロキシへのアクセス制限
    $isAllowed = false;
    foreach ($allowedReferers as $pattern) {
        if (preg_match($pattern, $referer)) {
            $isAllowed = true;
            break;
        }
    }

    if (!$isAllowed) {
        header("HTTP/1.1 403 Forbidden");
        echo "Error: Access denied. Invalid or missing Referer.";
        exit();
    }

    // POSTリクエストの場合、データとヘッダーを取得
    if ($requestMethod === 'POST') {
        // RAW POSTデータを取得 (JSON, x-www-form-urlencodedなど形式を問わない)
        $postData = file_get_contents('php://input'); 
        
        // Content-TypeとContent-Lengthヘッダーを取得
        // getallheaders() が使えない環境を考慮し、$_SERVERから取得
        $contentType = $_SERVER['CONTENT_TYPE'] ?? 'application/x-www-form-urlencoded';
        
        $transferHeaders[] = 'Content-Type: ' . $contentType;
        $transferHeaders[] = 'Content-Length: ' . strlen($postData);
    }
    
    // cURLオプションの設定
    $ch = curl_init();
    $curlOptions = [
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_URL => $fileUrl,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    ];

    if ($requestMethod === 'POST') {
        // POSTリクエストとして設定
        $curlOptions[CURLOPT_POST] = true;
        $curlOptions[CURLOPT_POSTFIELDS] = $postData;
        $curlOptions[CURLOPT_HTTPHEADER] = $transferHeaders;
    } 
    // GETリクエストの場合は、特別な設定は不要 (デフォルトでGETになる)

    curl_setopt_array($ch, $curlOptions);
    $contentData = curl_exec($ch);

    // エラー処理、MIMEタイプ判定、終了部分
    if ($contentData === false) {
        $curlError = curl_error($ch);
        $curlErrno = curl_errno($ch);
        header("HTTP/1.1 500 Internal Server Error");
        echo "Error: Could not retrieve content. cURL Error: " . $curlError . " (Code: " . $curlErrno . ")";
        exit();
    }

    // MIMEタイプを自動で判定して設定 (finfo拡張機能が必要)
    if (!function_exists('finfo_open')) {
        header("HTTP/1.1 500 Internal Server Error");
        echo "Error: Fileinfo extension is required for MIME type detection.";
        exit();
    }
    
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $contentType = finfo_buffer($finfo, $contentData);
    finfo_close($finfo);

    // テキスト系ファイルの場合はUTF-8を指定
    if (strpos($contentType, 'text/') === 0 || strpos($contentType, 'json') !== false || strpos($contentType, 'xml') !== false) {
        header("Content-Type: " . $contentType . "; charset=UTF-8");
    } else {
        header("Content-Type: " . $contentType);
    }
    
    echo $contentData;
    curl_close($ch);

} else {
    header("Content-Type: text/html; charset=UTF-8");
    echo "<h1>Content Proxy</h1><p>This proxy requires a 'file' GET parameter with a URL-encoded content URL.</p>";
}