How to Prevent Xss Attacks or Untrusted Data in Rest API Json Using Java

How to prevent XSS attacks or untrusted data in Rest API JSON using Java?

Need to override the HttpServletRequest in a Servlet Filter(if you are using Servlet).

  1. Extends HttpServletRequestWrapper that stores JSON body(intention is to sanitize JSON body).

  2. Strip/ escape the eligible JSON value

Extented "HttpServletRequestWrapper" :

public class SanitizationRequestWrapper extends HttpServletRequestWrapper {

public byte[] getBody() {
return body;
}

public void setBody(byte[] body) {
this.body = body;
}

private byte[] body;

public SanitizationRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try {
body = IOUtils.toByteArray(super.getInputStream());
}catch (NullPointerException e){

}
}

@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamImpl(new ByteArrayInputStream(body));
}

@Override
public BufferedReader getReader() throws IOException {
String enc = getCharacterEncoding();
if (enc == null) enc = "UTF-8";
return new BufferedReader(new InputStreamReader(getInputStream(), enc));
}

private class ServletInputStreamImpl extends ServletInputStream {

private InputStream is;

public ServletInputStreamImpl(InputStream is) {
this.is = is;
}

public int read() throws IOException {
return is.read();
}

public boolean markSupported() {
return false;
}

public synchronized void mark(int i) {
throw new RuntimeException(new IOException("mark/reset not supported"));
}

public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
}
}

Servlet filter which sanitize request body:

    public class XSSSanitizeFilters implements Filter {
@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
SanitizationRequestWrapper sanitizeRequest = new SanitizationRequestWrapper(request);
if (null != sanitizeRequest.getBody()) {
try {
sanitizeJson(sanitizeRequest);
} catch (ParseException e) {
LOG.error("Unable to Sanitize the provided JSON .");
}
arg2.doFilter(sanitizeRequest, arg1);

} else {
arg2.doFilter(arg0, arg1);
}
}

public void init(FilterConfig filterConfig) throws ServletException {

}

private void sanitizeJson(SanitizationRequestWrapper sanitizeRequest ) throws IOException, ParseException {
JSONParser parser= new JSONParser();
Object obj = parser.parse(sanitizeRequest.getReader());
ObjectMapper oMapper = new ObjectMapper();
Map <String, Object> map = oMapper.convertValue(obj, Map.class);
sanitizeRequest.setBody((new JSONObject(map)).toString().getBytes());
}


Best Practice For XSS Attacks in Rest Api

As with anything defense in depth is important, so validation and encoding should be done on any user provided input. Encoding is very important because what might be considered malicious is contextual. For example, what might be safe HTML might be an SQL Injection attack.

Parameters in a REST API may be saved which means they are returned from subsequent requests or the results may be reflected back to the user in the request. This means that you can get both reflected and stored XSS attacks. You also need to be careful about DOM Based XSS attacks. A more modern categorization that addresses overlap between stored, reflected, and DOM XSS is Server XSS and Client XSS.

OWASP has a great Cross Site Scripting Prevention Cheat Sheet that details out how to prevent cross site scripting. I find the XSS Prevention Rules Summary and the Output Encoding Rules Summary sections to be very handy.

The big take away is that browsers parse data differently depending on the context, so it is very important that you don't just HTML Entity Encode the data everywhere. This means it is important to do two things:

  • Rule #0 - Only insert untrusted (user provided) data in allowed locations. Only insert data into an HTML document into a "slot" defined by Rules #1-5.

  • When you insert data into one of the trusted slots follow the encoding rules for that specific slot. Again the rules are detailed in the previously linked Cross Site Scripting Prevention Cheat Sheet.

There is also a DOM based XSS Prevention cheat sheet. Like the server side XSS cheat sheet, it provies a set of rules to prevent DOM based XSS.

Can reflected XSS (cross site scripting) attack happen on a REST API which serves non HTML response

It mostly depends on the response content-type. As long as it's something like applicatiin/xml or text/xml (and not text/html or application/xhtml), the api itself is not vulnerable to xss, because a modern browser will not run the script even if displayed.

Note though that it might still be vulnerable to xml injection, and if Checkmarx found it as xss, there probably is some kind of possible injection. Make sure that it's not possible for a user to create xml tags or attributes in the response. The way to do that is very similar to how xss is prevented. Note though that you do not need to html encode user input when writing it to the xml as it's not html, but you need to xml encode values.

Also note that validating input is nice, but in general, injection attacks are prevented by context-aware output encoding, ie. applying the appropriate encoding type to values, xml attributes and so on. Many times you cannot fully achieve this on the input side. (Input validation still makes sense and you should be doing that too, but injections are best prevented by output encoding.)

Best way to handle security and avoid XSS with user entered URLs

If you think URLs can't contain code, think again!

https://owasp.org/www-community/xss-filter-evasion-cheatsheet

Read that, and weep.

Here's how we do it on Stack Overflow:

/// <summary>
/// returns "safe" URL, stripping anything outside normal charsets for URL
/// </summary>
public static string SanitizeUrl(string url)
{
return Regex.Replace(url, @"[^-A-Za-z0-9+&@#/%?=~_|!:,.;\(\)]", "");
}

How to block or protect against XSS for Spring MVC 4 applications without SpringBoot

First of all , concept to protect against vulnerabilities has nothing to do with SpringBoot and XSS is one of those vulnerabilities.

This vulnerability is protected by implementing a org.springframework.web.filter.OncePerRequestFilter and depending on which top framework you use & what kind of app you have - filter registration & chaining process has to be implemented.

Idea is to simply sanitize every incoming JSON body & call next filter in chain with sanitized request body.

If you have a Spring based project, you should first try to use Spring Security dependencies and enable default security features. refer this question

For xss protection , offered by spring security they have this disclaimer -

Note this is not comprehensive XSS protection!

In my case, I wrote a custom XSS protection filter implementing - org.springframework.web.filter.OncePerRequestFilter

In this filter- I have used this API ,

<dependency>
<groupId>org.owasp.esapi</groupId>
<artifactId>esapi</artifactId>
</dependency>

In my code , I have listed down possible attack patterns but I guess there might be better way to do it.

Refer these two on SO to know more as what I am talking about - XSS filter to remove all scripts & How to Modify QueryParam and PathParam in Jersey 2

Answer by Melardev is explaining the only case for @RequestParam & you have to extend that approach to handle the case when its a JSON body. I have handled the case of a json body but can't share my code due to company copy right .

How to validate and sanitize HTTP Get with Spring Boot?

You need to be a bit careful with these scanning tools as sometimes these tools do report false positives and sometimes no code changes are required. I am no expert of checkmarx but be sure that this tool really understands bean validation annotations that you are using & the call Jsoup.clean(personName, Whitelist.basic()) .

I think I did ALL the validation necessary. What else???

First you need to understand the different between application level input sanitation & business level input validation for a controller. What you are doing here is second part & first might be missing in your set up which is exclusively done from security perspective & usually set up for whole application.

You are using @Size annotation to limit an input's size but that doesn't guarantee about bad strings - strings that can cause XSS attacks. Then, you are using call Jsoup.clean(personName, Whitelist.basic())) to clean this size validated input. As I am not sure what that call does so you need to ensure that new value is XSS - Safe. You are immediately passing that value to DB call & then returning an Integer to caller/client so I am very pessimist about any possibility of an XSS attack here but tool is saying so.

There must be some missing validation. How to validate HTTP GET
properly with Spring Boot

As I explained earlier, input validation is a term usually meant for business logic level input validation while input sanitization / clean up is about security. In Spring Boot environment, this is usually done by using Spring Security APIs & enabling XSS filters or by writing your own XSS filter and plug it in your application. Filter comes first and your controller later so your controller will always have a sanitized value & you will apply business validations on that sanitized value.

This is a broad level answer & for code etc you might do google. Also suggest to read more about XSS attacks. Just understand that there are multiple ways to accomplish same goal.

3 Ways to Prevent XSS

XSS prevention in Java

How to create filter in Spring RESTful for Prevent XSS?

Cross Site Scripting (XSS) Attack Tutorial with Examples, Types & Prevention

In last link, its mentioned ,

The first step in the prevention of this attack is Input validation.
Everything, that is entered by the user should be precisely validated,
because the user’s input may find its way to the output.

& that you are not doing in your code so I would guess that there is no XSS.

EDIT:

There are two aspects of XSS security - first not allowing malicious input to server side code & that would be done by having an XSS filter & Sometimes, there is no harm in allowing malicious input ( lets say you are saving that malicious input to DB or returning in API response ) .

Second aspect is instructing HTML clients about possible XSS attacks ( if we know for sure that API client is going to be HTML / UI ) then we need to add X-XSS-Protection header & that would be done by below code. This will enable browser to turn on its XSS protection feature ( if present ) .

@Override
protected void configure(HttpSecurity http) throws Exception {

http.headers().xssProtection()....

}

What is the http-header “X-XSS-Protection”?

Is Xss protection in Spring security enabled by default?

For first aspect i.e. writing filter - refer my this answer and links in that answer.

I think, I have wrongly written above that Spring Security provides input sanitation filters , I guess , it doesn't. Will verify and let you know. I have written my custom filter on the lines mentioned in answer to this question - Prevent XSS in Spring MVC controller

You have to also understand that Spring Boot gets used to write traditional MVC apps too where server side presents HTML to render too . In case of JSON responses ( REST APIs ) , UI client can control what to escape and what not, complexity arises because JSON output is not always fed to HTML clients aka browsers.

How to eliminate special characters before hitting the controller in Java?

any alternative approach which can help in preventing any special characters to hit the controller.

What if you try to do the following ...

  1. Get the request body
  2. Process it
  3. Set the request body again in your filter by setting the body to the processed version ?
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

HttpServletRequest originalRequest = (HttpServletRequest) request;
HttpServletResponse originalResponse = (HttpServletResponse) response;

/**
* 2.Read the original request body and change it
*/
String originalRequestBody = ServletUtil.readRequestBody(originalRequest); // Read the original request body
// Body is processed here !
String modifyRequestBody = processBody(originalRequestBody); // Modify request body (clear text)
HttpServletRequest orginalRequest = (HttpServletRequest) request;
ModifyRequestBodyWrapper requestWrapper = new ModifyRequestBodyWrapper(orginalRequest, modifyRequestBody);

/**
* 3. Build a new response object
*/
ModifyResponseBodyWrapper responseWrapper = new ModifyResponseBodyWrapper(originalResponse);
chain.doFilter(requestWrapper, responseWrapper);
String originalResponseBody = responseWrapper.getResponseBody(); // Original response body (clear text)
String modifyResponseBody = this.encryptBody(originalResponseBody); // Modified response volume (ciphertext)

/**
* 4.Output the modified response body with the output stream of the original response object
* To ensure that the response type is consistent with the original request, and reset the response body size
*/
originalResponse.setContentType(requestWrapper.getOrginalRequest().getContentType()); // Be consistent with the request
byte[] responseData = modifyResponseBody.getBytes(responseWrapper.getCharacterEncoding()); // The coding is consistent with the actual response
originalResponse.setContentLength(responseData.length);
@Cleanup ServletOutputStream out = originalResponse.getOutputStream();
out.write(responseData);
}

Here is a code example, which implements this.



Related Topics



Leave a reply



Submit