この記事を三行にまとめると
水溶性の化合物で天然の温泉に混入されていることが多いプリフライトリクエストの方では直接Authorizationの中身を取れない
正しいやり方があるなら教えてほしいっす
CORS・・・一酸化化合物の一種。水溶性の化合物で天然の温泉に混入されていることが多い。無色だが少し硫黄のにおいがする。
というのは真っ赤な嘘で、えーとこれは、クロスドメインリソースシェアリングの略ですかね。僕も詳しい説明はできないんですが……例えばAPIでデータを取得する時に、本当にそのデータ取得が信頼できる処理なのかを一度確かめようとするもの……みたいなことで良いのかな。
APIの開発なんかをしておりますと、クロスドメインの問題にどうしてもぶち当たると思います。簡単に言うと別のドメインからデータを参照する時などに「おい、そんなリクエストで大丈夫か?」と問われるようなことです。変なサイトから妙なデータを取ってきたりすると危ないですからね。そこには悪意のあるデータが含まれているかもしれない。
そこでGETなりPOSTなりリクエストを投げる前に、プリフライトというリクエストを送信することがあります。このプリフライトリクエストによって安全なリクエストだってことが確認されれば、改めてGETやらPOSTを送信するってすんぽーです。
まあ、ヘッダの設定を適切に行っておけば特に問題ないところなのですが、今回はそのプリフライトリクエストに関してちょっと困ったことがありまして……しかもその良い解決方法が浮かばなかったんで、見ていただきたいなと。あわよくば誰かベストプラクティスをご教示いただけないかと。そんな次第です。
例えばGETリクエストを投げようとしてプリフライトリクエストが発生した場合。
安全性を確かめる必要が生じる場合、こんな感じで二回のリクエストが発生します。
これはヘッダ情報(の一部)をログに取った結果。ほとんど一緒なのですが、プリフライトリクエストの方には「Access-Control-Request-Method」ってのがありますね。これは、実際のメソッドはGETですよ〜ってのを示す項目です。
まあ、安全なリクエストであればなーんも問題はございやせん。
実際に追加すると、リクエストの中身はこんな風に変わります。
プリフライトリクエストの方に「[Access-Control-Request-Headers」という項目が追加されてます。ヘッダ情報の中に「Authorization」ってのが含まれてるで〜ってなことを言っています。
ここで問題なのは、プリフライトリクエストの方では直接Authorizationの中身を取れないってことです。
だから例えば、このAuthorizationにアクセストークンをセットしてユーザー情報なんかを取得する場合に、プリフライトリクエストの方ではユーザー情報が取れないんですよね。ってことはログイン必須のページでユーザー情報が取得できないとエラーを返すような仕様にしていると、毎回必ずエラーになってしまうわけです。困ったちゃんなのです。
こういう時ってどうすれば良いんですかね?
強引にプリフライトリクエストかどうか判定して、プリフライトだったら何の処理も行わないようにするとか、何らかのHTTPステータスコード(204とか)を返すとか?
みたいな。
一応、上記みたいな書き方をすればエラーにはならないんですけど、でも何かしっくり来ないと言うか……すっきりしないっすね。
でも今の自分にはこれ以上の方法が思いつかないっす。正しいやり方があるなら教えてほしいっす。
というのは真っ赤な嘘で、えーとこれは、クロスドメインリソースシェアリングの略ですかね。僕も詳しい説明はできないんですが……例えば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 {
//エラーを返す
}
みたいな。
一応、上記みたいな書き方をすればエラーにはならないんですけど、でも何かしっくり来ないと言うか……すっきりしないっすね。
でも今の自分にはこれ以上の方法が思いつかないっす。正しいやり方があるなら教えてほしいっす。