coder/coderd/httpmw/hsts.go

73 lines
2.1 KiB
Go

package httpmw
import (
"fmt"
"net/http"
"strings"
"golang.org/x/xerrors"
)
const (
hstsHeader = "Strict-Transport-Security"
)
type HSTSConfig struct {
// HeaderValue is an empty string if hsts header is disabled.
HeaderValue string
}
func HSTSConfigOptions(maxAge int, options []string) (HSTSConfig, error) {
if maxAge <= 0 {
// No header, so no need to build the header string.
return HSTSConfig{HeaderValue: ""}, nil
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
var str strings.Builder
_, err := str.WriteString(fmt.Sprintf("max-age=%d", maxAge))
if err != nil {
return HSTSConfig{}, xerrors.Errorf("hsts: write max-age: %w", err)
}
for _, option := range options {
switch {
// Only allow valid options and fix any casing mistakes
case strings.EqualFold(option, "includeSubDomains"):
option = "includeSubDomains"
case strings.EqualFold(option, "preload"):
option = "preload"
default:
return HSTSConfig{}, xerrors.Errorf("hsts: invalid option: %q. Must be 'preload' and/or 'includeSubDomains'", option)
}
_, err = str.WriteString("; " + option)
if err != nil {
return HSTSConfig{}, xerrors.Errorf("hsts: write option: %w", err)
}
}
return HSTSConfig{
HeaderValue: str.String(),
}, nil
}
// HSTS will add the strict-transport-security header if enabled. This header
// forces a browser to always use https for the domain after it loads https once.
// Meaning: On first load of product.coder.com, they are redirected to https. On
// all subsequent loads, the client's local browser forces https. This prevents
// man in the middle.
//
// This header only makes sense if the app is using tls.
//
// Full header example:
// Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
func HSTS(next http.Handler, cfg HSTSConfig) http.Handler {
if cfg.HeaderValue == "" {
// No header, so no need to wrap the handler.
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(hstsHeader, cfg.HeaderValue)
next.ServeHTTP(w, r)
})
}