YAHOO! 知恵袋に
(どなたか本当にお願いします!phpのcurlに関して教えて頂きたいです。)というPHPのcURLを使って、Googleにログインする方法が聞かれていたのでちょっと組んでみた。
実はこういうのは意外と面倒くさくて、
POSTデータでIDとパスワードを飛ばせばいいというものではない。
もちろんそれでログインできる(できてしまう)サイトもあるのだが、
セキュリティポリシーの高いサイトではそうはいかない。
不正なログインを防ぐためにフォーム内にトークンを埋め込み、
かつCookieにもそのトークンを埋め込んでおき、
サブミットされた際にフォームから飛んできたPOSTとCookieを比較しているのだ。
ちなみにGoogleとPixivはこの方式を採用している(2012/08/27現在)。
とりあえず早速ソースを見ていこう。
//URLを指定する
$url='https://accounts.google.com/ServiceLoginAuth';
//POST用のデータを作っておく
$data=array
(
//ID部分(適宜置き換え)
'Email'=>'',
//パスワード部分(適宜置き換え)
'Passwd'=>'',
//ログインを維持するかのチェックボックス部分
//'PersistentCookie'=>'yes',
);
//テンポラリファイルを作成する
$cookie=tempnam(sys_get_temp_dir(),'cookie_');
//cURLを初期化して使用可能にする
$curl=curl_init();
//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//文字列で結果を返させる
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
//クッキーを書き込むファイルを指定
curl_setopt($curl,CURLOPT_COOKIEJAR,$cookie);
//URLにアクセスし、結果を文字列として返す
$html=curl_exec($curl);
//cURLのリソースを解放する
curl_close($curl);
//Document初期化
$dom=new DOMDocument();
//html文字列を読み込む(htmlに誤りがある場合エラーが出るので@をつける)
@$dom->loadHTML($html);
//XPath初期化
$xpath=new DOMXPath($dom);
//inputのtypeがhiddenの要素をとってくる
$node=$xpath->query('//input[@type="hidden"]');
foreach($node as $v)
{
//POST用のデータに追加する
$data[$v->getAttribute('name')]=$v->getAttribute('value');
}
//cURLを初期化して使用可能にする
$curl=curl_init();
//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//メソッドをPOSTに設定
curl_setopt ($curl,CURLOPT_POST,true);
//POSTデータ設定
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
//クッキーを読み込むファイルを指定
curl_setopt($curl,CURLOPT_COOKIEFILE,$cookie);
//Locationをたどる
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,true);
//URLにアクセスし、結果を表示させる
curl_exec($curl);
//cURLのリソースを解放する
curl_close($curl);
//テンポラリファイルを削除
unlink($cookie);
という感じになる。
さて、実際に何をしているか見ていくと
//URLを指定する
$url='https://accounts.google.com/ServiceLoginAuth';
//POST用のデータを作っておく
$data=array
(
//ID部分(適宜置き換え)
'Email'=>'',
//パスワード部分(適宜置き換え)
'Passwd'=>'',
//ログインを維持するかのチェックボックス部分
//'PersistentCookie'=>'yes',
);
//テンポラリファイルを作成する
$cookie=tempnam(sys_get_temp_dir(),'cookie_');
の部分で必要な変数を用意している。
ID部分とパスワード部分は適宜置き換えて欲しい。
//cURLを初期化して使用可能にする
$curl=curl_init();
//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//文字列で結果を返させる
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
//クッキーを書き込むファイルを指定
curl_setopt($curl,CURLOPT_COOKIEJAR,$cookie);
//URLにアクセスし、結果を文字列として返す
$html=curl_exec($curl);
//cURLのリソースを解放する
curl_close($curl);
の部分でフォーム内のトークンを分析するためのHTMLを取得する。
//Document初期化
$dom=new DOMDocument();
//html文字列を読み込む(htmlに誤りがある場合エラーが出るので@をつける)
@$dom->loadHTML($html);
//XPath初期化
$xpath=new DOMXPath($dom);
//inputのtypeがhiddenの要素をとってくる
$node=$xpath->query('//input[@type="hidden"]');
foreach($node as $v)
{
//POST用のデータに追加する
$data[$v->getAttribute('name')]=$v->getAttribute('value');
}
の部分でHTML解析を実際に行いトークンを取得し、POST用データ配列に入れる。
//cURLを初期化して使用可能にする
$curl=curl_init();
//オプションにURLを設定する
curl_setopt($curl,CURLOPT_URL,$url);
//メソッドをPOSTに設定
curl_setopt ($curl,CURLOPT_POST,true);
//POSTデータ設定
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
//クッキーを読み込むファイルを指定
curl_setopt($curl,CURLOPT_COOKIEFILE,$cookie);
//Locationをたどる
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,true);
//URLにアクセスし、結果を表示させる
curl_exec($curl);
//cURLのリソースを解放する
curl_close($curl);
の部分で今まで作ったPOSTデータを飛ばしログインしている。
//テンポラリファイルを削除
unlink($cookie);
の部分でcookie保持用に用意したファイルを消しておく。
ただ、実際は放っておけばそのうち勝手に消えてしまうファイルなので、
わざわざ消す必要がないと言えばないのだが・・・
ポイントとなる関数