mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 09:12:14 +00:00
Add logging to CsrfTokenRequestHandler implementations
Add trace-level logging to show the logical path of CSRF token processing - Log token source (header or parameter) in resolveCsrfTokenValue - Log request attribute names in handle methods - Log failures in XorCsrfTokenRequestAttributeHandler (especially Base64 decoding) - Add similar logging to XorServerCsrfTokenRequestAttributeHandler Improves debugging capabilities without changing functionality. Closes gh-13626 Signed-off-by: yybmion <yunyubin54@gmail.com>
This commit is contained in:
parent
ba7be9c8b9
commit
a90ce5142c
@ -119,6 +119,9 @@ public final class CsrfFilter extends OncePerRequestFilter {
|
||||
}
|
||||
CsrfToken csrfToken = deferredCsrfToken.get();
|
||||
String actualToken = this.requestHandler.resolveCsrfTokenValue(request, csrfToken);
|
||||
if (actualToken != null && this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Found a CSRF token in the request"));
|
||||
}
|
||||
if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
|
||||
boolean missingToken = deferredCsrfToken.isGenerated();
|
||||
this.logger
|
||||
|
@ -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.
|
||||
@ -20,7 +20,10 @@ 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.util.Assert;
|
||||
|
||||
/**
|
||||
@ -29,10 +32,13 @@ import org.springframework.util.Assert;
|
||||
* 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";
|
||||
|
||||
/**
|
||||
@ -60,6 +66,9 @@ public class CsrfTokenRequestAttributeHandler implements CsrfTokenRequestHandler
|
||||
String csrfAttrName = (this.csrfRequestAttributeName != null) ? this.csrfRequestAttributeName
|
||||
: csrfToken.getParameterName();
|
||||
request.setAttribute(csrfAttrName, csrfToken);
|
||||
|
||||
logger.trace(LogMessage.format("Wrote a CSRF token to the following request attributes: [%s, %s]", csrfAttrName,
|
||||
CsrfToken.class.getName()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
|
@ -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.
|
||||
@ -21,6 +21,7 @@ import java.util.function.Supplier;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@ -30,6 +31,7 @@ import org.springframework.util.Assert;
|
||||
* available to the application through request attributes.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Yoobin Yoon
|
||||
* @since 5.8
|
||||
* @see CsrfTokenRequestAttributeHandler
|
||||
*/
|
||||
@ -49,10 +51,20 @@ public interface CsrfTokenRequestHandler extends CsrfTokenRequestResolver {
|
||||
Assert.notNull(request, "request cannot be null");
|
||||
Assert.notNull(csrfToken, "csrfToken cannot be null");
|
||||
String actualToken = request.getHeader(csrfToken.getHeaderName());
|
||||
if (actualToken == null) {
|
||||
actualToken = request.getParameter(csrfToken.getParameterName());
|
||||
if (actualToken != null) {
|
||||
return actualToken;
|
||||
}
|
||||
return actualToken;
|
||||
CsrfTokenRequestHandlerLoggerHolder.logger.trace(
|
||||
LogMessage.format("Did not find a CSRF token in the [%s] request header", csrfToken.getHeaderName()));
|
||||
|
||||
actualToken = request.getParameter(csrfToken.getParameterName());
|
||||
if (actualToken != null) {
|
||||
return actualToken;
|
||||
}
|
||||
CsrfTokenRequestHandlerLoggerHolder.logger.trace(LogMessage
|
||||
.format("Did not find a CSRF token in the [%s] request parameter", csrfToken.getParameterName()));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.csrf;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Utility class for holding the logger for {@link CsrfTokenRequestHandler}
|
||||
*/
|
||||
final class CsrfTokenRequestHandlerLoggerHolder {
|
||||
|
||||
static final Log logger = LogFactory.getLog(CsrfTokenRequestHandler.class);
|
||||
|
||||
private CsrfTokenRequestHandlerLoggerHolder() {
|
||||
}
|
||||
|
||||
}
|
@ -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.
|
||||
@ -22,7 +22,10 @@ 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;
|
||||
|
||||
@ -32,10 +35,13 @@ import org.springframework.util.Assert;
|
||||
* 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();
|
||||
|
||||
/**
|
||||
@ -70,6 +76,9 @@ public final class XorCsrfTokenRequestAttributeHandler extends CsrfTokenRequestA
|
||||
@Override
|
||||
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
|
||||
String actualToken = super.resolveCsrfTokenValue(request, csrfToken);
|
||||
if (actualToken == null) {
|
||||
return null;
|
||||
}
|
||||
return getTokenValue(actualToken, csrfToken.getToken());
|
||||
}
|
||||
|
||||
@ -79,12 +88,16 @@ public final class XorCsrfTokenRequestAttributeHandler extends CsrfTokenRequestA
|
||||
actualBytes = Base64.getUrlDecoder().decode(actualToken);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.trace(LogMessage.format("Not returning the CSRF token since it's not Base64-encoded"), ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] tokenBytes = Utf8.encode(token);
|
||||
int tokenSize = tokenBytes.length;
|
||||
if (actualBytes.length != tokenSize * 2) {
|
||||
logger.trace(LogMessage.format(
|
||||
"Not returning the CSRF token since its Base64-decoded length (%d) is not equal to (%d)",
|
||||
actualBytes.length, tokenSize * 2));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.security.web.server.csrf;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.multipart.FormFieldPart;
|
||||
@ -31,10 +34,13 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* resolving the token value as either a form data value or header of the request.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Yoobin Yoon
|
||||
* @since 5.8
|
||||
*/
|
||||
public class ServerCsrfTokenRequestAttributeHandler implements ServerCsrfTokenRequestHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ServerCsrfTokenRequestAttributeHandler.class);
|
||||
|
||||
private boolean isTokenFromMultipartDataEnabled;
|
||||
|
||||
@Override
|
||||
@ -42,6 +48,7 @@ public class ServerCsrfTokenRequestAttributeHandler implements ServerCsrfTokenRe
|
||||
Assert.notNull(exchange, "exchange cannot be null");
|
||||
Assert.notNull(csrfToken, "csrfToken cannot be null");
|
||||
exchange.getAttributes().put(CsrfToken.class.getName(), csrfToken);
|
||||
logger.trace(LogMessage.format("Wrote a CSRF token to the [%s] exchange attribute", CsrfToken.class.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.security.web.server.csrf;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@ -46,9 +47,27 @@ public interface ServerCsrfTokenRequestHandler extends ServerCsrfTokenRequestRes
|
||||
default Mono<String> resolveCsrfTokenValue(ServerWebExchange exchange, CsrfToken csrfToken) {
|
||||
Assert.notNull(exchange, "exchange cannot be null");
|
||||
Assert.notNull(csrfToken, "csrfToken cannot be null");
|
||||
return exchange.getFormData()
|
||||
.flatMap((data) -> Mono.justOrEmpty(data.getFirst(csrfToken.getParameterName())))
|
||||
.switchIfEmpty(Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(csrfToken.getHeaderName())));
|
||||
|
||||
String headerName = csrfToken.getHeaderName();
|
||||
String parameterName = csrfToken.getParameterName();
|
||||
|
||||
return exchange.getFormData().flatMap((data) -> {
|
||||
String token = data.getFirst(parameterName);
|
||||
if (token != null) {
|
||||
return Mono.just(token);
|
||||
}
|
||||
ServerCsrfTokenRequestHandlerLoggerHolder.logger
|
||||
.trace(LogMessage.format("Did not find a CSRF token in the [%s] request parameter", parameterName));
|
||||
return Mono.empty();
|
||||
}).switchIfEmpty(Mono.defer(() -> {
|
||||
String token = exchange.getRequest().getHeaders().getFirst(headerName);
|
||||
if (token != null) {
|
||||
return Mono.just(token);
|
||||
}
|
||||
ServerCsrfTokenRequestHandlerLoggerHolder.logger
|
||||
.trace(LogMessage.format("Did not find a CSRF token in the [%s] request header", headerName));
|
||||
return Mono.empty();
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.server.csrf;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Utility class for holding the logger for {@link ServerCsrfTokenRequestHandler}
|
||||
*/
|
||||
final class ServerCsrfTokenRequestHandlerLoggerHolder {
|
||||
|
||||
static final Log logger = LogFactory.getLog(ServerCsrfTokenRequestHandler.class);
|
||||
|
||||
private ServerCsrfTokenRequestHandlerLoggerHolder() {
|
||||
}
|
||||
|
||||
}
|
@ -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.
|
||||
@ -19,8 +19,11 @@ package org.springframework.security.web.server.csrf;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@ -32,10 +35,13 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* 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();
|
||||
|
||||
/**
|
||||
@ -72,12 +78,16 @@ public final class XorServerCsrfTokenRequestAttributeHandler extends ServerCsrfT
|
||||
actualBytes = Base64.getUrlDecoder().decode(actualToken);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.trace(LogMessage.format("Not returning the CSRF token since it's not Base64-encoded"), ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] tokenBytes = Utf8.encode(token);
|
||||
int tokenSize = tokenBytes.length;
|
||||
if (actualBytes.length != tokenSize * 2) {
|
||||
logger.trace(LogMessage.format(
|
||||
"Not returning the CSRF token since its Base64-decoded length (%d) is not equal to (%d)",
|
||||
actualBytes.length, tokenSize * 2));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user