Web - CORS
概要
CORS (Cross-Origin Resource Sharing) は、Webブラウザにおけるセキュリティ機能の1つであり、異なるオリジン (ドメイン、プロトコル、ポート) 間でのリソース共有を制御する仕組みである。
CORSが必要となった経緯において、以前のWebブラウザには"同一オリジンポリシー"という制限があり、あるWebサイトから別のドメインのリソースにアクセスすることは基本的に禁止されていた。
しかし、現代のWebアプリケーションでは、異なるドメイン間でのデータのやり取りが必要不可欠となっている。
そこで、CORSが導入されて、安全に異なるオリジン間での通信が可能となった。
CORSの基本的な動作の流れを以下に示す。
- まず、Webブラウザが異なるオリジンへのリクエストを送信する場合、プリフライトリクエスト (OPTIONSメソッド) を送信することがある。
- このリクエストでは、実際のリクエストが許可されるかどうかを確認する。
- サーバは適切なCORSヘッダを返すことにより、どのオリジンからのアクセスを許可するか、どのHTTPメソッドを許可するか、どのヘッダの使用を許可するか等を指定する。
重要なCORSヘッダとして、以下に示すものがある。
- Access-Control-Allow-Origin (許可するオリジンの指定)
- Access-Control-Allow-Methods (許可するHTTPメソッドの指定)
- Access-Control-Allow-Headers (許可するヘッダの指定)
これらのヘッダをサーバ側で適切に設定することにより、クロスオリジンリクエストを制御できる。
セキュリティでは、CORSは悪意のあるWebサイトからの不正なリクエストを防いで、認証されたリクエストのみを許可することにより、
クロスサイトスクリプティング (XSS) やクロスサイトリクエストフォージェリ (CSRF) 等の攻撃からWebアプリケーションを保護する。
サーバ側でCORSを設定する方法は複数存在しており、各言語やフレームワークで適切な方法が提供されている。
- Node.js
- corsミドルウェア
- Python
- django-cors-headers
- Flask-CORS
- PHP
- header関数の使用
また、開発時の注意点として、ワイルドカード (*) を使用して全てのオリジンを許可することは、本番環境では推奨されない。
必要最小限のオリジンのみを許可して、適切なセキュリティ設定を行うことが重要である。
PHPでのCORSの設定
ヘッダ設定による方法
// 全てのドメインからのアクセスを許可する場合
// 開発時のテスト以外では避けて、本番環境では必要最小限のドメインのみ許可することが推奨される
header("Access-Control-Allow-Origin: *");
// 特定のドメインからのアクセスのみ許可する場合
header("Access-Control-Allow-Origin: https://example.com");
// リクエストメソッドの設定
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
// 許可するヘッダの設定
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// 認証情報を含むリクエストを許可する場合
header("Access-Control-Allow-Credentials: true");
// プリフライトリクエストの結果をキャッシュする時間 (秒)
header("Access-Control-Max-Age: 86400");
動的なオリジン制御
$allowed_origins = [
'https://example.com',
'https://api.example.com'
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowed_origins)) {
header("Access-Control-Allow-Origin: " . $origin);
header("Access-Control-Allow-Credentials: true");
}
実装時の注意
セキュリティ
PHPにおいて、header("Access-Control-Allow-Origin: *");
は開発時のテスト以外では避けて、本番環境では必要最小限のドメインのみ許可することが推奨される。
プリフライトリクエスト
特定の条件下 (カスタムヘッダの使用時等) では、Webブラウザは実際のリクエストの前にOPTIONSメソッドでプリフライトリクエストを送信する。
以下の例では、プリフライトリクエストに適切に応答している。
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header("Access-Control-Allow-Origin: https://example.com");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Max-Age: 86400");
exit(0);
}
エラーハンドリング
try {
// CORSヘッダの設定
if (!headers_sent()) {
header("Access-Control-Allow-Origin: https://example.com");
}
else {
throw new Exception("Headers already sent");
}
}
catch (Exception $e) {
error_log("CORS設定エラー : " . $e->getMessage());
}
Apache2の場合
.htaccessファイルによる設定
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
</IfModule>
※注意
Header set Access-Control-Allow-Origin "*"
の設定は、開発時のテスト以外では避けて、本番環境では必要最小限のドメインのみ許可することが推奨される。
クレデンシャル (認証情報) を含むリクエストを許可する場合は、ワイルドカード (*) は使用できない。