Shield

Shield is a security middleware package for Nimbus that protects web applications from common web attacks. It sets secure HTTP headers, provides CSRF protection, and includes a Content Security Policy builder — all in a single, composable package.

Installation

Shield ships with Nimbus under packages/shield. Import it directly:

import "github.com/CodeSyncr/nimbus/packages/shield"

Quick Start

Register Shield in start/kernel.go alongside your other middleware:

package start

import (
    "github.com/CodeSyncr/nimbus"
    "github.com/CodeSyncr/nimbus/middleware"
    "github.com/CodeSyncr/nimbus/packages/shield"
)

func RegisterMiddleware(app *nimbus.App) {
    cfg := shield.DefaultConfig()

    app.Router.Use(
        middleware.Logger(),
        middleware.Recover(),
        shield.Guard(cfg),
        shield.CSRFGuard(cfg.CSRF),
    )
}

Security Headers

shield.Guard(cfg) sets the following headers on every response. Each can be individually configured or disabled.

Header Default Value Purpose
X-Content-Type-Options nosniff Prevents MIME-type sniffing
X-XSS-Protection 0 Disables legacy XSS auditor (use CSP instead)
X-Frame-Options SAMEORIGIN Clickjacking protection
Referrer-Policy strict-origin-when-cross-origin Controls referrer information
X-DNS-Prefetch-Control off Controls browser DNS prefetching
X-Download-Options noopen Prevents old IE from executing downloads
X-Permitted-Cross-Domain-Policies none Restricts Flash/PDF cross-domain access
Cross-Origin-Opener-Policy same-origin Isolates browsing context
Cross-Origin-Resource-Policy same-origin Controls resource loading across origins
Strict-Transport-Security disabled Forces HTTPS (enable for production)

Configuration

Override any default by modifying the Config struct:

cfg := shield.DefaultConfig()

// Clickjacking: deny all framing
cfg.FrameGuard = "DENY"

// Enable HSTS for production
cfg.HSTS.Enabled = true
cfg.HSTS.MaxAge = 365 * 24 * time.Hour
cfg.HSTS.IncludeSubdomains = true
cfg.HSTS.Preload = true

// Disable CSRF for an API-only app
cfg.CSRF.Enabled = false

app.Router.Use(shield.Guard(cfg))

CSRF Protection

Shield uses a signed double-submit cookie pattern. On every request a cryptographically random token is generated, HMAC-signed, and stored in a cookie. Unsafe methods (POST, PUT, PATCH, DELETE) must include the raw token in a header or form field.

How it works

  1. Middleware generates a 256-bit random token and sets it as a signed cookie.
  2. The raw token is stored in the request context.
  3. Templates use {{ .csrfField }} — auto-injected by ctx.View when Shield CSRF is enabled.
  4. On form submit, the token is sent as a hidden field (_csrf) or header (X-CSRF-Token).
  5. Middleware validates the submitted token against the cookie, using constant-time comparison.
  6. Tokens rotate after each successful validation (one-time use).

Template usage

Include {{ .csrfField }} in every form. It renders a hidden input with the token.

<form method="POST" action="/posts">
    {{ .csrfField }}
    <input type="text" name="title">
    <button type="submit">Create</button>
</form>

JavaScript / AJAX usage

If HttpOnly is false, client-side JS can read the cookie and send the token in a custom header:

cfg.CSRF.HttpOnly = false

// In your JavaScript:
const token = document.cookie
    .split('; ')
    .find(c => c.startsWith('__nimbus_csrf='))
    ?.split('=')[1]
    ?.split('.')[0]  // raw token before signature

fetch('/api/posts', {
    method: 'POST',
    headers: { 'X-CSRF-Token': token },
    body: JSON.stringify({ title: 'Hello' })
})

Excepting paths

Skip CSRF validation for API routes or webhooks:

cfg.CSRF.ExceptPaths = []string{
    "/api/",
    "/webhooks/",
}

Custom error handler

cfg.CSRF.ErrorHandler = func(c *http.Context) error {
    return c.JSON(http.StatusForbidden, map[string]string{
        "error":   "csrf_mismatch",
        "message": "Your session has expired. Please refresh the page.",
    })
}

Content Security Policy

Shield includes a fluent CSP builder. Use the presets or build your own policy directive by directive.

Strict preset

cfg.CSP = shield.CSPConfig{
    Enabled: true,
    Policy:  shield.Strict(),
}
// default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; ...

Relaxed preset

cfg.CSP = shield.CSPConfig{
    Enabled: true,
    Policy:  shield.Relaxed(),
}
// Allows CDN assets, inline styles — good for development

Custom policy

cfg.CSP = shield.CSPConfig{
    Enabled: true,
    Policy: shield.NewCSP().
        DefaultSrc("'self'").
        ScriptSrc("'self'", "https://cdn.jsdelivr.net").
        StyleSrc("'self'", "'unsafe-inline'", "https://fonts.googleapis.com").
        FontSrc("'self'", "https://fonts.gstatic.com").
        ImgSrc("'self'", "data:", "https:").
        ConnectSrc("'self'", "wss:").
        ObjectSrc("'none'").
        FrameAncestors("'none'").
        BaseURI("'self'").
        FormAction("'self'"),
}

Nonce-based scripts

policy := shield.NewCSP().DefaultSrc("'self'")
nonce := policy.Nonce("script-src")

// In template: <script nonce="{{ nonce }}">...</script>

Report-only mode

Test a policy without enforcing it:

cfg.CSP = shield.CSPConfig{
    Enabled:    true,
    ReportOnly: true,
    Policy:     shield.Strict().ReportURI("/csp-violations"),
}

Additional Middleware

Origin verification

Validate the Origin / Referer header on unsafe requests:

app.Router.Use(shield.VerifyOrigin("example.com", "www.example.com"))

Remove sensitive headers

app.Router.Use(shield.RemoveHeader("X-Powered-By", "Server"))

Timing-safe responses

Pad response times to a fixed duration to mitigate timing side-channels on sensitive endpoints:

app.Router.Post("/login", loginHandler, shield.NoTimingLeak(500*time.Millisecond))

Plugin Mode

Instead of registering middleware manually, you can use Shield as a Nimbus plugin in bin/server.go. This exposes named middleware "shield" and "csrf" that you can attach to routes or groups.

import "github.com/CodeSyncr/nimbus/packages/shield"

app.Use(shield.NewPlugin(shield.DefaultConfig()))

Full Example

package start

import (
    "time"

    "github.com/CodeSyncr/nimbus"
    "github.com/CodeSyncr/nimbus/middleware"
    "github.com/CodeSyncr/nimbus/packages/shield"
)

func RegisterMiddleware(app *nimbus.App) {
    cfg := shield.DefaultConfig()

    // Production hardening
    cfg.HSTS.Enabled = true
    cfg.HSTS.MaxAge = 365 * 24 * time.Hour
    cfg.HSTS.Preload = true
    cfg.CSRF.Secure = true

    // CSP
    cfg.CSP = shield.CSPConfig{
        Enabled: true,
        Policy: shield.NewCSP().
            DefaultSrc("'self'").
            ScriptSrc("'self'", "https://cdn.jsdelivr.net").
            StyleSrc("'self'", "'unsafe-inline'").
            ImgSrc("'self'", "data:").
            ObjectSrc("'none'").
            FrameAncestors("'none'"),
    }

    // Skip CSRF for API routes
    cfg.CSRF.ExceptPaths = []string{"/api/"}

    app.Router.Use(
        middleware.Logger(),
        middleware.Recover(),
        shield.Guard(cfg),
        shield.CSRFGuard(cfg.CSRF),
        shield.RemoveHeader("X-Powered-By"),
    )
}