Skip to content
shunsei.dev
Go back

GETメソッドに潜む3つの危険性

Edit page

はじめに

RISSの資格取得に向けて「体系的に学ぶ安全なWebアプリケーションの作り方第2版(以下、徳丸本)」を読んでいます。

その中で、GETメソッドには代表的な3つの危険性があることが分かりました。

それらについて、基本的な事象をはじめ、C#やGoといったプログラム単位での危険性について、まとめました。

GETメソッドに潜む3つの危険性

まず、同著で取り上げている危険性は以下の3つです。

  1. URL上に指定されたパラメータがRefere経由で外部に漏れる

  2. URL上に指定されたパラメータがアクセスログに残る

  3. URL上のパラメータがブラウザのアドレスバーに表示され、他人に覗かれる

実装レベルでの対応策は「重要な情報はURLに入れず、ボディに入れる」が最適と思われます。

これについて、C#とGoに分けて、BadパターンとGoodパターンの観点でまとめます。

実装レベルにおけるGETメソッドの危険性

■ 実装シナリオ:パスワード再設定

ユーザーにメールでリンクを送り、クリックするとパスワード再設定画面が開く機能を例に挙げてみます。

ここでは 「認証用トークン」 をどのようにサーバーに渡すかがキモになります。

❌ Badパターン:GETメソッド(パラメータ渡し)

URL: `https://example.com/reset-password?token=SECRET_12345`

この状態でユーザーが画面を開き、もしそのページ内に外部サイト(例: 広告や解析ツール)へのリンクや画像埋め込みがあったと仮定します。

ここでは、以下の2つが脆弱性の要因と考えられます。

  1. Referer漏洩:外部サイトへのリクエストヘッダー Referer?token=SECRET_12345 が丸ごと乗って送信される。外部サイトの管理者はそのトークンを使ってなりすましが可能になる。
  2. ログ: AWS ALBやIISのアクセスログに GET /reset-password?token=SECRET_12345 と残り、インフラ担当者全員にトークンがバレる。

✅ Good Pattern:POSTメソッド(ボディ渡し)

画面表示はGETで行いますが、重要な処理は フォーム送信(POST) で行います。

C#の場合

❌ 危険な実装:GETで秘密情報を受け取る

// [GET] /api/login?password=MyPassword123
[HttpGet("login")]
public IActionResult Login([FromQuery] string password)
{
    // ログに "GET /api/login?password=MyPassword123" と残る
    // ブラウザ履歴にもパスワードが残る
    // 外部サイトへ遷移すると Referer でバレる
    var user = _authService.Authenticate(password);
    return Ok(user);
}

✅ 安全な実装:POSTでBodyで受け取る

public class LoginRequest 
{
    public string Password { get; set; }
}

// [POST] /api/login
// ボディ: { "password": "MyPassword123" }
[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
    // ログには "POST /api/login" としか残らない(ボディは記録されない)
    // URLにも出ない
    var user = _authService.Authenticate(request.Password);
    return Ok(user);
}

Goの場合

❌ 危険な実装

// GET /api/[email protected]&token=SECRET
func UpdateEmail(c *gin.Context) {
    token := c.Query("token") // URLパラメータから取得
    // 危険!
}

✅ 安全な実装

type UpdateEmailRequest struct {
    NewEmail string `json:"new_email"`
    Token    string `json:"token"`
}

// POST /api/update_email
func UpdateEmail(c *gin.Context) {
    var req UpdateEmailRequest
    // JSONボディからバインドする
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "Invalid request"})
        return
    }
    // 安全に処理
}

どうしてもGETが必要な場合の「保険」(Referrer-Policy)

検索キーワードなど、仕様上どうしてもGETパラメータを使いたい(URLをシェアさせたい)場合があります。

その場合、「Refererによる漏洩」だけはHTTPヘッダーで防ぐことができます。

Referrer-Policy ヘッダー

レスポンスヘッダーに以下を設定します。

Referrer-Policy: strict-origin-when-cross-origin

この対応により、以下の効果が得られます。

ASP.NET Coreなら Program.cs や Middleware で設定しますし、GoならGinのミドルウェアで設定します。

まとめ

普段、何気なくGETしている処理も「本当にGETメソッドでいいか?」と考えるきっかけになりました。

これに限らず、なぜその処理を行うのか?という実装のその先にある目的を明確にすることで、ただのコードから意味のあるコードになると感じました。


Edit page
Share this post on:

Previous Post
「M4 MacBook AirのDocker環境は「OrbStack」一択だった話
Next Post
「映画選びで悩みたくない」一心で、MBTI × 感情分析レコメンドアプリを開発した話