Basic認証のCORSエラーに対応する
はじめに
Basic認証のリクエストでCORSエラーが発生した時に対応した方法についてまとめます。
CORSとは
CORS(Cross-Origin Resource Sharing)とは、異なるオリジン(ドメイン、プロトコル、ポート番号)間でのリソース共有を制御する仕組みです。
ブラウザのセキュリティポリシーにより、異なるオリジンからのリクエストに対してはデフォルトでアクセスが制限されます。
CORSエラーの原因
CORSエラーは、次のような原因で発生する可能性があります。
- サーバーが適切なCORSヘッダを返さない場合
- リクエストの起点となるオリジンが許可されていない場合
- リクエストに認証情報が含まれており、サーバーが適切な設定をしていない場合
Basic認証なしのCORSリクエストの対応
まずは、Basic認証なしの通常のCORSリクエストの対応方法です。
-
CORSリクエストを送信します
今回はXMLHttpRequestを使用してリクエストを送信します。request.jsfunction httpRequest(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); } }; xhr.send(); }
-
サーバ側で適切なCORSヘッダを返していない場合は、CORS policyのヘッダエラーが返ります
blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
-
サーバ側で適切なCORSヘッダを返す様に設定します
httpd.conf<IfModule mod_headers.c> Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "POST, GET, OPTIONS" Header set Access-Control-Allow-Headers "Content-Type, Authorization" </IfModule>
-
サーバ側で適切なCORSヘッダを設定すると、正常なレスポンスが返る様になります
Basic認証ありのCORSリクエストの対応
Basic認証ありのCORSリクエストでは、CORSヘッダを返すだけでは不十分です。
(ここでは、Basic認証なしのリクエストで対応した『サーバ側で適切なCORSヘッダを返す設定』は、既に対応していることを前提に進めます)
-
Basic認証ありのCORSリクエストを送信します
request.jsfunction httpRequest(url, username, password) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); // Basic認証を設定 var credentials = btoa(username + ':' + password); xhr.setRequestHeader('Authorization', 'Basic ' + credentials); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); } }; xhr.send(); }
-
サーバ側でCORSヘッダを返す様に設定していても、CORS policyのpreflightリクエストエラーが返ります
blocked by CORS policy: Response to preflight request doesn't pass access control check
レスポンスヘッダを確認すると設定したはずのヘッダが入っていません。
色々と調査した結果、Basic認証のCORSリクエストでは、最初にpreflightリクエストでOPTIONSメソッドを送信し、サーバからOKレスポンスを受け取ると、続けてGETメソッドを送信して、レスポンスデータ受け取るという2段階の通信をおこなっているのですが、最初のOPTIONSメソッドではBasic認証のAuthorizationヘッダが入らないので、Basic認証が通らないことが分かりました。
更に、認証を必要とする様なレスポンスデータは2番目のGETメソッドのレスポンスとして返却される為、GETメソッドのBasic認証が有効であれば、最初のOPTIONSメソッドのBasic認証は必ずしも必要無いことが分かりました。
-
OPTIONS以外のメソッドのみ、Basic認証を行う様に設定します
.htaccess<LimitExcept OPTIONS> AuthUserFile /home/www/example/.htpasswd AuthName "your ID and password" AuthType Basic Require valid-user </LimitExcept>
-
Basic認証で正常なレスポンスが返る様になります
最初のOPTIONSメソッド
2番目のGETメソッド
-
Basic認証で誤ったIDやパスワードを指定するとGETメソッドでエラーが返ります
最初のOPTIONSメソッド
2番目のGETメソッド
ブラウザのキャッシュが残っていると上手く反映されない場合があるので、適宜キャッシュをクリアしてください。
Discussion