HIGH replay attackecho gohmac signatures

Replay Attack in Echo Go with Hmac Signatures

Replay Attack in Echo Go with Hmac Signatures — how this specific combination creates or exposes the vulnerability

A replay attack in the Echo Go ecosystem with Hmac Signatures occurs when an attacker intercepts a valid request—typically including a signature, timestamp, and nonce—and retransmits it to the API to perform an action without proper authorization. In Echo Go, this often arises when endpoints authenticate requests solely by verifying the Hmac signature without enforcing strict one-time or freshness guarantees for the nonce/timestamp pair.

Hmac Signatures are commonly computed over a canonical string that includes a secret key, timestamp, nonce, and HTTP method/path/body. If the server only validates that the signature matches and does not track or reject previously seen nonces or expired timestamps, an attacker can capture a legitimate request and replay it later. For example, consider a payment initiation endpoint that accepts POST /transfer with headers X-Timestamp, X-Nonce, and X-Signature. An attacker who records this request can replay it within the timestamp’s validity window, causing duplicate transfers because the server does not link the request to a session or idempotency key.

This combination is vulnerable when the following conditions align: the timestamp window is generous, nonces are not stored or checked for duplication, and the server does not enforce idempotency at the application layer. Echo Go handlers that parse and verify the Hmac signature but skip nonce/timestamp deduplication checks effectively allow replay. Additionally, if the signature is computed over a subset of headers or body fields, an attacker might replay the request with modified non-sensitive parameters while keeping the signature valid, further exposing the system.

From a testing perspective, middleBrick scans such endpoints by observing whether replayed requests with identical nonce/timestamp combinations are accepted, and by checking whether the API’s authentication layer includes replay protections. Findings highlight risks where Hmac Signatures are present but insufficiently coupled with replay-countermeasures, mapping to OWASP API Top 10:2023 A07:2021 (Identification and Authentication Failures) and A05:2021 (Broken Access Control).

Note that middleBrick detects and reports these patterns without executing destructive replays, instead analyzing whether the API surface allows signature reuse and providing remediation guidance to harden the implementation.

Hmac Signatures-Specific Remediation in Echo Go — concrete code fixes

Remediation centers on ensuring each Hmac-signed request is unique and time-bound, and that the server tracks and rejects duplicates. Below are concrete patterns for Echo Go handlers that mitigate replay risks while preserving Hmac-based authentication.

1) Enforce strict timestamp windows and nonce deduplication. Validate that X-Timestamp is within a narrow window (for example, ±5 minutes) and store recently seen nonces in a fast, bounded cache (e.g., a sync.Map or a Redis set with TTL). Reject requests with a 401 if the nonce was already used within the chosen time-to-live.

// Example: Echo Go handler with replay protection
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
	"net/http"
	"strconv"
	"sync"
	"time"

	echo "github.com/labstack/echo/v4"
)

var (
	nonceStore = struct {
		sync.RWMutex
		seen map[string]bool
	}{seen: make(map[string]bool)}
	// 5-minute window for replay protection
	timestampSkew = 5 * time.Minute
)

func verifyHmac(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		timestampStr := c.Request().Header.Get("X-Timestamp")
		rawNonce := c.Request().Header.Get("X-Nonce")
		signature := c.Request().Header.Get("X-Signature")
		if timestampStr == "" || rawNonce == "" || signature == "" {
			return echo.NewHTTPError(http.StatusUnauthorized, "missing auth headers")
		}
		timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, "invalid timestamp")
		}
		reqTime := time.Unix(timestamp, 0)
		if time.Since(reqTime) > timestampSkew || time.Since(reqTime) < -timestampSkew {
			return echo.NewHTTPError(http.StatusUnauthorized, "timestamp expired")
		}
		// Check nonce replay
		nonceStore.RLock()
		if nonceStore.seen[rawNonce] {
			nonceStore.RUnlock()
			return echo.NewHTTPError(http.StatusUnauthorized, "replay detected")
		}
		// Note: in production, persist nonces with TTL aligned to timestamp window
		defer func() {
			defer nonceStore.Unlock()
			nonceStore.Lock()
			nonceStore.seen[rawNonce] = true
		}()
		nonceStore.Unlock()

		// Compute expected Hmac
		expectedSig := computeHmac(rawNonce, timestampStr, c.Request().Method, c.Request().URL.Path, c.Request().Body)
		if !hmac.Equal([]byte(signature), []byte(expectedSig)) {
			return echo.NewHTTPError(http.StatusUnauthorized, "invalid signature")
		}
		return next(c)
	}
}

func computeHmac(nonce, timestamp, method, path string, body interface{}) string {
	key := []byte("your-secret-key") // use env/secret manager in production
	mac := hmac.New(sha256.New, key)
	// Canonical representation: timestamp+nonce+method+path+body
	mac.Write([]byte(timestamp + nonce + method + path))
	// If body is JSON, ensure deterministic marshaling; here we simplify
	return fmt.Sprintf("%x", mac.Sum(nil))
}

2) Include a per-request unique element and idempotency. For state-changing operations, require an Idempotency-Key header and store processed keys with their outcome. This prevents duplicate side-effects even if a replay succeeds in signature verification.

// Idempotency-aware wrapper
var idempotencyStore = struct {
	sync.RWMutex
	results map[string][]byte
}{results: make(map[string][]byte)}

func idempotent(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		key := c.Request().Header.Get("Idempotency-Key")
		if key == "" {
			return echo.NewHTTPError(http.StatusBadRequest, "idempotency key required")
		}
		idempotencyStore.RLock()
		if resp, exists := idempotencyStore.results[key]; exists {
			idempotencyStore.RUnlock()
			c.Response().WriteHeader(http.StatusOK)
			c.Response().Write(resp)
			return nil
		}
		idempotencyStore.RUnlock()

		rec := middleware.NewResponseRecorder()
		err := next(c.SetResponseRecorder(rec))
		if err != nil {
			return err
		}
		idempotencyStore.Lock()
		idempotencyStore.results[key] = rec.Body
		idempotencyStore.Unlock()
		for k, v := range rec.HeaderMap() {
			c.Response().Header().Set(k, v[0])
		}
		c.Response().WriteHeader(rec.Code)
		c.Response().Write(rec.Body)
		return nil
	}
}

3) Ensure the Hmac computation includes all relevant request dimensions (method, path, canonical body) to prevent attackers from altering the request body without invalidating the signature. Avoid including variable fields that an attacker could control without invalidating the signature.

By combining short timestamp windows, strict nonce deduplication, and idempotency keys, the Echo Go API neutralizes replay risks even when Hmac Signatures are used for authentication. These patterns align with OWASP recommendations and help maintain the integrity of authenticated interactions.

Frequently Asked Questions

How does middleBrick detect replay vulnerabilities in Hmac-signed Echo Go APIs?
middleBrick runs black-box checks that observe whether identical nonce/timestamp combinations are accepted, and whether the authentication layer enforces freshness and deduplication. It reports findings when replay protections are missing.
Does middleBrick perform active replay attacks against my API?
No. middleBrick detects and reports replay risks without executing destructive replays or modifying data. It provides findings with remediation guidance only.