
GETとPOSTとは?Railsのroutes.rbからHTTPメソッドを理解する【60歳からのホワイトハッカー #7】
「60歳から始めるホワイトハッカーへの道」第7回です。
前回は、自分で作ったRailsブログを使ってHTTPとHTTPSの違いを調べました。
今回は、Webを勉強していると必ず出てくる
GET・POST・DELETEなどの「HTTPメソッド」
について勉強してみます。
私はRailsを使い始めて約5年になりますが、
「GET」「POST」という言葉は何度も見てきました。
でも改めて、
「実際にブラウザからRailsまで、どうやって処理されているの?」
と聞かれると、ちゃんと説明できるか少し怪しい(笑)。
そこで今回も、自分で作ったRailsブログ
「rails-fumiblog」を教材にして実際に調べてみました。
■ まずRailsのroutesを見てみる
Railsには、URLとControllerを結び付けるルーティングがあります。
ターミナルから次のコマンドを実行しました。
bin/rails routes | grep -E "GET|POST|PATCH|PUT|DELETE" | head -50すると、私のブログのログイン処理には次のようなルートがありました。
GET /users/sign_inPOST /users/sign_in
DELETE /users/sign_out
ここで最初の発見です。
同じ /users/sign_in なのにGETとPOSTがあります。
URLだけを見れば同じですが、RailsではHTTPメソッドによって処理先が変わります。
GET /users/sign_in↓
Users::SessionsController#new
POST /users/sign_in
↓
Users::SessionsController#create
つまりRailsでは、
「URLだけ」ではなく「HTTPメソッド+URL」
で処理が決まっているわけです。
■ GETをcurlで送ってみる
まずはGETです。
ターミナルから、自分の本番ブログへcurlでアクセスしてみました。
curl -v https://www.rails-fumiblog.com/users/sign_in -o /dev/null結果は、
> GET /users/sign_in HTTP/2
< HTTP/2 200
ちゃんとGETが送られ、HTTPステータス
200 OK が返ってきました。
Rails側では概念的に、
GET /users/sign_in↓
routes.rb
↓
Users::SessionsController#new
↓
ログイン画面
↓
200 OK
という流れになります。
普段ブラウザで何気なくログイン画面を開いていますが、
裏ではこんな通信が行われているわけですね。
■ 今度はPOSTしてみる
では同じURLへPOSTを送ったらどうなるのでしょうか?
curl -v -X POST https://www.rails-fumiblog.com/users/sign_in -o /dev/nullすると結果は、
> POST /users/sign_in HTTP/2
< HTTP/2 422
あれ?
422?
GETでは200だったのに、POSTにしただけで422になりました。
ここで「失敗した」で終わらせず、
Herokuのログを見て原因を調べてみます。
■ Herokuのログを調べる
Herokuのログを表示します。
heroku logs --tailその状態でもう一度POSTすると、Rails側には次の内容が記録されました。
Started POST "/users/sign_in"
Processing by Users::SessionsController#create as */*
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Content
ActionController::InvalidAuthenticityToken
(Can't verify CSRF token authenticity.)
原因が分かりました。
CSRFトークンを確認できなかったため、RailsがPOSTを拒否していたのです。
ここで注目したのは、
Processing by Users::SessionsController#createという部分です。
POST自体がRailsに届かなかったわけではありません。
POSTとして受信され、
ルーティングによってちゃんと
SessionsController#create
まで進んでいます。
その後のセキュリティチェックで止められたわけです。
■ CSRFって何だろう?
ここで初めて、
CSRF
という言葉を本格的に勉強することになりました。
CSRFは、
Cross-Site Request Forgery
の略です。
例えば私がrails-fumiblogへログインしたまま、
別の悪意のあるWebサイトを開いたとします。
そのサイトから、私の意思とは関係なく
rails-fumiblogへ何らかの変更リクエストを送られたら困ります。
自分│
├─ rails-fumiblogにログイン中
│
└─ 悪意のあるサイトを開く
│
│ 勝手にリクエスト
↓
rails-fumiblog
Railsには、こうした攻撃への対策があります。
今回curlから直接POSTしたときには、
Railsが正規のリクエストか確認するために必要なCSRFトークンがありませんでした。
そのため、
Can't verify CSRF token authenticity.となり、422で処理が止められました。
エラーが出たのですが、
今回はむしろRailsのセキュリティ機能が働いているところを実際に見ることができた
わけです。
■ 自分で作った「いいね機能」も調べてみた
ここまで調べて、
「POSTとDELETEをもっと分かりやすく確認できるものはないかな?」
と考えました。
ありました。
自分のブログに作った「いいね」機能です(笑)。
ルーティングを見ると、同じURLにPOSTとDELETEがあります。
POST /users/articles/:article_id/like
DELETE /users/articles/:article_id/like
Controllerでは、
POST↓
Users::LikesController#create
↓
いいねを作る
DELETE
↓
Users::LikesController#destroy
↓
いいねを削除
となっています。
同じURLでも、
POSTなら「作る」、DELETEなら「消す」
という違う処理になります。
これで「HTTPメソッド+URLで処理が決まる」という意味が、
かなり分かってきました。
■ JavaScriptではPOSTとDELETEを切り替えていた
さらに自分のコードを調べてみました。
「いいねボタン」のJavaScriptには、こんな処理を書いていました。
const method = liked ? "DELETE" : "POST";いいね済みならDELETE。
まだいいねしていなければPOST。
さらにfetchを使ってRailsへ通信しています。
const response = await fetch(`/users/articles/${articleId}/like`,
{
method: method,
headers: {
"X-CSRF-Token":
document.querySelector(
'meta[name="csrf-token"]'
).content,
"Accept": "application/json",
"Content-Type": "application/json"
},
credentials: "same-origin"
}
);
ここを見て驚きました。
さっきcurlで問題になったCSRFトークンが、ここにちゃんとある!
"X-CSRF-Token":document.querySelector(
'meta[name="csrf-token"]'
).content
普段自分で使っている「いいねボタン」では、
JavaScriptがページにあるCSRFトークンを取得して、
HTTPヘッダーに付けてRailsへ送っていました。
自分で書いたコードなのに、
HTTPの勉強をしてから改めて見ると意味が違って見えます(笑)。
■ 「いいね」を押してから何が起きている?
ここまで分かったことを一つにつなげてみます。
🤍 いいねをクリック↓
JavaScript
↓
まだ「いいね」していない
↓
POSTを選択
↓
CSRFトークンを付ける
↓
POST /users/articles/123/like
↓
routes.rb
↓
Users::LikesController#create
↓
いいねを保存
↓
JSONを返す
↓
JavaScript
↓
💖 いいね解除
そして、もう一度押すと、
💖 いいね解除↓
JavaScript
↓
DELETEを選択
↓
DELETE /users/articles/123/like
↓
Users::LikesController#destroy
↓
いいねを削除
↓
JSONを返す
↓
🤍 いいね
となっています。
ブラウザではハートマークを押しているだけですが、
裏ではこれだけの処理が動いていたんですね。
■ JSONも登場した
LikesControllerでは、処理後にHTMLページ全体を返しているわけではありません。
例えば「いいね」した後は、
{"liked": true,
"likes": 8
}
のようなJSONを返します。
JavaScript側では、
const data = await response.json();として受け取り、
button.textContent = data.liked? "💖 いいね解除"
: "🤍 いいね";
のように画面を書き換えています。
これまで何となく使っていた
HTTP・JavaScript・Rails・JSON
が、少しずつ一本につながってきました。
■ 今回分かったこと
今回、自分のRailsブログを使って実際に確認したことで、
GETやPOSTが単なる用語ではなくなりました。
- GETはページなどの情報を取得するときに使われる
- POSTはデータを送信して処理を行うときに使われる
- DELETEはデータを削除する処理に使われる
- Railsは「HTTPメソッド+URL」で処理先を決める
- POSTしただけでは必ず成功するわけではない
- RailsにはCSRF攻撃への対策がある
- 正規のJavaScriptではCSRFトークンを付けて通信していた
- RailsからJSONを返してJavaScriptで画面を書き換えることもできる
■ エラーからセキュリティの勉強になった
今回一番面白かったのは、
POSTを送ったら422になったことです。
最初は、
「あれ?失敗した?」
と思いました。
しかしHerokuのログを調べてみると、
RailsがCSRFトークンを確認できず、
リクエストを拒否していることが分かりました。
つまり、
GETとPOSTを勉強する↓
curlで実験する
↓
422になる
↓
Herokuログを見る
↓
CSRFを知る
↓
Railsのセキュリティ対策を知る
という流れになりました。
ホワイトハッカーを目指して勉強を始めてみると、
エラーも立派な教材になるんですね。
そして何より面白いのは、
自分で何年も作ってきたRailsブログが、
そのままセキュリティの教材になっていることです。
60歳からでも、一つずつ実際に試していけば理解できる。
人生、なかなか面白いものです(笑)。
■ 次回へ
今回はGET・POST・DELETEを調べていたところから、
CSRFやJSONまでたどり着きました。
まだまだ知らないことばかりです。
次回も、自分のRailsブログを実験台にしながら、
Webとセキュリティの仕組みを一つずつ調べていこうと思います。
「60歳から始めるホワイトハッカーへの道」まだまだ続きます。
※この記事は、私自身がホワイトハッカーを目指して勉強している過程を記録したものです。
不正アクセス等を目的としたものではありません。