ShellScript
Linux
curl
HTTP
Web

要ログインのWebページにコマンドラインからアクセスしてみる - Webログインの簡単な解説 -

はじめに

この記事ではcurlというコマンドをを使用して、LinuxのコマンドラインでWebページから情報を取得する方法を紹介します。

また、ログインが必要なページにおいて、情報を取得したり、送信したりする方法も合わせて紹介したいと想います。

私自身、Webの技術については初心者ですが、この内容を調べていく中で得るものが多かったので、Webの基礎知識を学ぶきっかけになれば幸いです。

環境

この記事では、Flask という Webフレームワークを使用した簡単なWebアプリをローカルで作成し、そのページに対してcurlコマンドを投げていきます。

Welcome | Flask (A Python Microframework)

その他に、ブラウザも使用します。私が使用した環境は以下となります。

  • Python 3.6.2
  • virtualenv (お好みで)
  • Firefox 57
  • curl 7.54.0

サンプルWebアプリの起動と終了

curl のアクセス先となるサンプルのWebアプリをローカルで立ち上げます。ソースはGithubにアップしています(Flask の Tutorial そのまんまです…手抜きですみません)。

eup42/Flaskr: flask tutorial application

virtualenv を使用するとシステムの環境を汚さずにWebアプリを立ち上げられます(無い場合はインストールしてください)。以下のコマンドでWebアプリのインストールと起動が行なえます。

# Install
git clone git@github.com:eup42/Flaskr.git
cd Flaskr
virtualenv .
source ./bin/activate
pip install --editable .

# Run Application
flask initdb
export FLASK_APP=flaskr
flask run

http://localhost:5000/ にアクセスするとこんな画面が表示されるはず。

Screenshot-2018-1-16 Flaskr.png

Username:
admin
Password:
default

でログインできるので、遊んでみてください(記事の投稿みたいなことしかできませんが…)

Webの基本的な仕組みと curl について

準備が一通り終わったところで、Webの基本的な仕組みに触れたいと思います。

私達が普段ブラウザからWebページにアクセスしたときには、HTTPという通信方式でデータのやり取りがなされます。どういう通信方式かというと

  1. クライアントからサーバーにリクエストを送る
  2. サーバーからクライアントにレスポンスを返す

という、非常に単純なものです。ブラウザを使っているとあまり意識はしませんが、curlを使ってみるとこのリクエストレスポンスのやり取りが良く分かります。

以下のコマンドを実行してGoogleにアクセスしてみます。-vは詳細表示をするオプションです。

curl -v www.google.com                                                                        

コマンドを実行すると下のような結果になると思います。

* Rebuilt URL to: www.google.com/                          
*   Trying 216.58.197.4...                                 
* TCP_NODELAY set                                          
* Connected to www.google.com (216.58.197.4) port 80 (#0)  
> GET / HTTP/1.1                                           
> Host: www.google.com                                     
> User-Agent: curl/7.54.0                                  
> Accept: */*                                              
>                                                          
< HTTP/1.1 302 Found                                       
< Cache-Control: private                                   
< Content-Type: text/html; charset=UTF-8                   
< Referrer-Policy: no-referrer                             
< Location: http://www.google.co.jp/?gfe_rd=cr&dcr=0&ei=h2FnWu3KF-fZ8Af9q5qwCQ                                         
< Content-Length: 271                                      
< Date: Tue, 23 Jan 2018 16:23:35 GMT                      
<                                                          
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">                                         
<TITLE>302 Moved</TITLE></HEAD><BODY>                      
<H1>302 Moved</H1>                                         
The document has moved                                     
<A HREF="http://www.google.co.jp/?gfe_rd=cr&amp;dcr=0&amp;ei=h2FnWu3KF-fZ8Af9q5qwCQ">here</A>.                         
</BODY></HTML>                                             
* Connection #0 to host www.google.com left intact

結果のうち、以下の部分がwww.google.comに対するリクエストになります。

> GET / HTTP/1.1                                           
> Host: www.google.com                                     
> User-Agent: curl/7.54.0                                  
> Accept: */*                                              
>       

GETと書いてあるのは、リクエストの種類のことで、他にはPOSTPUTなどがあり、一般にメソッドと呼ばれます。2-4行目はヘッダフィールドと呼ばれます。

次に以下の部分がサーバーからのレスポンスになります。

< HTTP/1.1 302 Found                                       
< Cache-Control: private                                   
< Content-Type: text/html; charset=UTF-8                   
< Referrer-Policy: no-referrer                             
< Location: http://www.google.co.jp/?gfe_rd=cr&dcr=0&ei=h2FnWu3KF-fZ8Af9q5qwCQ                                         
< Content-Length: 271                                      
< Date: Tue, 23 Jan 2018 16:23:35 GMT                      
<                                                          
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">                                         
<TITLE>302 Moved</TITLE></HEAD><BODY>                      
<H1>302 Moved</H1>                                         
The document has moved                                     
<A HREF="http://www.google.co.jp/?gfe_rd=cr&amp;dcr=0&amp;ei=h2FnWu3KF-fZ8Af9q5qwCQ">here</A>.                         
</BODY></HTML>

1行目の数字3桁はステータスコードと呼ばれ、リクエストの結果を示しています。ここの例の304とは、「ページは別のところに移動しちゃってもうありませんよ」を表しています。他のコード例としては、200リクエスト成功404ページが見つかりませんなどがあります。

2-7行目はヘッダフィールドになります。例えば、Location:にはページの移動先が記載されています。

9行目以降が、ボディ部で実際に送られてきたデータになります。この部分がブラウザによって解釈され画面に表示されます。

さて、ここでもう一つオプションを紹介します。ステータスコードで304が帰ってきたときに、Location:に記載されているアドレスに自動でGETリクエストを投げてくれるオプションがあります。

curl -v -L www.google.com

上記を実行すると、結果が変わり、2回リクエストとレスポンスのやり取りがなされていることがわかると思います。

さて、実はブラウザでも同様にHTTP通信のやり取りを見ることができます。Firefoxの場合、右上メニュー > ウェブ開発 > ネットワーク とクリックすると、ブラウザ下に開発ツールが開かれます。

この状態で、www.google.comにアクセスしてみると以下のようになるかと思います(画像は1つのリクエストをクリックした状態)。

スクリーンショット 2018-01-24 02.11.04.png

ここで、左下の表の1行は「1つのリクエストとそれに対するレスポンス」に当たります。それぞれにステータスコードが振られていることが分かります。

たくさんのリクエストが送信されているのは、1つ目のレスポンスの中のHTMLに画像やjavascriptを取得する命令があったため、ブラウザがそれを解釈して次々とリクエストを送信しているためです。

それぞれのリクエストをクリックするとヘッダ情報などの詳細を見ることができます。

curl を使ってWebページにログイン・記事の投稿をしてみる

さて、遅くなりましたがやっと本題に入ります。今までやってきたようにcurlでWebページの情報を取ってくるだけであれば比較的簡単にできます。

では、ログインが必要なページから情報を取ってきたりするにはどうしたら良いのでしょうか。実際に、サンプルのWebアプリを使って、curlからログイン、記事の投稿までやってみたいと思います。

ログインとは何か

実施に開発者ツールを開きながらログインを実行してみましょう。

先程の開発者ツールのネットワークを開いた状態で、Flaskr のログインページ( http://localhost:5000/login )にて、Username, Password を入力して Login ボタンを押してみます。すると以下の画像のようになるかと思います。

スクリーンショット 2018-01-27 21.32.52.png

大事なのはLoginをクリックした直後の通信ですので、一番上の通信を見てみましょう。クリックしてみると詳細が出てきます。

スクリーンショット 2018-01-27 21.37.07.png

  • リクエスト要求URL は http://localhost:5000/login
  • リクエストメソッドは POST
  • レスポンスステータスは 302 (リダイレクト)

となっています。ということは、POST メソッドで、 localhost:5000/login にリクエストを投げれば、ログインできそうです。また、POSTメソッドのときにはパラメータータブを見てみましょう。以下のようになっているはずです。

スクリーンショット 2018-01-27 21.43.11.png

: の左側が name 、右側が value と呼ばれます。今回は

name value
usename admin
password default

というデータがPOSTメソッドで送られていることになります。

curl でログインをしてみる

では、ログインを curl を使ってやってみます。基本的には上で見てきたことをやればOKです。

curl -X POST -d username=admin -d password=default  localhost:5000/login

-X はメソッドを指定するオプション、-d はPOSTメソッドで送信するデータの指定になります(実は-X オプションなくてもcurl-d のオプションを見てPOSTメソッドと判断してくれるので、-X はなくてもok)。

結果は以下のようになるはずです。

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 5000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5000 (#0)
> POST /login HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 31
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 31 out of 31 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 302 FOUND
< Content-Type: text/html; charset=utf-8
< Content-Length: 209
< Location: http://localhost:5000/
< Set-Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIllvdSB3ZXJlIGxvZ2dlZCBpbiJdfV0sImxvZ2dlZF9pbiI6dHJ1ZX0.DU75rA.BuEZtq3Qel26kKYqDiN5LdTNolk; HttpOnly; Path=/
< Server: Werkzeug/0.14.1 Python/3.6.2
< Date: Sun, 28 Jan 2018 06:05:32 GMT
< 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
* Closing connection 0
<p>You should be redirected automatically to target URL: <a href="/">/</a>.  If not click the link.

ログインの成功有無が分かり辛いですが、リダイレクトが発生しているので成功していそうです。パスワードを間違えると再度ログインページが表示されます。これはブラウザで見たときの挙動とも合致しています。

curl でログインした状態を保持する

さて、Flaskr にログインしたあとで、トップページにアクセスすると以下の画像のように投稿画面が表示されます。右上に log in と表示されていたものも log out になっています。

スクリーンショット 2018-01-28 15.08.52.png

curl で先述のログインするコマンドを打った後で、トップページにアクセスしてみるとどうでしょうか。

curl -X POST -d username=admin -d password=default  -v localhost:5000/login && curl -v localhost:5000

結果は...

<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="/static/style.css">
<div class=page>
  <h1>Flaskr</h1>
  <div class=metanav>

    <a href="/login">log in</a>

  </div>



  <ul class=entries>

    <li><em>Unbelievable.  No entries here so far</em>

  </ul>

* Closing connection 0
</div>%

レスポンスで帰ってきた、HTMLを見る限り何も変わっていません。

さて、何故か。実は、HTTP のプロトコルはステートレス・プロトコルと呼ばれるものの一つで、「一つのリクエストとレスポンスの組」が前後の組とは独立しており、サーバー側にログインの状態などが保存されません。

ステートレス・プロトコル - Wikipedia

そこで、セッション という考え方を使って、ログイン状態を管理します。セッションの詳しい説明は他の記事におまかせしたいともいます。

Cookieとセッションをちゃんと理解する - Qiita
今さら聞けないセッションとCookie、ログイン・ログアウト(Rails編) - Qiita

簡単に言うと Cookie という仕組みを使用し、ログイン時にクライアント側にセッションIDを保存し、次のアクセス時に、そのセッションIDをサーバーに渡して、ログインしているかどうかを管理しているのです。

では、curl で Cookie を使ってFlaskrにログインしてみましょう。

# Flaskr にログインし、Cookie をローカルに保存
curl -X POST -d username=admin -d password=default -c cookie -v localhost:5000/login

# 保存したCookie をサーバーに渡してアクセスする
curl -b cookie -v localhost:5000

1つめのコマンドでは、ログインのコマンドに -c <filename> のオプションを追加しました。これは cookie を <filename> で保存するオプションです。

2つめのコマンドは、トップページにアクセスするものに -b <filename> を追加しました。トップページにアクセスする際に、セッションを維持するためcookieの情報を渡します。

上記のコマンドを打つと、以下のようなhtmlが帰ってきます。

<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="/static/style.css">
<div class=page>
  <h1>Flaskr</h1>
  <div class=metanav>

    <a href="/logout">log out</a>

  </div>

    <div class=flash>You were logged in</div>



    <form action="/add" method=post class=add-entry>
      <dl>
        <dt>Title:
        <dd><input type=text size=30 name=title>
        <dt>Text:
        <dd><textarea name=text rows=5 cols=40></textarea>
        <dd><input type=submit value=Share>
      </dl>
    </form>

  <ul class=entries>

    <li><em>Unbelievable.  No entries here so far</em>

  </ul>

* Closing connection 0
</div>

なんかログインできてそうですね!

おまけ: ログインして記事を投稿

ログインはできたところで、この記事の目的は達成です。おまけとして、Flaskr に curl を使って記事を投稿してみます。

コマンドのみの紹介になりますが、Firefoxの開発者メニューで記事投稿の際にどういうデーターが送られているのかを解析した上でコマンド化しています。

# Flaskr にログインし、Cookie をローカルに保存
curl -X POST -d username=admin -d password=default -c cookie -v localhost:5000/login

# 記事を投稿する
curl -X POST -d title=test -d text=test -b cookie -v localhost:5000/add

おわりに

今回、curl から始まりログインの仕組みまで調べてみて、普段ブラウザがやってくれていることを少し理解できたんじゃないかと思いました。いやはや、curl ってすごい...。

最後に、この記事を通してWebの仕組みに関する知識が少しでも深まるきっかけになれば幸いです。