Web

【セキュリティー】CORS(Cross-Origin Resource Sharing)まとめ Part 2

引き続きCORSのまとめをやっていきたいと思います。今回は認証情報を含むリクエストに関してです。それではさっそく見ていきます!

withCredentialsプロパティ

デフォルトではクロスオリジンに対するリクエストにHTTP認証やクッキーなどの認証に用いられるリクエストヘッダは自動的に送信されません。これらを用いるためにはXMLHttpRequestのwithCredentialsプロパティをtrueでセットする必要があります。

withCredentialsプロパティをセットしない場合

まずはセットしない場合のサーバーとのやりとりを見ていきます。

GET http://example.jp/33/33-005.html HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://example.jp/33/
Upgrade-Insecure-Requests: 1
Host: example.jp
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Wed, 05 May 2021 23:34:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 408
Connection: keep-alive
Last-Modified: Mon, 14 May 2018 13:08:18 GMT
ETag: "198-56c2a2decfddb-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
X-UA-Compatible: IE=edge

<body>
<script>
  var req = new XMLHttpRequest();
  req.open('GET', 'http://api.example.net/33/33-006.php');
  req.onreadystatechange = function() {
      if (req.readyState == 4 && req.status == 200) {
         var span = document.getElementById('counter');
        span.textContent = req.responseText;
      }
  };
  req.send(null);
</script>
呼び出しカウンター:<span id="counter"></span>
</body>
GET http://api.example.net/33/33-006.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://example.jp
Connection: keep-alive
Referer: http://example.jp/
Host: api.example.net
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Wed, 05 May 2021 23:34:34 GMT
Content-Type: application/json
Content-Length: 11
Connection: keep-alive
Set-Cookie: PHPSESSID=jrpe1qbb85250e8m6ts9pes4j3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Access-Control-Allow-Origin: http://example.jp
X-UA-Compatible: IE=edge

{"count":1}

サーバーはSet-Cookieヘッダでクッキーをセットしようとしていることが分かります。ブラウザにクッキーがセットされているか確認するためリロードしてリクエストヘッダーを確認します。

GET http://api.example.net/33/33-006.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://example.jp
Connection: keep-alive
Referer: http://example.jp/
Cache-Control: max-age=0
Host: api.example.net

クッキーがセットされていないことが分かります。

withCredentialsプロパティをセットする場合

今度はwithCredentialsプロパティをセットしてクロスオリジンへリクエストを投げてみます。

var req = new XMLHttpRequest();
req.open('GET', 'http://api.example.net/33/33-006.php');
req.withCredentials = true; # 追記

今度はブラウザのコンソールでエラーが置きました。

クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://api.example.net/33/33-006.php にあるリモートリソースの読み込みは拒否されます (理由: CORS ヘッダー ‘Access-Control-Allow-Credentials’ は ‘true’ であるべき)。

withCredentialsプロパティをtrueにしたリクエストでは、サーバーはAccess-Control-Allow-Credentials: trueというヘッダーを返さなければなりません。

最後にレスポンスヘッダーをセットし、正しくクッキーがセットされたやりとりを見ておきます。

GET http://api.example.net/33/33-006b.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://example.jp
Connection: keep-alive
Referer: http://example.jp/
Host: api.example.net
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Wed, 05 May 2021 23:51:34 GMT
Content-Type: application/json
Content-Length: 11
Connection: keep-alive
Set-Cookie: PHPSESSID=1aaucob4d3qsqtgas69tdnfch2; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Access-Control-Allow-Origin: http://example.jp
Access-Control-Allow-Credentials: true
X-UA-Compatible: IE=edge

{"count":1}

リロードしてみると、クッキーがセットされているのが分かります。

GET http://api.example.net/33/33-006b.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://example.jp
Connection: keep-alive
Referer: http://example.jp/
Cookie: PHPSESSID=1aaucob4d3qsqtgas69tdnfch2
Cache-Control: max-age=0
Host: api.example.net

もぐくん
もぐくん
やったね!

  • XMLHttpRequestオブジェクトのwithCredentialsプロパティをtrueにする
  • レスポンスヘッダとしてAccess-Control-Allow-Credentials: trueを返す

さいごに

認証を含むリクエストの場合、プロパティを変更したりヘッダーをセットしたりと色々なことをしないといけないことが分かりました。今後、自分で認証を実装するときはこういうことに気をつけたいと思いました。

ここまで読んでいただきありがとうございました。

ABOUT ME
sakai
三重出身の28歳。前職はメーカーで働いていて、プログラミングスクールに通って未経験からWeb業界に転職しました。Railsをメインで使っていて、AWSも少しできます。音楽を聞くこととYoutubeを見るのが好きです。最近はへきトラ劇場にハマってます