😄
【解決法】EC-CUBEで外部APIとのAjax通信がCORSエラーに!CSRFトークンの自動付与が原因
■事象
EC-CUBEにプラグインを用いてクレジットカード決済システムを追加しました。
HTML単体での連携テストでは問題ありませんでしたが、EC-CUBEに組み込むとブラウザのコンソールに以下のCORSエラーが表示され、正常に動作しません。
jquery.js:9940 Access to XMLHttpRequest at 'https://xxxx.co.jp/xxxx/xxxx.aspx' from origin 'https://xxxx.xxxx.xxxx' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
■原因
このエラーは、CORS(Cross-Origin Resource Sharing)ポリシーによりリクエストがブロックされた ことを示しています。
ブラウザは、JavaScriptでクロスドメイン通信を行う際に、事前に プリフライトリクエスト(OPTIONS) を送信します。
この際、通信先サーバーから以下のようなレスポンスヘッダーが必要です:
Access-Control-Allow-Origin: *
しかし、レスポンスにこのヘッダーが存在しないため、ブラウザのセキュリティ機能によってリクエストが遮断されました。
なぜHTMLでは問題なく、EC-CUBEではエラーになるのか?
EC-CUBEから出力されたページには、以下のJavaScriptが含まれています:
$.ajaxSetup({
headers: {
'ECCUBE-CSRF-TOKEN': $('meta[name="eccube-csrf-token"]').attr('content')
}
});
このコードにより、すべての $.ajax()
リクエストにカスタムヘッダー ECCUBE-CSRF-TOKEN
が自動で追加されます。
カスタムヘッダーを含むリクエストは、「単純リクエスト」ではなくなり、プリフライト(OPTIONS)リクエストが必要になります。
その結果、通信先サーバーが以下を返さなければ、CORSによってブロックされてしまいます:
Access-Control-Allow-Origin
Access-Control-Allow-Headers: ECCUBE-CSRF-TOKEN
■解決方法
特定のドメインに対して、CSRFヘッダーを付与しないように制御する
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.crossDomain &&
/^https:\/\/xxxx\.xxxx\.co\.jp/.test(options.url)) {
// EC-CUBEが付与するCSRFヘッダーを削除
delete options.headers['ECCUBE-CSRF-TOKEN'];
}
});
この処理により、特定の外部ドメインに対するリクエストからCSRFヘッダーが除外され、プリフライトリクエストが不要になります。
その結果、CORSエラーが回避され、決済システムが正常に動作するようになります。
■補足
- この対応はあくまで外部の信頼できるサーバーに限定して適用するべきです。
- サーバー側でCORS設定を行える場合は、そちらで
Access-Control-Allow-*
ヘッダーを追加するのが本来の対応です。
Discussion