DNSが原因で発生したエラーについて(social loginプラグイン利用時)

※今回は私の備忘録的な意味合いが強いので、話しがあちこち分散していたり、ちょっと意味不明な感じかもしれませんがご容赦ください。m(__)m

先日、担当のサイトの「Facebookでログイン」「Twitterでログイン」のボタンが表示されたり、されなくなったり、表示されてもなかなかログイン出来なくなる、という現象が発生し、それが3日間続きました。その対応をしていく中で分かった事を書きたいと思います。

序章(ソーシャルログインとは)

最近、色んなサイトでよく見かける「Facebookでログイン」的な、ソーシャルアカウントによる簡単ログインボタンですが、これをWordPressで構築したサイトにも実装していました。

ただ、WPかどうかに関わらず、ソーシャル系アカウントでユーザー登録およびログインを行う機能を実装しているサイトの大半は、OAuthを使って認証させているんじゃないでしょうか。こちらについては、以前からセキュリティ上の問題が指摘されています。

【参考】
■単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる
http://www.sakimura.org/2012/02/1487/

■非技術者のためのOAuth認証(?)とOpenIDの違い入門
http://www.sakimura.org/2011/05/1087/

■Idcon11 implicit demo(Access Tokenだけで別人としてログインさせることが出来るデモ)
http://www.slideshare.net/ritou/idcon11-implicit-demo

■OAuth 2.0 Implicit Flowをユーザー認証に利用する際のリスクと対策方法について
http://d.hatena.ne.jp/ritou/20120206/1328484575

とは言っても、Twitterアカウントでのログインを実現させるためには、今のところOAuth以外の方法が無いので(と思っているのですが、もしそうでない場合にはご指摘いただけるとありがたいです)、ここをどうリスクヘッジさせるか考えた結果、認証部分はアクセス元サーバー情報や、アクセストークン以外の個別認証を実現している有料の外部サービスを使おうと思いました。

ソーシャルログインを実現する外部サービス

色々あるかと思いますが、WordPressサイト用にプラグインを提供しているサービスを使いたかったので、私はONEALL.comを利用することにしました。

■ONEALL.com
http://www.oneall.com/

APIも充実していて、JSONで各種ユーザー情報が取得出来たりするのも魅力でした。

APIの説明ページ
http://docs.oneall.com/api/

でもデメリットもいくつかあります。挙げるとしたら、こんな感じ。

  • 海外のサービスなので、レスポンスは総じて遅い(1秒以上かかる時もある)
  • 5,000ユーザー以上は有料
  • 無料プランだと、ボタンのデザインをカスタマイズ出来ない
  • 極稀に、返ってくるはずのレスポンスが空の時がある!(※1)

※1)
原因は不明だけど、使用開始当初は100回に1回くらいは起こっていた。(この件については別途書きます、いつか…)
そのため、自分でエラー処理を追加しないと、ユーザーはログインしたつもりになっていても、ログイン出来ていないという事が発生する。古いバージョンのプラグインを使っている人は今すぐアップデートして、さらに自分でエラー処理を追加コーディングする必要がある。

起こった問題(これが本題)

今回、ログインボタンすら表示されなくなったり、表示されてもログインがなかなか出来ない、という意味不明な状態に陥りました。

まず、ログインボタンを表示させる部分は、oneall側のJavaSriptを通さないと表示出来ない仕組みになっています。そしてログイン処理はcURLでoneallに接続する仕組みになっていました。
ボタンが表示された後にログインに失敗する部分のエラーログを確認すると、cURLで “Couldn't resolve host 'xxxxxxx” といったエラーが発生しておりました。

原因解明までに色々と時間を要しましたが、oneallドメインの権威ネームサーバに問題があり名前解決が出来ない事象が頻発したために起こった、という事が最終的に判明しました。いつからネームサーバに問題があったのかは分からないのですが、この問題が発生する1週間前に、利用しているレンタルサーバー側でDNSサーバの仕様変更が入り、外部ドメインのキャッシュが利用出来なくなった事が原因で発覚した、という経緯です。

DNS周りを確認する時に便利なコマンドとか諸々

Facebook上で、他のエンジニア様に相談した結果、色々とアドバイスをいただいたので、ここにメモしておこうと思います。

コマンド系

#DNSの問合せ結果をトレースする  
$ dig +trace 【example.com】 

#↑この結果に表示されたDNSに対して1つずつ確認  
$ dig +short 【example.com】 @【name.server.com】
#ホスト情報を確認する
$ host -t ns 【example.com】

#↑この結果に表示されたDNSに対して1つずつ確認  
$ host 【example.com】 【name.server.com】

力技系

cURLで、直IPアドレスで接続する方法。
(別の問題が発生したために、結局実装するところまで行けなかったのですが、コード例をいただいたので備忘録として記載させていただきます)

<?php
// ※gethostbyname関数もDNS指定できないので、
// プロセスが一個上がってしまうのを無視すれば、という前提条件付きで使用
$host = "www.php.net";
$uri = "/manual/ja/book.curl.php";
$ip_address = `dig @8.8.8.8 A $host | grep -E "$host\.[\s\t]+[0-9]+[\s\t]+IN[\t]+A[\t]" | awk '{print $5}'|head -1`;

$s = curl_init();
curl_setopt($s, CURLOPT_URL, 'http://' . $ip_address . $uri);
curl_setopt($s, CURLOPT_HTTPHEADER, array('Host' => $host));
$res = curl_exec($s);
curl_close($s);
var_dump($res);
?>

手っ取り早く解決したかったら、/etc/hosts とかにIPを追記してしまう。最強だけど、根本解決ではない。

さいごに

この件をONEALL側にメールで報告したところ、問題のあったネームサーバーをZONEから外してくれたので、名前解決も出来るようになって今では問題なくログイン処理が出来るようになりました。
英語でのコミュニケーションをしていた時には、素っ気無い反応しかなくて何度も心が折れそうになりましたが、コマンド結果を貼り付けて送ったところ、速攻で「Thank you once again for your debugging.」というコメントと共に、問題を解決してくれました。もし言葉は通じなくても、エンジニアならコマンド結果だけで意図が通じるもんですね!!!(本当に嬉しかった)

今回のことでDNSについて、多少理解が出来ました。
アドバイスいただいた皆様、本当にありがとうございます!!