はじめに
未経験から実務に飛び込んだ私にとって、開発に携わる工程のうち、全く想像だにしていなかった領域がある。それがWebシステムのセキュリティだ。
オリジナルアプリを作る上では微塵も考慮していなかったセキュリティ面がいざ自身の関わるプロダクトで問題として立ち上がってきた時に、基礎的な深掘りが必要だと痛感した。
今回はセキュリティの基礎をおさらいしつつ、特に重要だと感じたCSRFという攻撃手段と防御について学んだ証となる記事にしたい。
CSRFってなんだっけとなった時にどういった仕組みでどういった脆弱性を狙い、どういった防御策があるのかを振り返ることが目標。
Webシステムのセキュリティの基礎
情報セキュリティとは
セキュリティとは情報の「機密性」・「完全性」・「可用性」を維持することである。
それぞれを見ていこう
-
機密性
アクセスを認められたものだけがその情報にアクセスできる状態。関係ないものに情報を見せないこと。 -
完全性
情報が改竄・消去されていない状態を確保すること -
可用性
必要な時にいつでもアクセスできる状態を保持すること
漠然とセキュリティ対策と言ってもこういった種類があることを念頭に置いて実装を進めるべきだ。
リスク・脅威・脆弱性
これら曖昧に使っていた言葉についても認識を新たにしたい
-
リスク
何らかの損失が発生する可能性 -
脅威
リスクを現実化させる要因 -
脆弱性
脅威に対する弱み
メール送信機能があるWebアプリケーションで例文を作ってみると
「悪意あるユーザーがメール送信APIを大量に実行しようとする脅威に対し、一定時間当たりの送信回数に上限がなく、大量のリクエストを受け付ける状態になっている脆弱性が存在したため、大量のメールが送信され、送信コストの増加やドメインの信頼性低下などの影響が発生するリスクが顕在化した」
と言える。
基本を押さえたところで実際に遭遇・対応した脅威CSRFについて理解を深めたい
CSRFとは
CSRF(Cross-Site Request Forgery)は、ログイン済みのユーザーの権限を利用して、本人が意図していないリクエストをWebアプリケーションに送信させる攻撃のことである。
このようになる。
重要なのは中央の攻撃者が認証情報を盗んでいるわけではないという点だ。
ユーザーのブラウザには認証Cookieが保存されているため、悪意のあるサイトを開いてしまった場合、そこから送られたリクエストにブラウザが自動でCookieを付与してしまい、本人が操作していようがしてまいが、サーバーサイドからは本人による通信に見えてしまう。
CSRFの攻撃の特徴として認証済みであることと本人が操作していることは別なのだ。
改めて先ほどの「脅威・脆弱性・リスク」に当てはめて整理してみよう
- 脅威
攻撃者がログイン中のユーザーに
意図しないリクエストを送信させる - 脆弱性
リクエストが正規の画面から送信されたものか
十分に検証していない - リスク
ユーザーの意図しないデータ変更や
メール送信などが本人の権限で実行される
こう言い換えることができる。
私たちエンジニアはAIとの壁打ちでリスクを洗い出し、脅威を棚卸しし、実際に脆弱性を塞ぐ実装を心がけなければならない。
CSRFが狙う処理
ではCSRF攻撃を防ぐにはまず何が狙われるのかを知っておく必要がある。
ざっと洗い出すと
- メール送信
- パスワード変更
- 登録情報の変更
- 商品の購入
- データの削除
- ユーザーの招待
こういった状態を変更する処理が多く狙われる。
単純にGETリクエストでデータを読み取るよりも、被害者の権限で何かを実行させる。つまり、データ更新・削除・送信でサーバーの状態を変更する処理に攻撃することがメインとなる。
CSRFに対する防御策
CSRF攻撃の特徴・狙われる処理・使われる手口に触れたところで実際の防御について考えたい。
CSRFが成立する大きな理由の一つは、ブラウザが認証に使用するCookieを一定の条件下で自動的にリクエストへ付与することである。
つまり防御を考える際は
- そもそも別サイトからCookieを送らせない
- リクエスト元が信頼できるか確認する
- 別OriginのJavaScriptから自由にAPIを操作させない
- 正規の画面から送られたリクエストであることを検証する
といった複数の観点で守らなければならない。
それぞれの防御策の一例を見てみよう。
1.SameSite Cookie
別サイトからCookieを送らせないためにはこの制御が効果的だ
CookieにはSameSiteという属性があり、クロスサイトからのリクエストにCookieを付与するかをブラウザ側で制限できる
主に3種類
- Strict
最も厳しい設定。別サイトを起点とするリクエストでは、原則としてCookieを送信しない - Lax
Strictより緩い設定。同一サイト内では通常どおりCookieを送り、別サイトからでもトップレベルの通常遷移など一定条件ではCookieを送る。一方、一般的なクロスサイトのPOST、PUT、DELETEなどにはCookieを送らないため、多くのCSRF攻撃を防ぎやすい。 - None
クロスサイトか同一サイトかに関係なくCookieを送信する設定。最も制限が弱い。
実務では安全性と利便性のバランスが取れた、Laxを使う頻度が高いので覚えよう
Laxは通常のリンク遷移は許すが、POSTなどは厳しくする
Set-Cookie: session=abc123; SameSite=Lax
HTTPレスポンスヘッダーに上記の制御をつけて防ごう。
2. Better AuthのTrustedOrigins
リクエスト元が信頼できるか否かはOriginを検証する必要がある
Originとは大まかに
https://example.com:443
のドメインを
scheme → https
host → example.com
port → 443
の組み合わせとして考えるのがOriginである。
これを覚えておくことで、アプリケーション側で許可したOriginだけを信頼することで、想定していないWebサイトから送られたリクエストを拒否できる。
有名なのはBetter AuthというフレームワークのtrustedOriginsという設定で
const auth = betterAuth({
trustedOrigins: [
"https://example.com",
],
})
こうしてあるOriginを「信頼して良い」と明示する。
3. CORS
別OriginのJavaScriptから自由にAPIへアクセスさせないためには、**CORS(Cross-Origin Resource Sharing)**という仕組みが関係する。
実際のWebアプリケーションではフロントエンドとAPIが別Originとなることもある。
そこでAPI側から「このOriginからのJavaScriptによるアクセスなら許可してよい」
とブラウザへ伝える仕組みがCORSである
Honoでは例えば以下のように設定できる。
import { cors } from "hono/cors"
app.use(
"/api/*",
cors({
origin: ["https://app.example.com"],
allowMethods: ["GET", "POST", "OPTIONS"],
credentials: true,
})
)
Honoでは、CORSの設定をMiddlewareとして適用できるので、攻撃者が用意した別OriginからJavaScriptでAPIにアクセスしようとしてもCORSで許可したドメインでなければ、ブラウザでクロスオリジンアクセスを制限するという形で防御することができる。
DELETEなどの特定のリクエストにはOPTIONSメソッドによるPreflight Requestというリクエストを未然に送らない制限もある。
4. CSRF Token
最後に、正規の利用者から送られたリクエストであることを確認する代表的な方法として、CSRF Tokenがある。実際の実務では触れていないが代表するCSRF防御策なので取り上げる
CSRF Tokenとは、サーバーが生成する第三者から推測することが難しいランダムな値である。
例えば正規のWebページを開いた際に、サーバーから次のようなTokenを受け取ったとする。
csrfToken = "a8f3c9..."
そして、データを変更するPOSTやPUT、DELETEなどのリクエストを送信するときに、このTokenも一緒に送信する。
POST /api/profile
X-CSRF-Token: a8f3c9...
サーバー側では、CSRF Tokenは存在するかを尋ね、サーバー側が渡したTokenと一致するかどうかという検証を行う。
CSRFに効果を発揮するのは
「攻撃者は被害者のブラウザにリクエストを送らせることはできても、正しく実装されたCSRF対策では正規のCSRF Tokenを知ることができない」
という点である。
これはCookieがついているからという理由だけではなくCSRF Tokenが付与されて初めてユーザー本人の意図した操作であるという防御を構築することができる。
防御策のまとめ
SameSite
→ クロスサイト通信にCookieを付けるか制御する
trustedOrigins
→ リクエスト元のOriginを信頼できるか検証する
CORS
→ 別OriginのJavaScriptにクロスオリジンアクセスを許可するか制御する
CSRF Token
→ リクエストに正規のTokenが含まれているか検証する
それぞれ似たような防御策を敷きながら、実はそれぞれ守っている層が異なる。
重要なのはCSRF攻撃に対し一つの仕組みだけに頼るのではなく、複数の防衛網を敷く多層防御を実現することだと分かった。
終わりに
セキュリティに関して全く無知だったため、それぞれの用語が指している意味について押さえ、そこから実際に対策をしたCSRFについて深掘りし防御策をまとめてみた。もちろんこれ以外の様々な防御方があるため、自身のアプリケーションの構成に適した防御法を選択することが大事だと感じた。
XSSとSQLインジェクションについては機会があれば次記事で触れ、セキュリティヘッダーの仕組みとともにアウトプットしたい。
参考文献
- 小林恭平・坂本陽 著、佐々木拓郎 監修『イラスト図解式 この一冊で全部わかるWeb技術の基本』SBクリエイティブ
- 平野昌士 著、はせがわようすけ・後藤つぐみ 監修『フロントエンド開発のためのセキュリティ入門 知らなかったでは済まされない脆弱性対策の必須知識』翔泳社
- OWASP — Cross-Site Request Forgery Prevention Cheat Sheet
株式会社シンシア
株式会社xincereでは、実務未経験のエンジニアの方や学生エンジニアインターンを採用し一緒に働いています。
※ シンシアにおける働き方の様子はこちら
シンシアでは、年間100人程度の実務未経験の方が応募し技術面接を受けます。

