Forced Validation Paradigm


Forced Validation Paradigm

(A) Forced Validation using Validation Service Interface

In this article, I will try to explain to you how to ensure validation is always forced by the caller of any given functionality.

Let’s consider a simple example.
Here, in order to reduce the redundancy of calling the same piece of code, the developer decides to write a function where code is written once but called by multiple references.

Assume an interface that has a method that receives a parameter and performs some critical action. 

We decided to design our application with a utility class that would take care of the aspect of executing this critical function.
Example:
//utility class
class Processor{ 
       public static void process(String str) {
              $AttackVectorFunction$(str);  // Potential Vulnerability 
       }
}

In the above code, $AttackVectorFunction$() is a placeholder for that critical function.
$AttackVectorFunction$() has the potential for different types of weakness.
The Attack Vector is where Veracode Static Analysis will report the flaw.

A few CWE scenarios for
$AttackVectorFunction$().
Please note that this article also applies to other CWEs and Veracode Static Analysis may also report these flaws on other methods.
CWEsAttack Vector in JavaAttack Vector in .Net
CWE 501 Trust Boundary ViolationHttpSession.setAttribute()Session.Add()
CWE 89 SQL InjectionStatement.executeQuery()SqlCommand.ExecuteNonQuery()
CWE 566 Authorization Bypass Through User-Controlled SQL Primary Keyorg.hibernate.Session.get()SqlCommand.ExecuteNonQuery()
CWE 601 URL Redirection to Untrusted SiteHttpServletResponse.sendRedirect(url);Response.Redirect()
CWE 470 Unsafe ReflectionClass.forName()Assembly.LoadFile(), Assembly.CreateInstance()

Some business logic implementations use the utility to make a call to $AttackVectorFunction$() indirectly.
class BusinessA{
       public void functionA1(String str) {
              // do something
              Processor.process(str);
       }
       public void functionA2(String str) {
              //do something
              Processor.process(str);
       }
}

class BusinessB{
       public void functionB1(String str) {
              // do something
              Processor.process(str);
       }
}
public class ProcessorDemo{
       public static void main (String args[]) {
              BusinessA a = new BusinessA();
              a.functionA1(args[0]);
              a.functionA2(args[1]);
              BusinessB b = new BusinessB();
              b.functionB1(args[2]);
       }
}
As it receives the ‘str’ string and calls the "$AttackVectorFunction$()" function without precautionary measures.
Untrusted data used as ‘str’ could be dangerous.

In order to mitigate the flaw, the general recommendation would be adding validation logic as below:
 public static void process(String str) {
       if(validate(str)){
                          $AttackVectorFunction$(str);  
           }
   }

Here, validate() is function that would take care of required validation before it makes a call to $AttackVectorFunction$()
--> Hence, the flaw is successfully mitigated.

However, it is getting invoked from multiple business logic modules, there is the possibility that validation may NOT be always the same.
For e.g. functionA1 needs different validation compared to that of functionB2().


Now in order to mitigate the flaw, we will have to add validation within those function calls and remove validate() logic from process() itself.
class Processor{
     public static void process(String str) {
		$AttackVectorFunction$(str);	
     }
}

class BusinessA {
	
  public void functionA1(String str) {
		// do something
	      if(validateA1(str)){
              Processor.process(str);
          }
	}
	
  public void functionA2(String str) {
		//do something 
        if(validateA2(str)){
              Processor.process(str);
            }
	  }
}

class BusinessB{
   public void functionB1(String str) {
		// do something
		   if(validateB1(str)){
              Processor.process(str);
               }
	   }
}
A good example is that $AttackVectorFunction$() can accept below valid inputs :
  1. An email address (for e.g. “abc@example.com” )
  2. An alphanumeric Request ID (for e.g. “AJS90056GH3” )
  3. A hyphen separated Serial Id(For e.g. “12001-TX23-7281”) 
Business logic would be :
class BusinessC {
    public void doProcessEmail(String email) {
		// do something
		if(validateEmail(email)){
                  Processor.process(email);
             }
      }

     public void doProcessRequest(String reqId) {
		// do something
		if(validateRequest(reqId)){
                   Processor.process(reqId);
             }
	 }

      public void doProcessSerialId(String serialId) {
		// do something
		if(validateSerialId(serialId)){
                   Processor.process(serialId);
              }
	 }
}

class Processor{
    public static void process(String str) {
		$AttackVectorFunction$(str);	
     }
}
The problem with this implementation $AttackVectorFunction$() is invoked assuming that a Pre-validation is done before calling the Processor.process() by the caller program.

But we may introduce a new business code OR modify the existing code which does not do required Pre-validation.
For example, in the below example, we will invoke the Processor.process() by passing the untrusted OrderId coming from untrusted sources.
Class BusinessD {
     public void doOrder(String orderId) {
            Processor.process(orderId);
        }
}
Where I forget to add validation of Order id, OR I may find it convenient to skip this validation.
In such case, via process()  --> $AttackVectorFunction$() would be invoked without the OrderId validation.
--> Implying that the vulnerability finding is valid.


It is critical as this 1 usage or reference to the process() without validation can bring several types of weaknesses in the software.


How to Fix?
--> using Validation Service Interface

Here, we like to introduce ValidationService as an interface that has a validate() abstract method.
public interface ValidationService {
       public boolean validate(Object obj);
}

The abstract method validate() will be enforced by its implementors.
Now I would modify the Processor method like this :-
class Processor{
    public static void process(Object obj, ValidationService validator) {
		if(validator.validate(obj)){
			$AttackVectorFunction$(obj);	
		}
    }
}

As we see ‘validator’ passed as an argument will ensure ‘validation’ of the given variable ‘str’ before using the variable for $AttackVectorFunction$(). This is how we could implement forceful validation on it.
This means the Business class should provide the ValidationService instance reference for every call now.
Example:
class BusinessC {

   public void doProcessEmail(String email) {
		      Processor.process(email , new EmailValidator());
	    }
   public void doProcessRequest(String reqId) {
              Processor.process(reqId , new RequestValidator());
        }	
}
In the above example, ‘EmailValidator’ and ‘RequestValidator’ are both implementations of ValidationService.
For instance;
public class EmailValidator implements ValidationService {
	@Override
	public boolean validate(Object obj) {
        if( obj instanceof String )
		{
		// check email string validity.
		}
	}
}
public class RequestValidator implements ValidationService {
	@Override
	public boolean validate(Object obj) {
        if( obj instanceof String )
		{
		// check requested string validity.
		}
	}

}
With this implementation we will be forced to provide ValidationService implementations in every call:
For example in class BusinessD:
Class BusinessD {
     public void doProcessOrder(String orderId) {
                Processor.process(orderId , new OrderValidator());
           }
}
where OrderValidator must be defined:
public class OrderValidator implements ValidationService {
	@Override
	public boolean validate(Object obj) {
        if( obj instanceof String )
		{
		// check orderid string validity.
		}
	}

}
 
Another alternative if we can instead of String , use POJO objects as arguments to doProcess methods()
class BusinessC {
        public void doProcessEmail(Email useremail) {
		      Processor.process(useremail);
	    }
       public void doProcessRequest(Request req) {
              Processor.process(req);
        }
}
Class BusinessD {
       public void doProcessOrder(Order order) {
              Processor.process(order);
        }
}
class Processor{
public static void process(Object obj) {

		if(obj instanceof Email &&	EmailValidator.getInstance().validate(obj)){
			$AttackVectorFunction$(obj);	
		}else if(obj instanceof Process &&  ProcessValidator.getInstance().validate(obj)){
			$AttackVectorFunction$(obj);	
		}else if(obj instanceof Order && OrderValidator.getInstance().validate(obj)){
			$AttackVectorFunction$(obj);	
		}else{
	            // other validations followed by $AttackVectorFunction$(obj) calls.
		}
   }
}
With the above approach, we keep the signature of Processor.process() to default.

Click Here to see more details on these validation strategies on Page 2 of this article.
Page 2, we will learn how to :
- Mitigate several CWEs ( CWE 501, CWE 601, CWE 566, etc. )
- Define default validators.
- Define AllowList-based validators.



 

Topics (7)

Related Articles

Related Topics

    Ask the Community

    Get answers, share a use case, discuss your favorite features, or get input from the Community.