本サイトでは、アフィリエイト広告およびGoogleアドセンスを利用しています。

curl で Cookie 認証突破

様々な Web サービスがありますが利用する際に、ユーザーはまずログインして使用するといったものがあります。
有名どころはアプリケーション用のAPIキーを発行して使用させてくれたりしますが、そうでないところもまだ残っているんじゃ無いかと思います。
それと、 API キーを発行するまでじゃないんだよなぁといったときに、ログインしてちょっとだけ処理させたいといった需要でなんとか出来ないかトライした記録です。

はじめに

通常の Basic 認証なら、 curl のオプションに設定して終わりだったりします。ここでは専用のログインフォームがあって、そこに入力するといったものを想定しています。

ここで対象にあげたサービスらはちゃんと REST API を備えているので、今回紹介するような話は全く不要のものとなります。無駄な努力は避けるようにしましょう・・・。

実験 その1 Twitter

まず近年の傾向として、 CSRF 対策が入っていることが多いです。単なるユーザーとパスワードの認証なら POST して終わり、という認識でいると余計に時間をとられることになると思われます。

今回では、最初のログインフォームが表示された画面で、 authenticity_token と呼ばれる項目で、何かしらのキーが埋め込まれています。
これを設定してログイン情報を送り返す(POSTする)必要があります。ちゃんとチェックしているサイトならば、ここでログインに失敗します。

authenticity_tokenを得る

ログインのURLに以下のようにしてアクセスします。

$ curl https://twitter.com/login \
 -c login.cookie -o result.html 

もし、取得した結果が圧縮されたデータがくるようなら、 ” –compressed” オプションを追加します。

取得した result.html の中から、 authenticity_token が記述された部分があるのでこれをメモっておきます。

$ cat result.html | grep authenticity_token

ログインする

得られたトークンを使ってログインを行います。
Twitter の場合、現時点においてはログインボタンを押した後は /session のURL へアクセスするようになっています。
また、ユーザー名・パスワードの格納状況についても、ログイン画面の html を参照して確認しておきます。

そうして、必要なデータを POST で URLENCODED で送ります。このときのコマンドは以下のようになります。

curl -X POST https://twitter.com/sessions \
-b login.cookie \
-c session.cookie \
-H "Content-type: application/x-www-form-urlencoded" \
--data-urlencode "authenticity_token=得られたauthenticity_token" \
--data-urlencode "session[username_or_email]=ユーザー名" \
--data-urlencode "session[password]=パスワード"  \
-o result.html

認証が通れば、ユーザーのページへアクセスできるようになっていると思います。後続のアクセスはここで作成されたクッキーを使うようにします。

実験その2 GitHub

Twitter へログインがうまくいったので、 GitHub へのアクセスもトライしました。
先に述べているとおり、こちらも API は充実したものが用意されているので、このようなことをやる意味はないと思います。
ついでに、 GitHub は Basic 認証にも対応しているようで、 curl に -u オプションで情報セットして終わりということが可能なようです。

authenticity_tokenを得る

先の例と同じように、ログイン画面・ログインフォームが処理されるページの情報、どのようなパラメータが送られるのかを html を参照して確認しておきます。
その結果、現時点では https://github.com/login のページで authenticity_token を取得, https://github.com/session へログインします。

login のページではいくつか authenticity_token が記述された箇所がありますが、いずれでも同じパラメータとなっていました。

ログインする

ここで以下のようにして curl でアクセスを試みます

curl -X POST https://github.com/session \
-b login.cookie \
-c session.cookie \
-H "Content-type: application/x-www-form-urlencoded" \
--data-urlencode "authenticity_token=得られたauthenticity_token" \
--data-urlencode "login=ユーザー名" \
--data-urlencode "password=パスワード"  \
-o result.html

しかしこれは失敗しました。他にも隠れパラメータがあったようで、 「-d “utf8=✓”」も追加したのですが、まだうまくいきませんでした。
もしかするとまだ見落とししている情報があるのかもしれません。

でも curl でアクセスしたい

目的が違ってきた気がしますが、ここまでくると curl でどうしてもアクセスさせたい気がしてきました。

認証されたクッキー情報を用いさえすれば、ログイン後の情報へアクセスできるという点で以下のように考えてみました。

  1. 他のブラウザでログインさせクッキー情報を取得
  2. curlでそのクッキー情報を利用

というわけで早速 Firefox でログインまで処理させてみました。ログイン処理をさせる際に、開発者ツールを用いてアクセスの詳細を確認できるようにしておきます


ログイン直後にどのようなクッキーが設定されたかを確認します。
直後の送られてきたレスポンスのうち、 Set-Cookie のデータ列を確認します。これが次で必要になるのでコピーしておきます。

このクッキー情報を用いてアクセスします。
このクッキー情報を curl のオプションを別に設定して HTTP ヘッダ情報で埋めてやることにします。コマンドの例は以下のようになります。

curl -c session.cookie \
-H 'Cookie: (ここにブラウザから得たデータ列をセット)' \
GitHub · Build and ship software on a single, collaborative platform
Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build softwa...
-o result.html

先ほどの例と違ってシンプルになりました。

まとめ

curl でログイン済みの情報にアクセスするまでの例をご紹介しました。あまり出番はなさそうですが、従来のログイン方法とは1手変わっているので自分の勉強にはなりました。

最後の1つはブラウザの手を借りていますが、このようにしてセットする方法もあるよという参考になれば幸いです。
こうやって得られた認証済みクッキーを使えば Twitter, GitHub, GitLab, … 色々と API 使わなくてもなんとかなる場面は出てきそうです。

おまけ

これらの結論から、クッキーを使っていくことに注意して、C# の HttpWebRequest を発行していけば、自分のプログラムでこれらの一連の処理が可能です。

C# でのコード例を以下に示します。細かいところは再実装するところがあると思いますが、大まかにはこんな感じで処理ができました。

var cc = new CookieContainer();
var req = WebRequest.CreateHttp("ログインアドレス");
req.CookieContainer = cc;

using (var res = req.GetResponse())
{
  // authenticity_token を取得
}

// POST で送るユーザーログイン情報の構築
string postData;

req = WebRequest.CreateHttp("ログインアドレス2");
var buf = Encoding.UTF8.GetBytes(postData);

req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.CookieContainer = cc; 

using (var rs = req.GetRequestStream())
{
  rs.Write(buf, 0, buf.Length);
}
using (var res = req.GetResponse())
{
  // ログイン後のページへアクセス
}
プログラミング
すらりんをフォローする
すらりん日記
タイトルとURLをコピーしました