JS memo

お洒落にJavaScript。

2019年3月10日15:32

【CORS】クロスドメインによるエラーを解決する

メインビジュアル

【CORS】クロスドメインによるエラーを解決する

SEOの調査をするために、特定のキーワードをGoogle検索して表示された結果ページを取得して自分のページに反映させるというアプリをjavascriptのXMLHttpRequestを使って作成していたところ以下のエラーが発生しました。

Access to XMLHttpRequest at 'https://...(google検索結果のURL)' from origin 'http://...(javascriptを設置したページのURL)' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

CORS policy?Access-Control-Allow-Origin?なんかよくわからないけど、ヘッダーに何かが書かれていなくてポリシーに違反しているっぽい?専門用語が多すぎてちんぷんかんぷんでした。

原因をググってみたら結構多くの人がこの現象に遭遇しているみたいなので、このエラーの発生原因と解決策をまとめました。

  1. エラー発生の経緯
  2. エラーの原因
  3. エラーの解決策
  4. まとめ

エラーが発生した経緯

事件が発生したのはJavascriptのAjax通信を利用して違うドメインのサイトの情報を取得しようとしていた時でした。

実装内容の詳細は
特定のキーワードをgoogle検索した結果ページに表示されたページのタイトルをJavaScriptで取得→
自分のページに表示させる
とゆうものでした。

Javascript

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.google.com/search?q=%E5%B1%85%E9%85%92%E5%B1%8B", true);
xhr.responseType = "text";
xhr.send();
xhr.onreadystatechange = function() {
  if (this.readyState == 4 && this.status == 200) {
    // 結果表示されたページのタイトルを取得するコード
  }
};

このコードをRunすると下記のエラーが発生

Access to XMLHttpRequest at 'https://...(google検索結果のURL)' from origin 'http://...(javascriptを設置したページのURL)' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

日本語に訳すると「httpヘッダーに"Access-Control-Allow-Origin"が書かれていないので、CORS policyに違反している」ということらしいのですが、
そもそも【Access-Control-Allow-Origin】や【CORS policy】が分からなかったので、このエラーについて単語の意味から詳しく調べて見ました。

エラーの原因

このエラーの原因を探って見て、わかったことについて説明します。

CORSとは?

CORSとは【Cross Origin Resourse Sharing】の略で、一言で言えば、違うドメイン同士でjavascriptのxhrやfetchなどのAJAX通信をする際に、安全にやりとりができるよう定められた規約です。

そもそも違うドメインのページ同士はブラウザ上でAjax通信を通じてデータをやりとりすることができません!とsame-origin policyというルールが存在します。(正確には同じドメインでしかやりとりができませんというルールです。)

例えばsame-origin policyがない世界があったとします。その世界で自分の口座を登録している銀行のサイトにアクセスし、ログインアウトせずに悪意のあるサイトにアクセスしたとします。
自分の口座情報を格納したCookieが銀行のサイトで作成されていた場合、悪意のあるサイトはそのCookie情報を意図もたやすく取得できてしまいます。
もしそうなれば後は何が起こる想像がつきますよね。自分の銀行からお金が盗まれます。

same-origin policyは一見すると厄介に見えますが、実は私たちの情報を守ってくれている重要なルールなのです。しかし、全く悪意がない場合でも違うドメイン間でデータをやりとりしたい時ってありますよね。
そう行った場合に安全にデータをやりとりできるようにしてくれたものがCORSなのです。

Access-Control-Allow-Originとは?

ヘッダーとはページの言語、クッキーなどの必要な情報ほぼ全てを格納している箱のようなもので、ページがリクエスト(アクセス)された時に、そのヘッダーの中からリクエストに応じてページに反映する情報が決められます。
ヘッダー内容はChormeのディベロッパーツール>Network>Headersから確認することができます。

Access-Control-Allow-Originとはそのヘッダーの情報の一つで、そのページのデータのやりとりを許可するドメインを設定することができます。

例えば、

Access-Control-Allow-Origin: 'https://example.com'

ならhttps://example.comからのリクエストはOKしますよ!ということです。

全てのドメインからリクエストを許可している場合は、

Access-Control-Allow-Origin: '*'

となっています。

ここで薄々気づいているとは思いますが、今回のエラーはwww.google.comのページのAccess-Control-Allow-Originで私のサイトが許可されていないことが原因でした。
逆に許可されていたらすごいですよね笑。

そのためこのエラーの一番ベストな解決策としはGoogleさんにデータのやりとりを許可していただくことです。
もっともみたいな言い方をしてしまいましたが、Googleさんに許可をいただくのはアメリカの大統領になるぐらい難しいので、他にこのエラーを回避する方法を探してみると根本的解決ではないですが、色々あったのでご紹介します。

エラーの解決策

1. Chromeの拡張機能を使う

youtubeでChromeの拡張機能を使用する方法が紹介されていました。

使い方は以下のページから拡張機能を設置し、Chromeのブラウザ上で自分のxhrを使ったJavascriptを書いたページを表示し、この機能をオンにするだけです。

Allow-Control-Allow-Origin

一番簡単な方法で、これでAccess-Control-Allow-Origin問題をクリアできますが、もしかするとCORBによって次の問題が発生するかもしれません。

CORBについてはまた記事に書きたいと思います。

2. fetchのmodeを使用する

Javascriptにはxhrと似たようなfetchというAPIがあります。※以下参照

Fetch 概説 - Web API | MDN

このfetch APIのオプションにはクロスオリジンに対応するため、modeというものがあり、'cors','no-cors','same-origin'が設定できます。
このmodeに'no-cors'を設定すると、CORSの規定に違反してもエラーではなくnullの値が返ってきます。

この方法ではエラーは回避できますが、リソースは取得できません。

3. サーバーサイドでページを取得

CORSはJavascriptのxhrやfetchを使用した時に発生する問題でした。

そのため最終的な手段としてpythonやnode.jsなどサーバーサイドでページを取得するコードを書いて、そこで取得したデータをフロントエンドに渡す方法があります。

今回やりたかったことはサーバーサイドで実行できたので、ちょっとモヤモヤしていますがNode.jsを使って解決しました。

まとめ

このエラーを理解するのに結構時間がかかりましたが、今回はhttpヘッダーとかセキュリティー面で色々学べました。

次回はCORBについて書きたいと思います。BYE