Python

curlでログオンフォーム認証をしてページへアクセス(csrfにも対策)

Python

以前の記事でcurlの使い方について書きましたが、今回はcurlを実践的に使っていきます。

curlでページアクセスしたい場合、単純なアクセスだけであれば以下のようにするだけで簡単にアクセスできます。

curl https://syachiku.net

ただし、実際にはアクセスしたいページにログオン認証がされており、その認証をクリアしないとアクセスできない場合が多くあります。 社内のシステムにおいても基本的には認証が必要とされているのではないでしょうか。

Pythonを使うと比較的簡単に実装することができるのですが、今回は素のLinuxで実現したいのでcurlを使います。セッション管理の機能などがないので少し手間ですが、できないことはないです。

今回はそのようなログオンのフォーム認証をクリアしながらcurlでアクセスする方法についてまとめました。

最近はほぼ間違いなくログオンで実施されているcsrf対策についてもクリアするようにします。

1. curlでログオン認証をクリアするために利用するオプション

はじめにcurlでログオン認証をクリアするために利用するオプションと意味について確認していきます。

もし分からない、詳細をもっと知りたいといった場合には以下の記事でを参照しながら進めてください。

1.1. POSTでのパラメータしてのアクセス(-F)

  • フォームログオンなどでPOSTを利用する場合に使います。
curl -F "user=value1" -F "passwd=value2" http://www.hogehoge.com/logon

1.2. クッキーを指定してアクセスする(-b)

  • cオプションで保存したクッキーファイルを利用してアクセスすることができます。
$ curl -b <cookie file名> http://www.hogehoge.com/

1.3. リダイレクトも追うように(-L)

  • もしアクセスしたページがリダイレクトされた場合に、リダイレクト先のURLを表示します(ログオン認証などでよく使います)
$ curl -L <cookie file名> http://www.hogehoge.com/

1.4. レスポンス関連(-Iと-i)

//レスポンスメッセージのボディのみ
$ curl http://www.hogehoge.com/

// ステータスラインとヘッダのみ(-I)
$ curl -I http://www.hogehoge.com/

//すべてのレスポンスメッセージを表示(-i)
$ curl -i http://www.hogehoge.com/

1.5. 結果をファイルに書き出す(-o)

  • 結果をファイルに書き出します
  • ダウンロードファイルを指定するのを同じオプションです
$ curl http://www.hogehoge.com/ -o <file名>
  • 何も表示させたくない場合(/dev/nulへ送る)
$ curl http://www.hogehoge.com/ -o /dev/null

1.6. 指定した文字列だけを表示する(-w)

  • レスポンスコードだけ取得したい場合には以下の様に利用します
$ curl http://www.hogehoge.com/ -o /dev/null -w  '%{http_code}\n'

1.7. 進捗やエラーを表示しない(-s)

  • シェルスクリプトなどで結果が表示されるのが邪魔な場合に利用します。
$ curl -s http://www.hogehoge.com/

1.8. 詳細をログ出力(-vもしくは–verbose)

$ curl -v http://www.hogehoge.com/

2. 今回のログオン認証をクリアする例

今回はコンテナで構築しているローカルPC内にあるgitlabをサンプルとして使います。

なお、curlを使ってインターネット上にあるサイトにアクセスする際には色々と注意してください。

2.1. ログオン認証をクリアするまでの流れ

まずは、ログオン認証をクリアするまでの流れについて確認します。

ログオン認証をクリアしていない場合には、未認証とみなされるため、全てログオンページにリダイレクトされます(302)

  1. ログオンページにGETでアクセスしてcookieを受け取る
  2. ログオンページ内のcsrf tokenの値を取得する
  3. 1で得たcookieを添えたうえで再度ログオンページアクセスする。さらにユーザ名やパスワード、csrf tokenなどの値を設定してPOSTでアクセスしてログオン認証を実施する。そして再度認証が成功したcookieを受け取る
  4. 3で得たcookieを添えた上でログオン後のページにアクセスする

3. ログオンページ構成を解析する

では、実際にページを解析する方法について説明していきます。

最初にするべきことはログオンページ構成を確認することです。 やり方としてページソースを解析しながら進めることでももちろん問題ありませんが、今回はChromeの開発者ツールで通信の中身を見ながら解析をしてみます。

3.1. フォームで利用するユーザ名とパスワードなどのパラメータを確認する

Ctrl+Shift+iでChrome開発者ツールを起動します。 Gitlabのログオンページを開いてから実際にログオンしてみます。

ログオンに成功した際のパラメータを見てみます。

Networkを選択したらNameにあるsign_inを選択します。

下のほうにあるForm Dataを確認してください。 その中に今回のログオンで利用したパラメータが何なのかが表示されています。 具体的には以下のパラメータです。

  1. authenticity_tokens
  2. user[logon]
  3. user[password]
  4. user[rember_me]

また、Generalの部分を見るとStatus Code302でリダイレクトされていることが分かります。

今回はLdap認証を有効にしていないGitlabで検証していますが、
LDAP認証化したGitlabの際にはパラメータとログオンサイトが異なりますのでご注意ください。
ちなみにLDAP認証化した際には、以下の様になります。 
1. authenticity_tokens
2. username 
3. password 
ログオンURLは`${gitlab_url}/users/auth/ldap/callback`になります。

3.2. csrf対策で送信されているパラメータを確認して取得する

では次にauthenticity_tokensで渡しているパラメータの取得方法についてです。

csrf tokenの値を取得するにはログオンページのソースファイルを見る必要があります。

ソースを開いてcsrfで検索すると以下のような部分があります。この値がcsrf tokenです。 アクセスするタイミングで毎回値は変わります。

<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="Dc/t5Bp8jLKDnKc6g3ldvwcVO1Q7+XpbfQB27uelheTdxqLrRk6Qja4XKT0N/9b1vN3fMjvcCJa/WdwEazsf6g==" />

4. crulでのログオン認証ページにアクセスする

では、パラメータが分かったところで以下の処理を実装していきます。 私の環境は以下の通りです。

gitlab_url="[http://xxxxxx](http://192.168.10.31)"
gitlab_user="root"
gitlab_password="pass"
1. ログオンページにGETでアクセスしてcookieを受け取る
2. ログオンページ内のcsrf tokenの値を取得する
3. 1で得たcookieを添えたうえで再度ログオンページアクセスする。
4. さらにユーザ名やパスワード、csrf tokenなどの値を設定してPOSTでアクセスしてログオン認証を実施する。そして再度cookieを受け取る
5. 3で得たcookieを添えた上でページにアクセスする

4.1. ログオンページにGETでアクセスしてcookieを受け取る

まずは初回のログオンページにアクセスします。この時にcookieファイルを保存します。

curl -c cookie01.txt -s -L -X GET "http://192.168.10.31/users/sign_in"

そうするとcookieファイルが新規で作成されることが確認できます。

4.2. ログオンページ内のcsrf tokenの値を取得する

次にcsrf tokenの値を取得します。 先ほどのソースファイルを確認していてcsrf-tokenの行をgrepを使って抽出します。

curl -c cookie01.txt -s -L -X GET "http://192.168.10.31/users/sign_in" | grep csrf-token
<meta name="csrf-token" content="Y0SkL8y9vlwHXTrzclHhm9boPjAHsFqeny/qOoSRQ68u4GK+EitoGT+XPxso+95nlEPYB0pSFUEKrn1h0KglSw==" />

で、さらにsedでいらない部分を消しちゃいます。

curl -c cookie01.txt -s -L -X GET "http://192.168.10.31/users/sign_in" | grep csrf-token | sed -e  's/.*content\=\"//g'  | sed -e 's/\" \/.*//g'
3Tha+3kMshjgVwLFydJ5lMeyZgg/xJqFQL5FSdWvvCzSiJfd8qSDwTTgdOvhSrbVbSUaPOyOyqELNDcyD/DRVw==

4.3. 先ほどのcookieを添えたうえで再度ログオンページアクセス/ログオン認証

先ほどのcookie01.txtを使って、ログオン認証を試してみます。 また、今回の認証に成功した時に保存されるcookieファイルとしてcookie02.txtを保存するようにします。

curl -b cookie01.txt -c cookie02.txt -s -L -F "user[login]=root" -F "user[password]=P@ssw0rd" -F "user[remember_me]=0" -F "authenticity_token=3Tha+3kMshjgVwLFydJ5lMeyZgg/xJqFQL5FSdWvvCzSiJfd8qSDwTTgdOvhSrbVbSUaPOyOyqELNDcyD/DRVw==" "http://192.168.10.31/users/sign_in"

もし、ログオン認証に成功した場合にはcookie02.txtに以下の行(known_sign_inの部分)が追加されているはずです。 cookie01.txtcookie02.txtのサイズも見比べてみて下さい。

#HttpOnly_192.168.10.31 FALSE   /       FALSE   1627794758      known_sign_in   UnV3MnkveGdReTdIOEs5MXRBY3FPM252bm80cUtIQUFtNCsxMWV4R0ZLQ3ZGY1lyVGdjcjlMbU92WDdQSU1nenFIYjBualdzWER6bTAyQkZTQXVHaTRlV0VWWGNuOHlvcXc1SU1McHNTcGJ5amxjaGNnYUJLaTZOUWhaWEtJdXEtLWlZQ1JqY2xCZjRpdE9QY05LdllFemc9PQ%3D%3D--8f29a2a382a02e3b643428e2b0b5e605857453b8

4.4. 認証cookieを添えた上でページにアクセスする

では最後に認証に成功した際のcookieであるcookie02.txtを添えてページにアクセスすることができます。 こんな感じですね。

# curl -b cookie02.txt -I -X GET "http://192.168.10.31/admin/projects"
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 18 Jul 2021 05:17:13 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Cache-Control: max-age=0, private, must-revalidate, no-store
Etag: W/"f2c2fda319a936ed7f3185ee4b25ed0f"
Pragma: no-cache
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: DENY
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: ggiHdTXmOk
X-Runtime: 0.374683
X-Ua-Compatible: IE=edge
X-Xss-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000
Referrer-Policy: strict-origin-when-cross-origin

ちなみに、cookie02.txtを指定しない場合には以下のように302でサインインページにリダイレクトされる形になります。

[root@gitlab curl_test]# curl  -I -X GET "http://192.168.10.31/admin/projects"
HTTP/1.1 302 Found
Server: nginx
Date: Sun, 18 Jul 2021 05:18:20 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 100
Connection: keep-alive
Cache-Control: no-cache
Location: http://192.168.10.31/users/sign_in
Set-Cookie: experimentation_subject_id=eyJfcmFpbHMiOnsibWVzc2FnZSI6IkltSXpNelprWmpoakxUZGpNRGt0TkRnMVlpMWlNRFZtTFRBME9UUm1ORFV6WVdGbVppST0iLCJleHAiOm51bGwsInB1ciI6ImNvb2tpZS5leHBlcmltZW50YXRpb25fc3ViamVjdF9pZCJ9fQ%3D%3D--b728a632acbe94c7c47d8bc697c02275df6bd599; path=/; expires=Thu, 18 Jul 2041 05:18:20 -0000; HttpOnly
Set-Cookie: _gitlab_session=48e8ca606e7042ff7d40a947b03c5c76; path=/; expires=Sun, 18 Jul 2021 07:18:20 -0000; HttpOnly
X-Request-Id: Fduw65opLu
X-Runtime: 0.018545
Strict-Transport-Security: max-age=31536000
Referrer-Policy: strict-origin-when-cross-origin

5. まとめ

今回はパーツ単位で解析したので、一部手動での作業が発生しましたが、シェルスクリプトの形にすればGitlabページへのアクセスを自動かする事もできます。

curlでログオン認証するのは場合にもよりますがこのやり方に従ってうまくいけば簡単に実現できるかと思います。

Pythonのオススメ勉強方法

私がオススメするPython初心者向けの最初に購入すべき書籍は「シリコンバレー一流プログラマーが教える Pythonプロフェッショナル大全です。

シリコンバレー一流プログラマーが教える Pythonプロフェッショナル大全

この書籍は実際にシリコンバレーの一流エンジニアとして活躍している酒井潤さんが書いた本です。

内容も初心者から上級者までまとめられており、各Lessonも長すぎずに分かりやすくまとめられているので、初心者の方にもおすすめです。

シリコンバレー一流プログラマーが教える Pythonプロフェッショナル大全

今回は以上となります。

コメント