Clickjacking in Spring Boot with Hmac Signatures
Clickjacking in Spring Boot with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an invisible or misleading layer tricks a user into interacting with a page they did not intend to interact with. In Spring Boot applications that rely on Hmac Signatures for request integrity, clickjacking can still occur because Hmac protects the authenticity and integrity of requests but does not prevent the browser from rendering the page inside an invisible frame. If the application embeds its own UI in an iframe and uses Hmac Signatures only to validate state-changing requests, an attacker can embed the target page in a malicious site, overlay interactive elements, and relay user actions through the embedded context. The Hmac Signature on the request may remain valid if the embedded page includes the required signature parameters or cookies, allowing the forged action to be accepted as legitimate.
Consider a Spring Boot app that generates Hmac Signatures per request using a shared secret and includes the signature in a request parameter or header. If the app serves pages that perform sensitive operations via GET or POST without additional anti-CSRF context tied to the render path, and if the page is served with a missing or weak Content-Security-Policy (CSP) frame-ancestors directive, the browser may allow the page to be embedded. An attacker can then overlay transparent controls on top of embedded elements (such as a funds transfer or profile update), and the signed requests initiated from within the iframe will still carry the valid Hmac Signature. This means the backend validates the signature and processes the action, even though the user believed they were interacting with the attacker’s page. The vulnerability is not in the Hmac algorithm itself, but in the absence of frame-busting and CSP enforcement alongside signature validation.
Additionally, if the Hmac implementation binds the signature to specific parameters but the UI includes dynamic query parameters that are reflected in the signed string, an attacker may attempt parameter substitution within the iframe context. For example, a signed request that includes an action parameter could be manipulated if the embedding site can control part of the UI state through query parameters passed into the iframe src. The presence of Hmac Signatures might give a false sense of protection, leading developers to neglect standard clickjacking mitigations such as X-Frame-Options or CSP frame-ancestors. Therefore, relying solely on Hmac Signatures without securing the rendering context leaves the application exposed to clickjacking, where the integrity of requests is preserved but the user’s consent context is compromised.
Hmac Signatures-Specific Remediation in Spring Boot — concrete code fixes
To mitigate clickjacking while using Hmac Signatures in Spring Boot, combine signature validation with strict framing controls and secure cookie/session practices. Use Content-Security-Policy frame-ancestors to disallow embedding, set X-Frame-Options where appropriate, and ensure that Hmac validation includes nonce or origin checks that are not exposed to embedded contexts.
Example Spring Boot configuration with CSP and Hmac validation:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new HmacValidationFilter(), UsernamePasswordAuthenticationFilter.class)
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; frame-ancestors 'none';")
)
.frameOptions().disable() // CSP handles framing; X-Frame-Options may be set separately if needed
);
return http.build();
}
}
Example Hmac signature generation and validation utility used by the filter:
@Component
public class HmacUtil {
private final String secret = System.getenv("HMAC_SECRET");
public String computeHmac(String data) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(key);
return Hex.encodeHexString(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException("Hmac computation failed", e);
}
}
public boolean validateHmac(String data, String receivedHmac) {
String computed = computeHmac(data);
return computed.equals(receivedHmac);
}
}
Example filter that validates Hmac and enforces origin binding to prevent framing attacks:
public class HmacValidationFilter extends OncePerRequestFilter {
@Autowired
private HmacUtil hmacUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI();
String origin = request.getHeader("Origin");
String receivedHmac = request.getHeader("X-Request-Hmac");
if (shouldValidateHmac(uri)) {
String payload = buildPayloadForHmac(request); // includes method, path, nonce/timestamp, selected headers
if (!hmacUtil.validateHmac(payload, receivedHmac)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid Hmac");
return;
}
if (!"https://your-trusted-origin.com".equals(origin)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid origin");
return;
}
}
filterChain.doFilter(request, response);
}
private String buildPayloadForHmac(HttpServletRequest request) {
// Example: include method, path, timestamp, and a nonce to prevent replay
String timestamp = String.valueOf(System.currentTimeMillis());
String nonce = UUID.randomUUID().toString();
return String.join("|", request.getMethod(), request.getRequestURI(), timestamp, nonce);
}
private boolean shouldValidateHmac(String uri) {
return uri.startsWith("/api/");
}
}
By combining these measures, the application ensures that even if an attacker can trick a browser into rendering the page in an iframe, the Hmac validation will reject requests lacking proper origin binding and the CSP frame-ancestors directive will block the page from being embedded in the first place.