Reusable security middleware for Go web applications.
go-web-security provides a small set of security-focused packages for server-rendered Go applications without coupling them to authentication, sessions, or application-specific business logic.
It is designed to work alongside go-web-core, but can also be used independently.
- Cross-origin request protection using Go's standard library
- Secure HTTP response headers
- Panic recovery middleware
- Configurable trusted origins
- No application-managed CSRF secrets or tokens
- Automated tests for core security behaviour
- Compatible with Go's vulnerability tooling
go get github.com/danieljmanningdev/go-web-security@v0.2.0Adds a small set of defensive HTTP response headers.
handler := headers.Secure(mux)Currently sets:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Example:
mux := http.NewServeMux()
handler := headers.Secure(mux)
if err := http.ListenAndServe(":8080", handler); err != nil {
log.Fatal(err)
}Recovers from panics in downstream HTTP handlers, logs the failure, and returns an HTTP 500 Internal Server Error response.
handler := recovery.Middleware(
logger,
mux,
)Logged request information includes:
- HTTP method
- request path
- recovered panic value
Example:
logger := slog.New(
slog.NewTextHandler(os.Stdout, nil),
)
handler := recovery.Middleware(
logger,
mux,
)Provides a small wrapper around Go's standard-library http.CrossOriginProtection.
The protection rejects unsafe cross-origin browser requests while allowing same-origin requests and safe methods such as GET, HEAD, and OPTIONS.
Unlike the previous implementation, applications do not need to manage:
CSRF secret keys
CSRF cookies
hidden CSRF form fields
request tokens
Basic usage:
handler, err := csrf.Protect(
csrf.Config{},
mux,
)
if err != nil {
log.Fatal(err)
}Additional origins can be explicitly trusted when required:
handler, err := csrf.Protect(
csrf.Config{
TrustedOrigins: []string{
"http://localhost:8080",
},
},
mux,
)
if err != nil {
log.Fatal(err)
}Trusted origins should only contain origins that the application intentionally allows to make unsafe requests.
For example, a local development configuration might use:
csrfConfig := csrf.Config{
TrustedOrigins: []string{
"http://localhost:8080",
"http://127.0.0.1:8080",
},
}Production applications should configure only the origins they actually require.
No special hidden CSRF field is required.
A normal server-rendered form can remain:
<form method="post" action="/account">
<button type="submit">Save</button>
</form>Cross-origin protection is applied by middleware before unsafe requests reach the application handler.
A minimal application using all three packages could look like:
package main
import (
"log"
"log/slog"
"net/http"
"os"
"github.com/danieljmanningdev/go-web-security/csrf"
"github.com/danieljmanningdev/go-web-security/headers"
"github.com/danieljmanningdev/go-web-security/recovery"
)
func main() {
logger := slog.New(
slog.NewTextHandler(os.Stdout, nil),
)
mux := http.NewServeMux()
mux.HandleFunc("/", func(
w http.ResponseWriter,
r *http.Request,
) {
_, _ = w.Write([]byte("hello"))
})
var handler http.Handler = mux
csrfHandler, err := csrf.Protect(
csrf.Config{},
handler,
)
if err != nil {
log.Fatal(err)
}
handler = csrfHandler
handler = headers.Secure(handler)
handler = recovery.Middleware(
logger,
handler,
)
if err := http.ListenAndServe(
":8080",
handler,
); err != nil {
log.Fatal(err)
}
}Middleware wraps from the outside in.
For example:
var handler http.Handler = mux
csrfHandler, err := csrf.Protect(
csrf.Config{},
handler,
)
if err != nil {
log.Fatal(err)
}
handler = csrfHandler
handler = headers.Secure(handler)
handler = recovery.Middleware(
logger,
handler,
)The final handler is passed to the HTTP server.
The exact middleware stack can vary depending on the application.
This project is deliberately small.
It provides reusable security primitives without trying to become a complete authentication or security framework.
Authentication, users, sessions, password management, permissions, application-specific authorization, and business rules should live in separate packages or applications.
Where suitable security functionality exists in Go's standard library, this project prefers building on that rather than maintaining unnecessary custom security implementations.
The goal is simple:
Give new Go web projects a small, tested security baseline without rebuilding the same middleware each time.
Format the code:
gofmt -w .Run the tests:
go test ./...Run static analysis:
go vet ./...Check for known vulnerabilities:
govulncheck ./...Check whitespace errors:
git diff --checkA clean release should pass all four checks.
The project is currently in early development.
Until the API stabilises, releases should be considered pre-v1.0.0 and may contain breaking changes.
Current release:
v0.2.0
See LICENSE.