Let's Go Security improvements › Configuring HTTPS settings
Previous · Contents · Next
Chapter 10.3.

Configuring HTTPS settings

Go has good default settings for its HTTPS server, but it’s possible to optimize and customize how the server behaves.

One change, which is almost always a good idea to make, is to restrict the elliptic curves that can potentially be used during the TLS handshake. Go supports a few elliptic curves, but as of Go 1.19 only tls.CurveP256 and tls.X25519 have assembly implementations. The others are very CPU intensive, so omitting them helps ensure that our server will remain performant under heavy loads.

To make this tweak, we can create a tls.Config struct containing our non-default TLS settings, and add it to our http.Server struct before we start the server.

I’ll demonstrate:

File: cmd/web/main.go
package main

import (
    "crypto/tls" // New import
    "database/sql"
    "flag"
    "html/template"
    "log"
    "net/http"
    "os"
    "time"

    "snippetbox.alexedwards.net/internal/models"

    "github.com/alexedwards/scs/mysqlstore"
    "github.com/alexedwards/scs/v2"
    "github.com/go-playground/form/v4"
    _ "github.com/go-sql-driver/mysql"
)

...

func main() {
    ...

    app := &application{
        errorLog:       errorLog,
        infoLog:        infoLog,
        snippets:       &models.SnippetModel{DB: db},
        templateCache:  templateCache,
        formDecoder:    formDecoder,
        sessionManager: sessionManager,
    }

    // Initialize a tls.Config struct to hold the non-default TLS settings we
    // want the server to use. In this case the only thing that we're changing
    // is the curve preferences value, so that only elliptic curves with
    // assembly implementations are used.
    tlsConfig := &tls.Config{
        CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
    }

    // Set the server's TLSConfig field to use the tlsConfig variable we just
    // created.
    srv := &http.Server{
        Addr:      *addr,
        ErrorLog:  errorLog,
        Handler:   app.routes(),
        TLSConfig: tlsConfig,
    }

    infoLog.Printf("Starting server on %s", *addr)
    err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem")
    errorLog.Fatal(err)
}

...

Additional information

TLS versions

TLS versions are also defined as constants in the crypto/tls package, and Go’s HTTPS server supports TLS versions 1.0 to 1.3.

You can configure the minimum and maximum TLS versions via the tls.Config.MinVersion and MaxVersion fields. For instance, if you know that all computers in your user base support TLS 1.2, but not TLS 1.3, then you may wish to use a configuration like so:

tlsConfig := &tls.Config{
    MinVersion: tls.VersionTLS12,
    MaxVersion: tls.VersionTLS12,
}

Restricting cipher suites

The full range of cipher suites that Go supports are defined in the crypto/tls package constants.

For some applications, it may be desirable to limit your HTTPS server to only support some of these cipher suites. For example, you might want to only support cipher suites which use ECDHE (forward secrecy) and not support weak cipher suites that use RC4, 3DES or CBC. You can do this via the tls.Config.CipherSuites field like so:

tlsConfig := &tls.Config{
    CipherSuites: []uint16{
        tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
        tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
        tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    },
}

Go will automatically choose which of these cipher suites is actually used at runtime based on the cipher security, performance, and client/server hardware support.

It’s also important (and interesting) to note that if a TLS 1.3 connection is negotiated, any CipherSuites field in your tls.Config will be ignored. The reason for this is that all the cipher suites that Go supports for TLS 1.3 connections are considered to be safe, so there isn’t much point in providing a mechanism to configure them.

Basically, using tls.Config to set a custom list of supported cipher suites will affect TLS 1.0-1.2 connections only.