Cleartext transmission of sensitive information

Cleartext transmission of sensitive information

Description

When requests and responses travel over HTTP without TLS protection, network observers can read their contents. An attacker on the network or in an intermediary position may capture login credentials, session cookies, and personal information through packet sniffing or a man-in-the-middle attack.

Potential impact

  • Disclosure of credentials, session tokens, and personal information sent without encryption
  • Account impersonation or session hijacking using stolen passwords or cookies
  • Modification of requests and responses, including payment data or returned content
  • Loss of trust and failure to meet applicable privacy or security requirements, such as PCI-DSS

Remediation

  • If Go terminates external TLS connections directly, use http.ListenAndServeTLS. If a trusted reverse proxy terminates TLS, verify both external HTTPS and the protected path between the proxy and application.
  • Use certificates issued by a trusted CA. In development or internal environments, manage trust for self-signed certificates appropriately.
  • Set Strict-Transport-Security on HTTPS responses. It does not protect the first HTTP request before the browser knows the policy.
  • Use the HTTP port, such as 80, only to direct clients to HTTPS, such as port 443. Redirects cannot protect credentials or cookies already sent over HTTP; sensitive requests must start with HTTPS.
  • Minimize sensitive data transmission and set Secure on sensitive cookies.

Examples

Before

go
package main

import (
    "log"
    "net/http"
)

func loginHandler(w http.ResponseWriter, r *http.Request) {
    // Simplified: real authentication must verify the password
    username := r.FormValue("username")
    password := r.FormValue("password")

    // Plain HTTP can expose username and password in transit
    log.Printf("login attempt: %s / %s", username, password)
    w.Write([]byte("ok"))
}

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

    // Before: serve HTTP without TLS
    // Requests and responses travel in plaintext
    if err := http.ListenAndServe(":80", nil); err != nil {
        log.Fatal(err)
    }
}

After

go
package main

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

func loginHandler(w http.ResponseWriter, r *http.Request) {
    // Real services require additional authentication and validation
    username := r.FormValue("username")
    log.Printf("login attempt for username=%q", username)
    w.Write([]byte("ok"))
}

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

    // Use port 80 only to redirect to 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", // Fixed HTTPS host from deployment configuration
                Path:     r.URL.Path,
                RawQuery: r.URL.RawQuery,
            }).String()
            http.Redirect(w, r, target, http.StatusMovedPermanently)
        })); err != nil {
            log.Fatal(err)
        }
    }()

    // After: TLS with the actual certificate and key file paths
    if err := http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil); err != nil {
        log.Fatal(err)
    }
}

Explanation:

  • Before: http.ListenAndServe serves /login over plain HTTP. Login credentials can be intercepted in transit; this example also improperly logs the password.
  • After: http.ListenAndServeTLS serves HTTPS. The redirect uses a fixed deployment host rather than the request's Host. %q quotes the username so control characters do not break the log record, and the password is not logged.

References