-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
Is your feature request related to a problem? Please describe.
spring-cloud-starter-gateway-server-webflux, version 4.3.2
Turning on debug logging by the standard, spring method:
logging.level.org.springframework.cloud.gateway=DEBUG
will by default log all headers of all received requests (this happens twice for each request, in the ObservedRequestHttpHeadersFilter here and here). By logging all headers, it will default to logging the entire Authorization header (if included) and its value (a non-obfuscated token, or other auth).
This current debug logging implementation in ObservedRequestHttpHeadersFilter is a problem for two reasons:
- It is impossible to disable or obfuscate. The current implementation only allows for either no debug logging, or debug logging that should never be turned on in a production environment, due to the security risk of logging authentication.
- It is an easy-to-mistake security risk. The controlling parameter is such a high-level, standard configuration to modify. Nowhere in the name of the parameter would a user understand the production security risk they are turning on.
These both add up to essentially never being able to turn on this debug logging in production. That is not something that needs to happen frequently, but it is my understanding that it is not a BAD thing to do in production, across the various spring libraries, as it would be to do in this library
Describe the solution you'd like
The ObservedRequestHttpHeadersFilter debug logs should be default not include the authorization header in its log. Potentially, it could be included at a TRACE level, or by a separate parameter all together. By default, the token should be obfuscated. This conveys to the developer that the request has a Authorization header and the header has a value, but removes the security risk of logging the Authorization header value.
LMK if there is agreement on this approach. Or if there is a reason we can't/shouldn't go this direction. Im happy to put up a PR, if I get the 👍 on approach.
Describe alternatives you've considered
Ive briefly tried implementing such an approach in my server, and I believe it is impossible or at least not as easy as just overriding ObservedRequestHttpHeadersFilter with my own implementation, since the GatewayDocumentedObservation is package private.
Additional context
Below is one of the log messages included when setting logging.level.org.springframework.cloud.gateway=DEBUG, with identifying information removed. Very partial token included, to show the real issue:
2025-10-27T19:10:08.338Z - -:- - DEBUG 7 --- [api-gateway] [tp346413038-231] g.f.h.o.ObservedRequestHttpHeadersFilter : Client observation {name=http.client.requests(null), error=null, context=name='http.client.requests', contextualName='null', error='null', lowCardinalityKeyValues=[http.method='GET', http.status_code='UNKNOWN', spring.cloud.gateway.route.id='<route id>', spring.cloud.gateway.route.uri=<route uri>], highCardinalityKeyValues=[http.uri=<htti uri>], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@2db5b900', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=1.6014E-5, duration(nanos)=16014.0, startTimeNanos=271475955578866}'], parentObservation={name=http.server.requests(null), error=null, context=name='http.server.requests', contextualName='null', error='null', lowCardinalityKeyValues=[exception='none', method='GET', outcome='SUCCESS', status='200', uri='UNKNOWN'], highCardinalityKeyValues=[http.url=<http url>], map=[class io.micrometer.core.instrument.Timer$Sample='io.micrometer.core.instrument.Timer$Sample@568dda4', class io.micrometer.core.instrument.LongTaskTimer$Sample='SampleImpl{duration(seconds)=6.85781E-4, duration(nanos)=685781.0, startTimeNanos=271475954918061}'], parentObservation=null}} created for the request. New headers are [X-Forwarded-For:<ip address>, X-Forwarded-Proto:"https", X-Forwarded-Port:"443", Host:<host>, X-Amzn-Trace-Id:<trace id>, User-Agent:<user agent>, Accept-Encoding:"gzip, deflate", Accept:"*/*", Cookie:"AWSALB=<ALB id>; AWSALBCORS=<cors info>, Authorization:"Bearer ey[...]Qg"]