I am sending a request from Postman, with the header "Api-Version": " 1"
The response status is 200, but I am expecting to throw 400
In a filter class which extends OncePerRequestFilter, I have the following code snippet:
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
try {
String reqClientKey = httpServletRequest.getHeader("Api-Version");
...
The value of reqClientKey is "1" instead of " 1".
Why the value gets trimmed? How can I get the spaces as well?
The rest specification RFC 7230 states the following under the ”field parsing” section.
The field value does not include any leading or trailing whitespace
So leading or trailing whitespaces are not allowed in header values
Related
From postman I am hitting a post request like http://localhost:8084/abc/api/v1/xyz having payload and header. we have configured a Filter class extending GenericFilterBean before it hits the Controller. Its executing all the codes of Filter class fine but while executing 'chain.doFilter(request, response);' in the end to forward request to controller method its throwing below exception.In Filter class we are reading the payload and saving in audit table. In Controller class method we have parameters like #RequestBody annotaion, #Context HttpServletRequest, BindingResult.
18:59:25,779 INFO [stdout] (default task-1) 18:59:25,778||WARN |AbstractHandlerExceptionResolver:197|Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: UT010029: Stream is closed]
Kindly suggest on this.
Is your filter reading the contents of the request? If so then you'll need to look at alternatives to this, as the input stream is not likely to be reusable without some assistance.
Can you post some code from the filter method?
Spring provides at least one mechanism to work around this, which might be helpful here, specifically the ContentCachingRequestWrapper. You can either create a new Filter which wraps the actual request in this wrapper, or you can simply make use of it yourself in your filter.
You might update your filter with something like this
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(currentRequest);
// todo: your magic code, using wrappedRequest to access the body of the request
// note: passing through the wrapped request,
// which allows later handlers to also access the request
chain.doFilter(wrappedRequest, servletResponse);
}
Note the documentation for ContentCachingRequestWrapper notes
HttpServletRequest wrapper that caches all content read from the input stream and reader, and allows this content to be retrieved via a byte array.
The error that you're receiving indicates you're reading the InputStream of the request, and you should rather simply access the getContentAsByteArray method of the wrapper.
BELOW IS THE static code analysis report from SpotBugs
XSS_SERVLET: Potential XSS in Servlet
A potential XSS was found. It could be used to execute unwanted JavaScript in a client's browser. (See references)
Vulnerable Code:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String input1 = req.getParameter("input1");
[...]
resp.getWriter().write(input1);
}
Solution:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String input1 = req.getParameter("input1");
[...]
resp.getWriter().write(Encode.forHtml(input1))
Encode.forJava for JavaScript is writing special chars and JSON string is compromised.
How to use Encoder to send JSON string. without failing security CHECK
Perhaps you could have a look at OWASP JSON sanitizer https://www.owasp.org/index.php/OWASP_JSON_Sanitizer#tab=Main ?
I`m using ContentCachingRequestWrapper to cache my request in Spring Boot filter. Unfortunatelly, when I use method getContentAsByteArray() to get content of my request - I get only array with size 8000 bytes.
I haven`t got any post limit in Tomcat. What is more, when I check size of request earlier - it is correct.
Do you know why ContentCachingRequestWrapper.getContentAsByteArray() return only 8000 bytes?
Code:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
chain.doFilter(requestWrapper, response);
//here --> requestWrapper.getContentAsByteArray().length = 8000 (for larger request)
savemyRequest(new String(requestWrapper.getContentAsByteArray()), response.getStatus());
}
I encountered a similar issue and decided to keep what I got here.
People in the comments already mentioned a bit.
ContentCachingRequestWrapper itself does not read the request body. It wraps the request body with (as class name implies) PushBackInputStream. As soon as the request body is read (somewhere after filters), then a copy of the data is cached in ContentCachingRequestWrapper.
In my case issue was the next: Request body was started to be converted by
MappingJackson2HttpMessageConverter. Reading of request body and converting to the Java Object happened in a gradual way: first part (8000bytes) read - first part converted, ... . Then conversion exception happened and then I got only 8000bytes in a controller exception handler (#ExceptionHandler) - part of the request body that was read by a converter.
I was going through the source code of the org.springframework.security.web.FilterChainProxy class. I want to undersatnd how its doFilter method work. The following is the code.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
FilterInvocation fi = new FilterInvocation(request, response, chain);
List<Filter> filters = getFilters(fi.getRequestUrl());
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(fi.getRequestUrl() +
filters == null ? " has no matching filters" : " has an empty filter list");
}
chain.doFilter(request, response);
return;
}
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters);
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
}
My understanding is If I define custom filter not related to Spring in the web.xml , they will be included in the FilterChain object passed to the FilterChainProxy (I understand this happens via the DelegatingFilterProxy). Is that correct?
I think the IF block gets executed when there are non-spring Filters defined in the web.xml and when there are no Filters defined in the application context.
VirtualFilterChain here caters for Filters defined in the application text.
There is a return statement in the If block which prevents VirtualFilterChain section getting executed.
But how does this handle both Filters defined in the web.xml and the ones defined in the application context?
the "filterChain" parameter refers to the Servlet filters defined in web.xml. Look at this code in DelegatingFilterProxy.java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
...
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
The invokeDelegate(...) is what invokes FilterChainProxy's doFilter(...) method.
List<Filter> filters = getFilters(fi.getRequestUrl());
generates a list of Spring Security filters that match given url (some filters are listed in this section).
If no Spring Security filters match the requestUrl, the execution just moves on to the rest of the filters defined in web.xml. That's what the if() block is for.
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
This is where Spring Security filters' doFilter(...) methods get called. So, for example, if you have UsernamePasswordAuthenticationFilter as one of the filters configured, then virtualFilterChain.doFilter(...) will eventually invoke UsernamePasswordAuthenticationFilter's doFilter(...) method.
I have a few controllers that use the #RequestHeader annotation to get the logged in user. I cannot seem to find the correct way to alter the header before the method is called.
I already tried to wrap the request and using an interceptor (in the prehandle method, if I'm not mistaken) and pass along the request,but it seems like the headers are not being queried. An exception is thrown by the servlet dispatcher that the username is missing in the header.
Therefore I'm wondering whether someone knows how and when spring handles this annotation, so I can write the proper interceptor.
Don't have the code at hand, but if needed, I will post fragments later on. But the question is simple: how to inject a param into the request header when #RequestHeader is used on a controller method?
Kind regards,
Tom
First edit:
#Sotirios
I tried using the Filter, which works. But this is less convenient for me than the handlerinterceptor. Since I only need the filter for debugging. So again the better question: why isn't this not working with an interceptor?
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpRequest) {
#Override
public Enumeration getHeaders(String name) {
Enumeration headers = super.getHeaders(name);
if( isUseFilter() && Constants.REMOTE_USER.equalsIgnoreCase(name) ){
String user = super.getHeader(name);
headers = enumeration(asList(isEmpty(user)? getDebuggingUserId() :user));
}
return headers;
}
};
chain.doFilter(wrapper, response);
}