CORS問題

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
フライト画像

この記事を三行にまとめると

水溶性の化合物で天然の温泉に混入されていることが多い
プリフライトリクエストの方では直接Authorizationの中身を取れない
正しいやり方があるなら教えてほしいっす
CORS・・・一酸化化合物の一種。水溶性の化合物で天然の温泉に混入されていることが多い。無色だが少し硫黄のにおいがする。

というのは真っ赤な嘘で、えーとこれは、クロスドメインリソースシェアリングの略ですかね。僕も詳しい説明はできないんですが……例えばAPIでデータを取得する時に、本当にそのデータ取得が信頼できる処理なのかを一度確かめようとするもの……みたいなことで良いのかな。

APIの開発なんかをしておりますと、クロスドメインの問題にどうしてもぶち当たると思います。簡単に言うと別のドメインからデータを参照する時などに「おい、そんなリクエストで大丈夫か?」と問われるようなことです。変なサイトから妙なデータを取ってきたりすると危ないですからね。そこには悪意のあるデータが含まれているかもしれない。

そこでGETなりPOSTなりリクエストを投げる前に、プリフライトというリクエストを送信することがあります。このプリフライトリクエストによって安全なリクエストだってことが確認されれば、改めてGETやらPOSTを送信するってすんぽーです。

まあ、ヘッダの設定を適切に行っておけば特に問題ないところなのですが、今回はそのプリフライトリクエストに関してちょっと困ったことがありまして……しかもその良い解決方法が浮かばなかったんで、見ていただきたいなと。あわよくば誰かベストプラクティスをご教示いただけないかと。そんな次第です。



プリフライトリクエスト

説明が重複しちゃいますが、プリフライトリクエストってのは、安全なリクエストか確かめるためにOPTIONSメソッドで送信されるリクエストです。GET、POST、HEAD以外のメソッドを使用する場合や、カスタムヘッダを送信する場合などに発生します。プリフライトリクエストによって安全が確認できればGETやPOSTリクエストが送信されるので、動きとしては二回リクエストが投げられていることになります。

例えばGETリクエストを投げようとしてプリフライトリクエストが発生した場合。

//プリフライトリクエスト
Array (
  [host] => norm-nois.com
  [Accept-Encoding] => gzip, deflate, sdch
  [Accept-Language] => ja,en-US;q=0.8,en;q=0.6
  [Access-Control-Request-Method] => GET
  [Connection] => keep-alive
)

//実際のGETリクエスト
Array (
  [host] => norm-nois.com
  [Accept-Encoding] => gzip, deflate, sdch
  [Accept-Language] => ja,en-US;q=0.8,en;q=0.6
  [Connection] => keep-alive
)

安全性を確かめる必要が生じる場合、こんな感じで二回のリクエストが発生します。

これはヘッダ情報(の一部)をログに取った結果。ほとんど一緒なのですが、プリフライトリクエストの方には「Access-Control-Request-Method」ってのがありますね。これは、実際のメソッドはGETですよ〜ってのを示す項目です。

まあ、安全なリクエストであればなーんも問題はございやせん。



カスタムヘッダ

じゃあ何が問題なのかっつー話なのですが、例えばヘッダ情報に「Authorization」みたいなカスタムヘッダを追加したい場合。

実際に追加すると、リクエストの中身はこんな風に変わります。

//プリフライトリクエスト
Array (
  [host] => norm-nois.com
  [Accept-Encoding] => gzip, deflate, sdch
  [Accept-Language] => ja,en-US;q=0.8,en;q=0.6
  [Access-Control-Request-Headers] => authorization
  [Access-Control-Request-Method] => GET
  [Connection] => keep-alive
)

//実際のGETリクエスト
Array (
  [host] => norm-nois.com
  [Accept-Encoding] => gzip, deflate, sdch
  [Accept-Language] => ja,en-US;q=0.8,en;q=0.6
  [Authorization] => abcde1234
  [Connection] => keep-alive
)

プリフライトリクエストの方に「[Access-Control-Request-Headers」という項目が追加されてます。ヘッダ情報の中に「Authorization」ってのが含まれてるで〜ってなことを言っています。

ここで問題なのは、プリフライトリクエストの方では直接Authorizationの中身を取れないってことです。

だから例えば、このAuthorizationにアクセストークンをセットしてユーザー情報なんかを取得する場合に、プリフライトリクエストの方ではユーザー情報が取れないんですよね。ってことはログイン必須のページでユーザー情報が取得できないとエラーを返すような仕様にしていると、毎回必ずエラーになってしまうわけです。困ったちゃんなのです。

//ヘッダ情報を取得
$headers = getallheaders();

//トークンがあるか判定
if(!empty($headers['Authorization'])) {
  //ユーザー情報を取得
} else {
  //エラーを返す
}

こういう時ってどうすれば良いんですかね?

強引にプリフライトリクエストかどうか判定して、プリフライトだったら何の処理も行わないようにするとか、何らかのHTTPステータスコード(204とか)を返すとか?

//プリフライトリクエストだったら処理終了
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
  exit;
}

//トークンの判定やらユーザー情報の取得を行う
$headers = getallheaders();
if(!empty($headers['Authorization'])) {
  //ユーザー情報を取得
} else {
  //エラーを返す
}

みたいな。






一応、上記みたいな書き方をすればエラーにはならないんですけど、でも何かしっくり来ないと言うか……すっきりしないっすね。

でも今の自分にはこれ以上の方法が思いつかないっす。正しいやり方があるなら教えてほしいっす。
 もしかしたら何か関連しているかも? 
 みんなからのコメント 
2020年05月01日 00:13:52
匿名
助かりました。ありがとうございます☆
2020年05月01日 19:14:03
まっち~(管理人)
いえいえ、お役に立てて何よりです。