Skip to content

Captcha Middleware

CAPTCHA verification middleware package providing session-based captcha challenge and verification.

Overview

This package implements CAPTCHA verification middleware that protects routes by requiring users to complete a CAPTCHA challenge before accessing the protected resource. It supports pluggable providers (currently hCAPTCHA) and uses encrypted sessions for verification state.

Architecture

Captcha Flow

Core Components

Provider Interface

go
type Provider interface {
    // CSP directives for the captcha provider
    CSPDirectives() []string
    // CSP sources for the captcha provider
    CSPSources() []string
    // Verify the captcha response from the request
    Verify(r *http.Request) error
    // Session expiry duration after successful verification
    SessionExpiry() time.Duration
    // Script HTML to include in the page
    ScriptHTML() string
    // Form HTML to render the captcha widget
    FormHTML() string
}

ProviderBase

go
type ProviderBase struct {
    Expiry time.Duration `json:"session_expiry"` // Default: 24 hours
}

func (p *ProviderBase) SessionExpiry() time.Duration

hCAPTCHA Provider

go
type HcaptchaProvider struct {
    ProviderBase
    SiteKey string `json:"site_key" validate:"required"`
    Secret  string `json:"secret" validate:"required"`
}

// CSP Directives: script-src, frame-src, style-src, connect-src
// CSP Sources: https://hcaptcha.com, https://*.hcaptcha.com

Captcha Session

go
type CaptchaSession struct {
    ID    string    `json:"id"`
    Expiry time.Time `json:"expiry"`
}

var CaptchaSessions = jsonstore.Store[*CaptchaSession]("captcha_sessions")

func newCaptchaSession(p Provider) *CaptchaSession
func (s *CaptchaSession) expired() bool

Middleware Integration

go
type hCaptcha struct {
    captcha.HcaptchaProvider
}

func (h *hCaptcha) before(w http.ResponseWriter, r *http.Request) bool {
    return captcha.PreRequest(h, w, r)
}

var HCaptcha = NewMiddleware[hCaptcha]()

PreRequest Handler

go
func PreRequest(p Provider, w http.ResponseWriter, r *http.Request) (proceed bool)

This function:

  1. Checks for valid session cookie
  2. Validates session expiry
  3. Returns true if session is valid
  4. For non-HTML requests, returns 403 Forbidden
  5. For POST requests, verifies the captcha token
  6. For GET requests, renders the CAPTCHA challenge page

Configuration

hCAPTCHA Configuration

yaml
middleware:
  my-captcha:
    use: hcaptcha
    site_key: "YOUR_SITE_KEY"
    secret: "YOUR_SECRET"
    session_expiry: 24h # optional, default 24h

Route Configuration

yaml
routes:
  - host: example.com
    path: /admin
    middlewares:
      - my-captcha

Usage Examples

Basic Setup

go
import "github.com/yusing/godoxy/internal/net/gphttp/middleware"

hcaptchaMiddleware := middleware.HCaptcha.New(middleware.OptionsRaw{
    "site_key": "your-site-key",
    "secret":   "your-secret",
})

Using in Middleware Chain

yaml
# config/middlewares/admin-protection.yml
- use: captcha
  site_key: "${HCAPTCHA_SITE_KEY}"
  secret: "${HCAPTCHA_SECRET}"
  bypass:
    - type: CIDR
      value: 10.0.0.0/8

Session Management

Sessions are stored in a JSON-based store with the following properties:

  • Session ID: 32-byte CRNG (crypto/rand.Read) random hex string
  • Expiry: Configurable duration (default 24 hours)
  • Cookie: godoxy_captcha_session with HttpOnly flag

CSP Integration

The CAPTCHA provider supplies CSP directives that should be added to the response:

go
// hCAPTCHA CSP Directives
CSPDirectives() []string
// Returns: ["script-src", "frame-src", "style-src", "connect-src"]

CSPSources() []string
// Returns: ["https://hcaptcha.com", "https://*.hcaptcha.com"]

HTML Template

The package includes an embedded HTML template (captcha.html) that renders the CAPTCHA challenge page with:

  • Provider script (<script src="https://js.hcaptcha.com/1/api.js">)
  • Provider form (<div class="h-captcha" data-sitekey="...">)
  • Auto-submit callback on successful verification

Security Considerations

  1. Session Cookie: Uses HttpOnly flag to prevent JavaScript access
  2. Token Verification: Tokens are verified server-side with the CAPTCHA provider
  3. Remote IP: Client IP is included in verification request to prevent token reuse
  4. Session Expiry: Sessions expire after configurable duration
  5. Non-HTML Fallback: Non-HTML requests receive 403 without challenge page

Error Handling

go
var ErrCaptchaVerificationFailed = errors.New("captcha verification failed")

// Verification errors are logged with request details
log.Warn().Err(err).Str("url", r.URL.String()).Str("remote_addr", r.RemoteAddr).Msg("failed to verify captcha")

Integration with GoDoxy

The captcha middleware integrates with GoDoxy's:

  • Authentication: Sessions are managed via auth.SetTokenCookie
  • Session Store: Uses jsonstore for persistent session storage
  • Middleware Framework: Implements RequestModifier interface

Released under the MIT License.