Skip to content

Commit

Permalink
Merge pull request #32 from US-ELRR/elrr-improve-request-validation
Browse files Browse the repository at this point in the history
Add try/catch, enhance error nmessages and remove unneeded dependencies.
  • Loading branch information
phleven authored Jan 23, 2025
2 parents c04f7f7 + a34f31b commit bb0910b
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 232 deletions.
50 changes: 18 additions & 32 deletions src/main/java/com/deloitte/elrr/datasync/DatasyncApplication.java
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
/**
*
*/

package com.deloitte.elrr.datasync;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.client.RestTemplate;


import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;

@SpringBootApplication
@EnableScheduling
@ComponentScan({ "com.deloitte.elrr" })
@ComponentScan({"com.deloitte.elrr"})
@EnableEncryptableProperties
public class DatasyncApplication {

/**
*
*/
@Autowired
private Environment env;

/**
*
* @param args
*/
public static void main(final String[] args) {
SpringApplication.run(DatasyncApplication.class, args);
}

/**
* This for returning the Rest Template.
*
* @param builder
* @return RestTemplate
*/
@Bean
public RestTemplate restTemplate(final RestTemplateBuilder builder) {
return builder.build();
}
/**
* @param args
*/
public static void main(final String[] args) {
SpringApplication.run(DatasyncApplication.class, args);
}

/**
* This for returning the Rest Template.
*
* @param builder
* @return RestTemplate
*/
@Bean
public RestTemplate restTemplate(final RestTemplateBuilder builder) {
return builder.build();
}
}
20 changes: 13 additions & 7 deletions src/main/java/com/deloitte/elrr/datasync/HeaderFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,22 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha

HttpServletRequest httpServletRequest = (HttpServletRequest) request;

if (checkHttpHeader == false) {
chain.doFilter(request, response);
} else {
if ("https".equalsIgnoreCase(httpServletRequest.getHeader("X-Forwarded-Proto"))) {
try {
if (checkHttpHeader == false) {
chain.doFilter(request, response);
} else {
log.error("Not a HTTPS request.");
((HttpServletResponse) response)
.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not a HTTPS request.");
if ("https".equalsIgnoreCase(httpServletRequest.getHeader("X-Forwarded-Proto"))) {
chain.doFilter(request, response);
} else {
log.error("Not a HTTPS request.");
((HttpServletResponse) response)
.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not a HTTPS request.");
}
}
} catch (IOException | ServletException e) {
log.error("Error: " + e.getMessage());
e.printStackTrace();
return;
}
}
}
12 changes: 6 additions & 6 deletions src/main/java/com/deloitte/elrr/datasync/InputSanatizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

public class InputSanatizer {

private static final String CHAR_WHITE_LIST_REGEX = "^[\\x09\\x0A\\x0D\\x20-\\x7E | \\xC2-\\xDF | \\xE0\\xA0-\\xBF | [\\xE1-\\xEC\\xEE\\xEF]{2} | \\xED\\x80-\\x9F | [\\xF0\\\\x90-\\xBF]{2} | [\\xF1-\\xF3]{3} | [\\xF4\\x80-\\x8F]{2}]*$";
public static boolean isValidInput(String input) {
return GenericValidator.matchRegexp(input, CHAR_WHITE_LIST_REGEX);
}
private static final String CHAR_WHITE_LIST_REGEX =
"^[\\x09\\x0A\\x0D\\x20-\\x7E | \\xC2-\\xDF | \\xE0\\xA0-\\xBF | [\\xE1-\\xEC\\xEE\\xEF]{2} | \\xED\\x80-\\x9F | [\\xF0\\\\x90-\\xBF]{2} | [\\xF1-\\xF3]{3} | [\\xF4\\x80-\\x8F]{2}]*$";

public static boolean isValidInput(String input) {
return GenericValidator.matchRegexp(input, CHAR_WHITE_LIST_REGEX);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ public class JSONRequestSizeLimitFilter extends OncePerRequestFilter {
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (isApplicationJson(request) && request.getContentLengthLong() < MAX_SIZE_LIMIT) {
filterChain.doFilter(request, response);
} else {
log.error("Request size exceeds the limit.");
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request size exceeds the limit.");
try {
if (isApplicationJson(request) && request.getContentLengthLong() < MAX_SIZE_LIMIT) {
filterChain.doFilter(request, response);
} else {
log.error("Request size exceeds the limit.");
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request size exceeds the limit.");
}
} catch (IOException | ServletException e) {
log.error("Error: " + e.getMessage());
e.printStackTrace();
return;
}
}

Expand Down
178 changes: 102 additions & 76 deletions src/main/java/com/deloitte/elrr/datasync/SanatizingFilter.java
Original file line number Diff line number Diff line change
@@ -1,97 +1,123 @@
package com.deloitte.elrr.datasync;

import fr.spacefox.confusablehomoglyphs.Confusables;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Iterator;

import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Iterator;

import fr.spacefox.confusablehomoglyphs.Confusables;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class SanatizingFilter implements Filter {


private boolean invalidParam;
private boolean invalidParam;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

HttpServletResponse httpResponse = (HttpServletResponse) response;
WrappedHttp httpRequest;
invalidParam = false;

StringBuilder body = new StringBuilder();
for(String line : request.getReader().lines().toList()) {
if(InputSanatizer.isValidInput(line)) {
body.append(line);
body.append('\n');

}
else {
log.error("Illegal line in request body: " + line);
httpResponse.sendError(HttpStatus.BAD_REQUEST.value(), "Illegal line in request body: " + line);
}
}
if(httpResponse.isCommitted())
return;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

httpRequest = new WrappedHttp((HttpServletRequest) request, body.toString());
httpRequest.getParameterMap(); //might help to cache parameters for future filter chain
HttpServletResponse httpResponse = (HttpServletResponse) response;
WrappedHttp httpRequest;
invalidParam = false;

//below we check each parameter string for any invalid values
httpRequest.getParameterNames().asIterator().forEachRemaining((param) -> {
String paramVal = request.getParameter(param);
if(!InputSanatizer.isValidInput(paramVal)) {
invalidParam = true;
}
});

if(invalidParam) {
log.error("Illegal Parameter Value");
httpResponse.sendError(HttpStatus.BAD_REQUEST.value(), "Illegal Parameter Value");
return;
}
StringBuilder body = new StringBuilder();
try {
for (String line : request.getReader().lines().toList()) {
if (InputSanatizer.isValidInput(line)) {
body.append(line);
body.append('\n');
} else {
log.error("Illegal line in request body: " + line);
httpResponse.sendError(
HttpStatus.BAD_REQUEST.value(), "Illegal line in request body: " + line);
}
}
} catch (IOException e) {
log.error("Error: " + e.getMessage());
e.printStackTrace();
return;
}

if (hasHomoGlyphs(httpRequest))
{
log.error("Request body contains homoglyphs.");
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request body contains homoglyphs.");
return;
}
if (httpResponse.isCommitted()) return;

chain.doFilter(httpRequest, response);
}
httpRequest = new WrappedHttp((HttpServletRequest) request, body.toString());
httpRequest.getParameterMap(); // might help to cache parameters for future filter chain

private static boolean hasHomoGlyphs(WrappedHttp httpRequest) {
if(!StringUtils.hasLength(httpRequest.getBody()))
{
return false;
}
Confusables confusables = Confusables.fromInternal();
JSONObject jsonObject = new JSONObject(httpRequest.getBody());
Iterator keys = jsonObject.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
String value = (String) jsonObject.get(key);
boolean dangerousKey = confusables.isDangerous(key);
boolean dangerousValue = confusables.isDangerous(value);
if (dangerousKey || dangerousValue) {
return true;
}
}
return false;
}
// Check each parameter string for any invalid values
httpRequest
.getParameterNames()
.asIterator()
.forEachRemaining(
(param) -> {
String paramVal = request.getParameter(param);
if (!InputSanatizer.isValidInput(paramVal)) {
invalidParam = true;
log.error("Illegal Parameter Value " + paramVal);
}
});

}
if (invalidParam) {
try {
httpResponse.sendError(HttpStatus.BAD_REQUEST.value(), "Illegal Parameter Value");
return;
} catch (IOException e) {
log.error("Error: " + e.getMessage());
e.printStackTrace();
return;
}
}

if (hasHomoGlyphs(httpRequest)) {
try {
log.error("Request body contains homoglyphs.");
httpResponse.sendError(
HttpServletResponse.SC_BAD_REQUEST, "Request body contains homoglyphs.");
return;
} catch (IOException | JSONException e) {
log.error("Error: " + e.getMessage());
e.printStackTrace();
return;
}
}

try {
chain.doFilter(httpRequest, response);
} catch (IOException | ServletException e) {
log.error("Error: " + e.getMessage());
e.printStackTrace();
return;
}
}

private static boolean hasHomoGlyphs(WrappedHttp httpRequest) {
if (!StringUtils.hasLength(httpRequest.getBody())) {
return false;
}
Confusables confusables = Confusables.fromInternal();
JSONObject jsonObject = new JSONObject(httpRequest.getBody());
Iterator keys = jsonObject.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
String value = (String) jsonObject.get(key);
boolean dangerousKey = confusables.isDangerous(key);
boolean dangerousValue = confusables.isDangerous(value);
if (dangerousKey || dangerousValue) {
return true;
}
}
return false;
}
}
Loading

0 comments on commit bb0910b

Please sign in to comment.