Let's Go Server and security improvements › Configuring HTTPS settings
Previous · Contents · Next
Chapter 9.5.

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.23 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/slog"
    "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{
        logger:         logger,
        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,
        Handler:   app.routes(),
        ErrorLog:  slog.NewLogLogger(logger.Handler(), slog.LevelError),
        TLSConfig: tlsConfig,
    }

    logger.Info("starting server", "addr", srv.Addr)

    err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem")
    logger.Error(err.Error())
    os.Exit(1)
}

...

Additional information

TLS versions

By default, Go’s HTTPS server is configured to support TLS 1.2 and 1.3. You can customize this and change the minimum and maximum TLS versions using the tls.Config.MinVersion and MaxVersion fields and TLS versions constants in the crypto/tls package.

For example, if you want the server to support TLS versions 1.0 to 1.2 only, you can use a configuration like so:

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

Cipher suites

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

However some of these cipher suites (specifically, the cipher suites that do not support Perfect Forward Secrecy, or which use RC4, 3DES or CBC_SHA256) are considered weak and are not used by Go’s HTTPS server by default. As of Go 1.23, the cipher suites that Go’s HTTPS server uses by default are:

TLS_AES_128_GCM_SHA256                          // TLS 1.3 connections only
TLS_AES_256_GCM_SHA384                          // TLS 1.3 connections only
TLS_CHACHA20_POLY1305_SHA256                    // TLS 1.3 connections only

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256         // TLS 1.2 connections only
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384         // TLS 1.2 connections only
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256           // TLS 1.2 connections only
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384           // TLS 1.2 connections only
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256     // TLS 1.2 connections only
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256   // TLS 1.2 connections only

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA            // TLS 1.0, 1.1 and 1.2 connections
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA            // TLS 1.0, 1.1 and 1.2 connections
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA              // TLS 1.0, 1.1 and 1.2 connections
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA              // TLS 1.0, 1.1 and 1.2 connections

If you want to change the cipher suites used by Go’s HTTPS server — either by including weak cipher suites that aren’t used by default, or removing certain ciphers which are used by default — you can do this via the tls.Config.CipherSuites field.

For example, if you want to use the default cipher list but with the CBC ciphers omitted, you can configure your tls.Config like this:

tlsConfig := &tls.Config{
    CipherSuites: []uint16{
        tls.TLS_AES_128_GCM_SHA256,
        tls.TLS_AES_256_GCM_SHA384,
        tls.TLS_CHACHA20_POLY1305_SHA256,
        tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
        tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
        tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_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, the tls.Config.CipherSuites 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.CipherSuites to set a custom list of supported cipher suites will affect TLS 1.0-1.2 connections only. So, in the above example, it isn’t actually necessary to include the TLS 1.3 specific ciphers and it can be simplified to this:

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