説明
CORSは、ブラウザーが別のオリジンからの応答を読める条件を定めます。未検証のOriginをAccess-Control-Allow-Originへそのまま反映すると、信頼できないサイトが応答を読める可能性があります。認証済みの応答が漏えいするかどうかは、ブラウザーの認証情報送信、Cookieポリシー、サーバーの認証情報許可設定にも依存します。CORSを許可しただけで、セッションCookieの値が相手のサイトへ送られるわけではありません。
想定される影響
- 信頼できないサイトが機密性のあるAPI応答を読める可能性があります。
- 認証情報付きリクエストが許可される条件では、被害者の権限で返されるデータが漏えいするおそれがあります。
- CORSは認証・認可やCSRF対策の代わりにはならず、それぞれの保護も必要です。
対処方法
- 許可する送信元のスキーム、ホスト、ポートを明示的な許可リストで管理してください。
HttpServletResponse.setHeader("Access-Control-Allow-Origin", userInput)のように未検証の入力を反映しないでください。公開する応答への意図的な*許可と、機密性のある応答を区別してください。- 送信元によって応答ヘッダーが変わり、応答をキャッシュする場合は
Vary: Originも設定してください。認証情報は必要な送信元だけに許可し、認証・権限確認とCSRF対策を維持してください。
例
許可する送信元は実際のサービスに合わせてください。以下は応答ヘッダー設定の抜粋であり、プリフライトと認証情報のポリシーは別途構成する必要があります。
変更前
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class InsecureCORS {
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String origin = request.getHeader("Origin"); // ユーザー入力に基づく設定
response.setHeader("Access-Control-Allow-Origin", origin); // 未検証の送信元
}
}
変更後
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
public class SecureCORS {
private static final Set<String> ALLOWED_ORIGINS = Set.of(
"https://example.com",
"https://sub1.example.com",
"https://sub2.example.com"
);
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String origin = request.getHeader("Origin");
if (origin != null && ALLOWED_ORIGINS.contains(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
} else {
response.setHeader("Access-Control-Allow-Origin", "https://example.com"); // 既定の許可する送信元
}
}
}
説明:
- 変更前: ユーザー入力を
Access-Control-Allow-Originへ直接設定し、攻撃者の送信元を許可できてしまいます。 - 変更後: 許可リストにある送信元だけを反映します。それ以外には固定の送信元を返すため、一致しない送信元からは応答を読めません。