1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クロスサイトリクエストフォージェリ (CSRF) 対策

1
Last updated at Posted at 2025-12-04

クロスサイトリクエストフォージェリ (CSRF) 対策

トークンを用いた防御手法と、Goでのミドルウェア実装。

1. CSRF攻撃の基本
CSRFは、Cross-Site Request Forgeryの略で、ユーザーが認証情報(セッションCookie)を持つ状態で、悪意のある外部サイトからそのユーザーになりすまして意図しないリクエスト(送金、パスワード変更などのCRUD操作)を実行させる攻撃です。

POST/PUT/DELETE には必ず CSRFトークン をつけます。
要は、ご本人のリクエストかどうか?を証明する実装が必須。

Go言語によるCSRF対策の実装
ご提示いただいたGo言語のコードを、機能別に分類し解説します。

2-1. CSRFトークン生成とセッション保存
トークンは予測不能でなければなりません。暗号論的に強力な乱数生成器を使用します。

// 1. 超強い秘密の合言葉(トークン)を作る関数
func generateCSRFToken() string {
    b := make([]byte, 32)           // 32バイト=めっちゃ長いランダムな数字を作る
    rand.Read(b)                    // 完全ランダムに!!
    return base64.StdEncoding.EncodeToString(b)  // それを文字に変換して返す
}
// → 例: "aBcDeFgHiJkLmNoPqRsTuVwXyZ123456" みたいな超長い合言葉ができる!
// ログインしたときにやること
token := generateCSRFToken()        // 秘密の合言葉を作る
session.Values["csrf_token"] = token  // セッション(ユーザーのメモ帳)に覚えさせる
session.Save(r, w)                  // メモ帳を保存!

2-2. HTMLフォームへのトークン埋め込み
通常のフォーム送信と、JavaScript(Ajax)からの送信の両方に対応します。

<!-- 2. HTMLに秘密の合言葉を2箇所にこっそり書く -->
<meta name="csrf-token" content="aBcDeFgHiJkL...">   <!-- JavaScriptが読めるように -->
<form method="POST">
    <input type="hidden" name="csrf_token" value="aBcDeFgHiJkL..."> <!-- 普通のフォーム用 -->
    <button>送信</button>
</form>
<!-- → 悪者のサイトにはこの合言葉が書かれてない! -->

2-3. 門番としてのミドルウェア検証
すべての変更系リクエスト(POST, PUT, DELETEなど)が、このチェックを通過するように設定します。

// 3. ミドルウェア=「門番おじさん」
// 全部のリクエストをここでチェック!
func CSRFMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        
        // GET(見るだけ)は安全だからスルー
        if r.Method == "GET" || r.Method == "HEAD" || r.Method == "OPTIONS" {
            next.ServeHTTP(w, r)
            return
        }

        // 合言葉を持ってきて!
        token := r.FormValue("csrf_token")           // フォームから探す
        if token == "" {
            token = r.Header.Get("X-CSRF-Token")     // なければヘッダーから探す(Ajax用)
        }

        // セッションから正しい合言葉を取り出す
        session, _ := store.Get(r, "session")
        expected := session.Values["csrf_token"]

        // 合言葉が空 or 間違ってる → 即ドア閉める!!
        if token == "" || expected != token {
            http.Error(w, "合言葉が違う!入れないよ!", http.StatusForbidden)
            return
        }

        // 合言葉OK!通してあげる
        next.ServeHTTP(w, r)
    })
}

2-4. SameSite属性による追加防御
SameSite Cookie属性は、ブラウザレベルでクロスサイトリクエスト時にCookieを送信するかどうかを制御します。これはトークンチェックとは別に、最も簡単で強力な防御策の一つです。

// 4. クッキーに「他サイトからは来るな!」の札をつける
http.SetCookie(w, &http.Cookie{
    Name:     "session",
    Value:    sessionID,
    HttpOnly: true,        // JavaScriptから読めない
    Secure:   true,         // HTTPSのみ
    SameSite: http.SameSiteLaxMode,  // ← 悪者のサイトからクッキー送れない!!
})
<!-- AjaxでPOSTするときの書き方(超大事!) -->
<script>
const token = document.querySelector('meta[name="csrf-token"]').content;

fetch("/delete", {
    method: "POST",
    headers: {
        "X-CSRF-Token": token   // ← ここに合言葉を入れる!!
    }
})
</script>

・SameSite: Lax:トップレベルナビゲーション(リンククリックなど)経由のGETリクエスト以外では、クロスサイトのCookie送信をブロックします。多くのCSRF攻撃を防ぎます。

・SameSite: Strict:同じサイト内からのリクエスト以外では、一切Cookieを送信しません。最も安全ですが、ユーザー体験に影響が出る可能性あります。

3. CSRF対策チェックリストとWebセキュリティの基本

対策カテゴリ 項目  目的と効果
CSRFトークン トークンは32byte以上のランダムか? 予測されるのを防ぐ
トークンをセッションに保存し、リクエストと一致比較しているか? 本人からのリクエストであることを証明
フォームとAjaxヘッダーの両方に対応しているか? すべての変更操作経路をカバー
SameSite Cookie セッションCookieにSameSite=LaxまたはStrictを設定しているか? ブラウザにCookieの自動送信を制限させ、広範囲のCSRFをブロック
ミドルウェア POST/PUT/DELETEは全てCSRFMiddlewareを通っているか? 変更操作を伴うリクエストを漏れなく防御

Webセキュリティの基本3大脆弱性まとめ

情弱性 対策  Go言語での対応例
SQLインジェクション (SQLi) プリペアドステートメントの使用  ?プレースホルダによるバインド変数の利用
クロスサイトスクリプティング (XSS) 出力時の適切なエスケープ処理 html/templateパッケージの使用
クロスサイトリクエストフォージェリ (CSRF) SameSite CookieとCSRFトークンの併用 SameSiteLaxModeと上記ミドルウェアの実装
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?