Skip to content

Add logging to CsrfTokenRequestHandler implementations #16994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;

/**
Expand All @@ -29,10 +32,13 @@
* value as either a header or parameter value of the request.
*
* @author Steve Riesenberg
* @author Yoobin Yoon
* @since 5.8
*/
public class CsrfTokenRequestAttributeHandler implements CsrfTokenRequestHandler {

private static final Log logger = LogFactory.getLog(CsrfTokenRequestAttributeHandler.class);

private String csrfRequestAttributeName = "_csrf";

/**
Expand Down Expand Up @@ -60,6 +66,9 @@ public void handle(HttpServletRequest request, HttpServletResponse response,
String csrfAttrName = (this.csrfRequestAttributeName != null) ? this.csrfRequestAttributeName
: csrfToken.getParameterName();
request.setAttribute(csrfAttrName, csrfToken);

logger.trace(LogMessage.format("CSRF token written to request attributes: %s, %s", CsrfToken.class.getName(),
csrfAttrName));
}

@SuppressWarnings("serial")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,11 +16,12 @@

package org.springframework.security.web.csrf;

import java.util.function.Supplier;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;

/**
Expand All @@ -30,12 +31,15 @@
* available to the application through request attributes.
*
* @author Steve Riesenberg
* @author Yoobin Yoon
* @since 5.8
* @see CsrfTokenRequestAttributeHandler
*/
@FunctionalInterface
public interface CsrfTokenRequestHandler extends CsrfTokenRequestResolver {

Log logger = LogFactory.getLog(CsrfTokenRequestHandler.class);

/**
* Handles a request using a {@link CsrfToken}.
* @param request the {@code HttpServletRequest} being handled
Expand All @@ -51,6 +55,13 @@ default String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfT
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
if (actualToken != null) {
logger.trace(
LogMessage.format("CSRF token found in request parameter: %s", csrfToken.getParameterName()));
}
}
else {
logger.trace(LogMessage.format("CSRF token found in request header: %s", csrfToken.getHeaderName()));
}
return actualToken;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,13 +16,14 @@

package org.springframework.security.web.csrf;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.function.Supplier;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.util.Assert;

Expand All @@ -32,10 +33,13 @@
* value from the masked value as either a header or parameter value of the request.
*
* @author Steve Riesenberg
* @author Yoobin Yoon
* @since 5.8
*/
public final class XorCsrfTokenRequestAttributeHandler extends CsrfTokenRequestAttributeHandler {

private static final Log logger = LogFactory.getLog(XorCsrfTokenRequestAttributeHandler.class);

private SecureRandom secureRandom = new SecureRandom();

/**
Expand All @@ -55,6 +59,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response,
Assert.notNull(response, "response cannot be null");
Assert.notNull(deferredCsrfToken, "deferredCsrfToken cannot be null");
Supplier<CsrfToken> updatedCsrfToken = deferCsrfTokenUpdate(deferredCsrfToken);
logger.trace(LogMessage.format("XOR CSRF token created and will be written to request attributes"));
super.handle(request, response, updatedCsrfToken);
}

Expand All @@ -70,7 +75,18 @@ private Supplier<CsrfToken> deferCsrfTokenUpdate(Supplier<CsrfToken> csrfTokenSu
@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
String actualToken = super.resolveCsrfTokenValue(request, csrfToken);
return getTokenValue(actualToken, csrfToken.getToken());
if (actualToken == null) {
logger.trace(LogMessage.format("No CSRF token value found in request"));
return null;
}
String tokenValue = getTokenValue(actualToken, csrfToken.getToken());
if (tokenValue == null) {
logger.trace(LogMessage.format("CSRF token validation failed"));
}
else {
logger.trace(LogMessage.format("CSRF token successfully validated"));
}
return tokenValue;
}

private static String getTokenValue(String actualToken, String token) {
Expand All @@ -79,12 +95,14 @@ private static String getTokenValue(String actualToken, String token) {
actualBytes = Base64.getUrlDecoder().decode(actualToken);
}
catch (Exception ex) {
logger.trace(LogMessage.format("Failed to find CSRF token since Base64 decoding failed"), ex);
return null;
}

byte[] tokenBytes = Utf8.encode(token);
int tokenSize = tokenBytes.length;
if (actualBytes.length != tokenSize * 2) {
logger.trace(LogMessage.format("Failed to validate CSRF token since token length is invalid"));
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,9 @@
import java.security.SecureRandom;
import java.util.Base64;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import reactor.core.publisher.Mono;

import org.springframework.security.crypto.codec.Utf8;
Expand All @@ -32,10 +35,13 @@
* masked value as either a form data value or header of the request.
*
* @author Steve Riesenberg
* @author Yoobin Yoon
* @since 5.8
*/
public final class XorServerCsrfTokenRequestAttributeHandler extends ServerCsrfTokenRequestAttributeHandler {

private static final Log logger = LogFactory.getLog(XorServerCsrfTokenRequestAttributeHandler.class);

private SecureRandom secureRandom = new SecureRandom();

/**
Expand All @@ -57,13 +63,19 @@ public void handle(ServerWebExchange exchange, Mono<CsrfToken> csrfToken) {
createXoredCsrfToken(this.secureRandom, token.getToken())))
.cast(CsrfToken.class)
.cache();
logger.trace(LogMessage.format("XOR CSRF token created and will be written to exchange attributes"));
super.handle(exchange, updatedCsrfToken);
}

@Override
public Mono<String> resolveCsrfTokenValue(ServerWebExchange exchange, CsrfToken csrfToken) {
return super.resolveCsrfTokenValue(exchange, csrfToken)
.flatMap((actualToken) -> Mono.justOrEmpty(getTokenValue(actualToken, csrfToken.getToken())));
.flatMap((actualToken) -> Mono.justOrEmpty(getTokenValue(actualToken, csrfToken.getToken())))
.doOnNext(tokenValue -> {
if (tokenValue != null) {
logger.trace(LogMessage.format("CSRF token successfully validated"));
}
});
}

private static String getTokenValue(String actualToken, String token) {
Expand All @@ -72,12 +84,14 @@ private static String getTokenValue(String actualToken, String token) {
actualBytes = Base64.getUrlDecoder().decode(actualToken);
}
catch (Exception ex) {
logger.trace(LogMessage.format("Failed to find CSRF token since Base64 decoding failed"), ex);
return null;
}

byte[] tokenBytes = Utf8.encode(token);
int tokenSize = tokenBytes.length;
if (actualBytes.length != tokenSize * 2) {
logger.trace(LogMessage.format("Failed to validate CSRF token since token length is invalid"));
return null;
}

Expand Down