機密情報の平文送信

機密情報の平文送信

説明

要求と応答が TLS で保護されていない HTTP を通ると、通信経路上で内容を読み取られる可能性があります。同じネットワーク上や通信の中継地点にいる攻撃者が、パケットの盗聴や中間者攻撃によってログイン情報、セッション Cookie、個人情報などを盗むおそれがあります。

想定される影響

  • 暗号化されずに送信された認証情報、セッショントークン、個人情報の漏えい
  • 盗まれたパスワードや Cookie によるなりすまし、セッションの乗っ取り
  • 決済情報や応答内容など、要求と応答の改ざん
  • 信頼の低下や、PCI-DSS など適用されるプライバシー・セキュリティ要件への不適合

対処方法

  • Go が外部の TLS 接続を直接終端するなら、http.ListenAndServeTLS を使ってください。信頼するリバースプロキシが終端する場合は、外部の HTTPS と、プロキシからアプリケーションまでの経路の保護を確認してください。
  • 信頼する CA の証明書を使ってください。開発・内部環境で自己署名証明書を使う場合も、信頼設定を適切に管理してください。
  • HTTPS 応答に Strict-Transport-Security を設定してください。ブラウザーがポリシーを知る前の最初の HTTP 要求まで保護するものではありません。
  • HTTP ポート(例: 80)は HTTPS(例: 443)への案内だけに使ってください。リダイレクト前に送信された認証情報や Cookie は保護できないため、機密性の高い要求は最初から HTTPS で送ってください。
  • 機密情報の送信を必要最小限にし、機密性の高い Cookie に Secure を設定してください。

例

変更前

go
package main

import (
    "log"
    "net/http"
)

func loginHandler(w http.ResponseWriter, r *http.Request) {
    // 簡略化した例: 実際の認証ではパスワード検証などが必要
    username := r.FormValue("username")
    password := r.FormValue("password")

    // 平文の HTTP では username と password が通信中に露出する可能性
    log.Printf("login attempt: %s / %s", username, password)
    w.Write([]byte("ok"))
}

func main() {
    http.HandleFunc("/login", loginHandler)

    // 変更前: TLS なしで HTTP を提供
    // 要求と応答が平文で送信される
    if err := http.ListenAndServe(":80", nil); err != nil {
        log.Fatal(err)
    }
}

変更後

go
package main

import (
    "log"
    "net/http"
    "net/url"
)

func loginHandler(w http.ResponseWriter, r *http.Request) {
    // 実際のサービスでは別途、認証と検証が必要
    username := r.FormValue("username")
    log.Printf("login attempt for username=%q", username)
    w.Write([]byte("ok"))
}

func main() {
    http.HandleFunc("/login", loginHandler)

    // ポート 80 は HTTPS へのリダイレクトにだけ使用
    go func() {
        if err := http.ListenAndServe(":80", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            target := (&url.URL{
                Scheme:   "https",
                Host:     "www.example.com", // デプロイ設定で固定した HTTPS ホスト
                Path:     r.URL.Path,
                RawQuery: r.URL.RawQuery,
            }).String()
            http.Redirect(w, r, target, http.StatusMovedPermanently)
        })); err != nil {
            log.Fatal(err)
        }
    }()

    // 変更後: 実際の証明書と鍵ファイルのパスで TLS を設定
    if err := http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil); err != nil {
        log.Fatal(err)
    }
}

説明:

  • 変更前: http.ListenAndServe は /login を平文の HTTP で提供します。ログイン情報を通信中に盗まれる可能性があり、この例ではパスワードも不適切にログへ記録しています。
  • 変更後: http.ListenAndServeTLS で HTTPS を提供し、リダイレクト先には要求の Host ではなく設定済みの固定ホストを使います。ユーザー名は %q で引用して制御文字によるログ構造の破壊を防ぎ、パスワードは記録しません。

参考資料