Forced Validation Paradigm Page2
------------------
Using ValidationService to mitigate CWE 501 Trust Boundary Violation
We see that developer defines a Session Container Management class which takes care of aspects of session management including adding session attributes.
| public class SessionContainerMgmt { .. public void setSessionAttribute(String key, Object val) { httpSession.setAttribute(key,val); //CWE 501 flagged } .. } |
This call to HttpSession.setAttribute() mixes trusted and untrusted data in the same data structure, thereby encouraging programmers to mistakenly trust unvalidated data.
Remediation: Avoid storing untrusted data alongside trusted data in the same data structure. Establish and maintain trust boundaries for data storage.
Note that only 1 finding is reported by SAST, however, it is a critical one. Since there will be several usages/references of this utility function setSessionAttribute.
Based on the different types of objects we are trying to add to the session container. It is important that before we add an attribute we must ensure some validation of the given key-value pair.
| public void setSessionAttribute(String key, Object val) { if(validate(val)){ httpSession.setAttribute(key,val); //CWE 501 flagged, can be mitigated } } |
Not only value, we would recommend validation of key string also.
public void setSessionAttribute(String key, Object val) {
if(validateKey(key) && validateValue(val)){
httpSession.setAttribute(key,val); //CWE 501 flagged, can be mitigated
}
}
*Here, for understanding the concepts, we will focus only on the 'val' (Value) argument.*Both validation methods are implemented accordingly.
Keeping in mind, that it is an Object and different validation methods would be required for different Objects, e.g. some String patterns, Product (as Object), Order (as Object), and User (as Object).
Validation logic would be different for all of these.
We recommend something like below:-
Make the default method as:-
private void setSessionAttribute(String key, Object val) {
httpSession.setAttribute(key,val); //CWE 501 flagged, can be mitigated
}
And define a new Public method that enforces the caller of this function to provide a ValidationService
public void setSessionAttribute(String key, Object val, ValidationService valValidator) throws ValidationException{
if (valValidator.validate(val)) {
setSessionAttribute(key,val); //Call the private method
// OR httpSession.setAttribute(key,val);
}else{
throw new ValidationException(“Given value object is invalid.”);
}
}
Typical usage:
SessionContainerMgmt scm = new SessionContainerMgmt();
String value = request.getParameter("input");
scm.setSessionAttribute( name, value);
Recommended code change:
SessionContainerMgmt scm = new SessionContainerMgmt();
String value = request.getParameter("input");
// scm.setSessionAttribute( name, value);
ValidationService validator = new ValidationServicImpl();
try{
scm.setSessionAttribute(name, value, validator);
}catch(ValidationException ex) {
..
}
*Here, ValidationServiceImpl is an implementation of the ValidationService by overriding the abstract method of validation()This is forced validation for calling programs.
Another approach is forced validation in the utility function itself.
Which means keeping the default signature available for callers of this function.
public void setSessionAttribute(String key, Object val) {
if(obj instanceof String && Pattern.compile("^[A-Za-z0-9]+$").matcher(obj.toString()).find()){
httpSession.setAttribute(key,val);
}
else if(obj instanceof Email && EmailValidator.getInstance().validate(obj)){
httpSession.setAttribute(key,val);
}
else if(obj instanceof Process && ProcessValidator.getInstance().validate(obj)){
httpSession.setAttribute(key,val);
}
else if(obj instanceof Order && OrderValidator.getInstance().validate(obj)){
httpSession.setAttribute(key,val);
}
else{
// other validations followed by httpSession.setAttribute() calls.
}
}
That would allow to keep the original reference as is:
scm.setSessionAttribute( name, value);
Default Validators
We can also define some default validators inside the ValidationService interface itself for e.g.
public interface ValidationService {
public boolean validate(Object obj);
public static final String PTRN_ONLY_ALPHA = "^[A-Za-z]+$";
public static final String PTRN_ALPHA_NUM = "^[A-Za-z0-9]+$";
public static final ValidationService DEF_VALIDATOR_ONLY_ALPHA = new ValidationService() {
@Override
public boolean validate(Object obj) {
return obj instanceof String? Pattern.compile(ValidationService.PTRN_ONLY_ALPHA).matcher(obj.toString()).find()
: false;
}
};
public static final ValidationService DEF_VALIDATOR_ALPHA_NUM = new ValidationService() {
@Override
public boolean validate(Object obj) {
return obj instanceof String? Pattern.compile(ValidationService.PTRN_ALPHA_NUM).matcher(obj.toString()).find()
: false;
}
};
}
So calling programs can use it directly without having to define a validator every time.
try{
scm.setSessionAttribute(name, value, ValidationService.DEF_VALIDATOR_ALPHA_NUM);
}catch(ValidationException ex){
..
}
AllowList Validation Example
public interface ValidationService {
public boolean validate(Object obj);
public static final List<String> vibgyorColorList = Arrays
.asList(new String[] { "Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet" });
public static final ValidationService VIBGYOR_VALIDATOR = new ValidationService() {
@Override
public boolean validate(Object obj) {
return obj instanceof String
? vibgyorColorList.stream().anyMatch(obj.toString()::equalsIgnoreCase)
: false;
}
};
Usage:
favouriteColor = request.getParameter(“user_color”);
// scm.setSessionAttribute("Color", favouriteColor);
try{
scm.setSessionAttribute("Color", favouriteColor, ValidationService.VIBGYOR_VALIDATOR);
}catch(ValidationException ex){
..
}
An example for CWE 601 URL Redirection
public class URLRedirection {
public static void redirect(String url) {
response.sendRedirect(url); // CWE 601
}
}
Usage:
urlredirection.redirect(url);
To ensure safe URL Redirection, we are required to validate the URL variable which is flagged as they were originating from untrusted sources (external sources)
We would maintain an allow-list containing all possible URL that are expected to be used by the application.
There is a high possibility that the whole URL will not match (URL with different context, page, or even query string attached) but what we can do is at least validate the beginning part of the URL is matching to one of the Allow-list’s URL.
public interface ValidationService {
public boolean validate(Object obj);
static List<String> safeRedirectURLsList = Arrays.asList(new String[]{
"https://www.example.com/ ",
"https://ext.trustedsite.com/ ",
"https://auth.idpsite.com/ ",
"https://int.example.com/ ",
"https://www.example-preprd.com/ "});
public static final ValidationService SAFE_URL_VALIDATOR = new ValidationService() {
@Override
public boolean validate(Object obj) {
return obj instanceof String
? safeRedirectURLsList.stream().anyMatch(obj.toString()::startsWith)
: false;
}
};
}
Approach 1:
public class URLRedirection {
public static void redirect(String url, ValidationService validator) {
if(validator.validate(url)) {
response.sendRedirect(url);
}else {
throw new ValidationException("The url is invalid");
}
}
}
Usage:
try {
urlredirection.redirect(url,ValidationService.SAFE_URL_VALIDATOR);
} catch (ValidationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
OR Approach 2:
public class URLRedirection {
public static void redirect(String url) {
if(ValidationService.SAFE_URL_VALIDATOR.validate(url)) {
response.sendRedirect(url);
}else {
throw new ValidationException("The url is invalid");
}
}
}
usage:
try {
urlredirection.redirect(url);
} catch (ValidationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The former allowed the business class to specify the Validator while the latter uses a default validator for all URL redirection.
CAUTION:
The paradigm comes with a limitation in that validation depends on what specific conditions are defined and implemented in the correct way.
The developer should NOT define “do nothing return true” implementations of this service. This would bypass the necessary validation required to achieve a goal (Mitigation of the risk associated.)
CAUTION:
The paradigm comes with a limitation in that validation depends on what specific conditions are defined and implemented in the correct way.
The developer should NOT define “do nothing return true” implementations of this service. This would bypass the necessary validation required to achieve a goal (Mitigation of the risk associated.)
ValidationService ALWAYS_GOOD = new ValidationService() {
@Override
public boolean validate(Object obj) {
return true;
}
};
Topics (2)
Related Articles
Grace Period in New Policy 971Number of Views Why is there discrepancy a Scan Size? 1.22KNumber of Views How to download only Dynamic scan result report only. 1.85KNumber of Views Is it possible to know what percentage of an application is scanned? 1.3KNumber of Views Report saying that not all modules were scanned 1.55KNumber of Views
This topic isn't available in this community.
Related Topics
Ask the Community
Get answers, share a use case, discuss your favorite features, or get input from the Community.
.png)