Information Security 8 min read

Preventing API Parameter Tampering and Replay Attacks with Signature Verification in Java

The article explains how exposed API endpoints can be intercepted and altered, and presents practical security measures—including HTTPS, encrypted parameters, timestamp‑based signatures, and a Spring Boot filter implementation—to detect and block tampering and replay attacks in a Java backend.

Architect
Architect
Architect
Preventing API Parameter Tampering and Replay Attacks with Signature Verification in Java

When an API is exposed to the public network for third‑party service calls, attackers can capture the request, modify parameters, and resend it, leading to data theft or server attacks.

To mitigate these risks, several security mechanisms are recommended: use HTTPS to encrypt traffic, apply parameter encryption and request‑time limits, and employ signature verification based on a shared secret key.

Preventing Parameter Tampering

Transmit data over https to ensure encryption and integrity.

Implement backend validation such as parameter encryption and request timestamp checks; the article uses this approach as a case study.

Signature Verification Process

The frontend encrypts request parameters with a pre‑agreed secret key, generates a signature (sign1), and places it in the request headers.

The server receives the request, re‑signs the parameters in a filter using the same secret key to obtain sign2.

If sign1 equals sign2, the request is considered legitimate; otherwise, it is rejected as tampered.

Preventing Replay (Re‑posting) Attacks

Include a timestamp header in each request and sign it together with the parameters.

The server checks that the timestamp is within an acceptable window (e.g., 60 seconds); if it exceeds the limit, the signature is considered expired.

Altering the timestamp without the secret key invalidates the signature, preventing replay.

Other optional solutions include using a unique database primary key with optimistic locking or a anti‑replay token (UUID) stored in a hidden form field, and disabling the submit button with JS throttling.

Core Filter Implementation (Java)

@Component
@Slf4j
public class SignAuthFilter implements Filter {

    @Autowired
    private SignAuthProperties signAuthProperties;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Initializing SignAuthFilter...");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // Skip URIs that do not require signing
        String requestUri = httpRequest.getRequestURI();
        for (String ignoreUri : signAuthProperties.getIgnoreUri()) {
            if (requestUri.contains(ignoreUri)) {
                log.info("URI " + requestUri + " does not require signature verification.");
                chain.doFilter(request, response);
                return;
            }
        }
        // Retrieve signature and timestamp
        String sign = httpRequest.getHeader("Sign");
        String timestampStr = httpRequest.getHeader("Timestamp");
        if (StringUtils.isEmpty(sign)) {
            responseFail("Signature cannot be empty", response);
            return;
        }
        if (StringUtils.isEmpty(timestampStr)) {
            responseFail("Timestamp cannot be empty", response);
            return;
        }
        // Replay time limit
        long timestamp = Long.parseLong(timestampStr);
        if (System.currentTimeMillis() - timestamp >= signAuthProperties.getTimeout() * 1000) {
            responseFail("Signature has expired", response);
            return;
        }
        // Verify signature
        Map
parameterMap = httpRequest.getParameterMap();
        if (SignUtils.verifySign(parameterMap, sign, timestamp)) {
            chain.doFilter(httpRequest, response);
        } else {
            responseFail("Signature verification failed", response);
        }
    }

    private void responseFail(String msg, ServletResponse response) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println(msg);
        out.flush();
        out.close();
    }

    @Override
    public void destroy() {
        log.info("Destroying SignAuthFilter...");
    }
}

The filter can be configured via application.yml :

sign:
  # Signature timeout in seconds
  timeout: 60
  # URIs that do not require signing
  ignoreUri:
    - /swagger-ui.html
    - /v2/api-docs

And the corresponding properties class:

@Data
@ConfigurationProperties(prefix = "sign")
public class SignAuthProperties {
    /** Signature timeout */
    private Integer timeout;
    /** URIs that can be accessed without a signature */
    private List
ignoreUri;
}

The actual signature verification logic (e.g., hashing algorithm) is omitted because it varies with the chosen encryption method; developers only need to agree on a common approach between front‑end and back‑end.

In summary, by combining HTTPS, encrypted parameters, timestamp‑based signatures, and a server‑side filter, API interfaces can be protected against tampering and replay attacks.

JavaSpring BootAPI securityReplay AttackParameter encryptionrequest signing
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.