「単純なプロキシ実装例」の版間の差分
(→ソースコード) |
|||
| (同じ利用者による、間の2版が非表示) | |||
| 3行目: | 3行目: | ||
== svgMap.jsの初期化 == | == svgMap.jsの初期化 == | ||
| − | |||
<pre> | <pre> | ||
<!doctype html> | <!doctype html> | ||
| 24行目: | 23行目: | ||
</html> | </html> | ||
</pre> | </pre> | ||
| − | |||
* corsProxy.jsは[[クロスオリジンアクセス#corsProxy.js]]を参照 | * corsProxy.jsは[[クロスオリジンアクセス#corsProxy.js]]を参照 | ||
| 33行目: | 31行目: | ||
* simpleCORSproxy.php | * simpleCORSproxy.php | ||
** >PHP7.4 | ** >PHP7.4 | ||
| + | ** GETリクエストのみ対応 | ||
* proxyへのアクセスに際して、refererリクエストヘッダが必要なコードになっています。 | * proxyへのアクセスに際して、refererリクエストヘッダが必要なコードになっています。 | ||
<!-- * refererヘッダを用いてアクセス制限をかける場合、プロキシはwebAppと別ドメインになっている必要があります。(同一ドメインではrefererヘッダが付与されない) | <!-- * refererヘッダを用いてアクセス制限をかける場合、プロキシはwebAppと別ドメインになっている必要があります。(同一ドメインではrefererヘッダが付与されない) | ||
** 参考までに、同じホストでもwww.[ドメイン名]と[ドメイン名]が別ドメインとして認識されることを利用することもできます。 | ** 参考までに、同じホストでもwww.[ドメイン名]と[ドメイン名]が別ドメインとして認識されることを利用することもできます。 | ||
** refererヘッダによる制限のためには、<code>if (preg_match("|^https?://svgmap\.org|", $referer) .. </code>の行を適宜書き換えてください--> | ** refererヘッダによる制限のためには、<code>if (preg_match("|^https?://svgmap\.org|", $referer) .. </code>の行を適宜書き換えてください--> | ||
| − | + | ||
<pre> | <pre> | ||
<?php | <?php | ||
| 107行目: | 106行目: | ||
} | } | ||
</pre> | </pre> | ||
| − | </ | + | |
| + | |||
| + | * simpleCORSproxyPost.php (POSTリクエストにも対応版:開発・検証中) | ||
| + | |||
| + | <pre> | ||
| + | <?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>"; | ||
| + | } | ||
| + | </pre> | ||
2025年10月31日 (金) 13:14時点における最新版
この例は、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>";
}