Validaciones de parámetros de entrada

Introducción

Una gran cantidad de vulnerabilidades (fácilmente sobre 70-80%) se explotan debido a la falta de validación de parámetros (o datos) de entrada, como por ejemplo un formulario de login, una casilla de búsqueda... Pero también puede suceder sobre parámetros aparentemente inofensivos pero que no se validan correctamente. Incluso se pueden realizar directamente sobre las APIs, por lo que la validación en el lado de cliente es insuficiente.

Los riesgos de estas vulnerabilidades son multiples (normalmente graves). Por ejemplo, un caso crítico sería la inyección de comandos  (véase https://owasp.org/www-community/attacks/Command_Injection ) lo que permitiría abrir una linea de comandos con el promt del usuario que ejecuta la aplicación.

Por ello recomendamos:

  • Filtrado de HTML malicioso.

  • Validar en el cliente el parámetro para no dejar fluir peticiones al servidor, como medida de mejorar el rendimiento del servidor. Adicionalmente se recomienda utilizar un anti-CSRF token.

  • Validar SIEMPRE en el lado del servidor los parámetros de entrada en el servidor.

¿Por que validar los parámetros si son parámetros tipados?

Es un error común pensar en que un dato tipado es un dato correctamente validado, como veremos, no es así.

Una cadena de caracteres sin validar puede conllevar multiples riesgos, como por ejemplos inyecciones de código, ejecución de comandos remotos, etc...

Por este motivo SIEMPRE se debe validar los datos de entrada y en casos necesarios escapar los carácteres conflictivos en su presentación.

Si lo deseas puede ver el siguiente video con un ejemplo de inyección SQL:

https://youtu.be/oLahd_ksX6c

Filtrado de HTML malicioso

Se recomienda en general que cualquier entrada se valide y se "limpie" para evitar la inyección de HTML en la capa vista, evitando así los Cross-site Scripting.

Se recomienda siempre

Las anotaciones de Spring u otros método de validación de parámetros no garantizan absolutamente que se valide correctamente el parámetro de entrada, por tanto, debemos aun así escapar las posibles inyecciones de HTML que hayan evadido este sistema.

Se recomienda hacerlo siempre.

A continuación dejamos las siguiente referencias:

https://docs.jboss.org/hibernate/validator/6.0/api/org/hibernate/validator/constraints/SafeHtml.html

https://jsoup.org/cookbook/cleaning-html/whitelist-sanitizer

https://github.com/owasp/java-html-sanitizer

Validación en Servidor

La validación en el servidor se debe realizar bajo las siguientes recomendaciones:

  1. Si se esta utilizando un framework que incluye librerías o métodos de validación, se recomienda utilizar estos. Para ello, se debe revisar la documentación del mismo para garantizar que se utiliza correctamente.

  2. Mediante librerías de validación de parámetros como ESAPI de OWASP.

  3. Mediante código en el momento de uso, para ello se puede validar mediante expresiones regulares.

Verifique que...

Entendemos que los 2 primeros métodos  son delegados en un tercero, por ello debemos verificar que la fuente es confiable y que hacemos un uso correcto del mismo

Uso de Framework

Los frameworks actuales suelen llevar incorporados métodos de validación, para ello debemos revisar la documentación y hacer un uso correcto de la misma.

Normalmente el funcionamiento es similar a validar nosotros el datos, pero utilizando el sistema de validación interno del framework.

Veamos un ejemplo simplificado:

En Java sin framework podríamos validar de esta manera

Clase "Employee " donde guardamos la contraseña

package com.javatpoint;      import javax.validation.constraints.Pattern;  public class Employee {          private String name;            private String pass;  // No existe validación en la clase             public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }      public String getPass() {          return pass;      }      public void setPass(String pass) {          this.pass = pass;      }  }

Clase "EmployeeController " es el controlador  de la clase "Employee "

package com.javatpoint;      import javax.validation.Valid;  import org.springframework.stereotype.Controller;  import org.springframework.ui.Model;  import org.springframework.validation.BindingResult;  import org.springframework.web.bind.annotation.ModelAttribute;  import org.springframework.web.bind.annotation.RequestMapping;      @Controller  public class EmployeeController {          private static final Pattern passwordPattern = Pattern.compile("^[a-zA-Z0-9]{3}");     @RequestMapping("/hello")      public String display(Model m)      {          m.addAttribute("emp", new Employee());          return "viewpage";      }      @RequestMapping("/helloagain")      public String submitForm(@ModelAttribute("emp") Employee employee , BindingResult br)      {      // Validamos del objeto empleado la contraseña     try {               if ( !passwordPattern .matcher(employee.getPassword()).matches()  {           throw new YourValidationException( "Improper password format." ); //Si es incorrecta la validación continua el flujo mediante excepción       }       // Si es correcta la validación continua el flujo normal de ejecución         if(br.hasErrors())          {              return "viewpage";          }          else          {          return "final";          }         } catch(YourValidationException e ) {             response.sendError( response.SC_BAD_REQUEST, e.getMessage() );         }               } 

 

En java mediante el sistema de anotaciones (el sistema interno de validación)

Clase "Employee " donde guardamos la contraseña

package com.javatpoint;      import javax.validation.constraints.Pattern;  public class Employee {          private String name;        @Pattern(regexp="^[a-zA-Z0-9]{3}",message="length must be 3")  // Configuramos la expresión regular mediante anotación en la clase     private String pass;              public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }      public String getPass() {          return pass;      }      public void setPass(String pass) {          this.pass = pass;      } 

Clase "EmployeeController " es el controlador  de la clase "Employee "

Uso de librerías externas

En este caso, si no utilizamos un framework podemos utilizar librerías especificas como ESAPI para la validación de parámetros de entrada.

Use librerías recomendadas en este Marco

Tenga especial atención en utilizar librerías recomendadas en el Marco de Seguridad, ya que las librerías de validación de fuente no confiables pueden contener vulnerabilidades o no validar de la manera adecuada. Esto implica añadir un riesgo al Producto pensando además que estamos mitigando otros problemas.

Ejemplo

Validación sin frameworks ni librerías

Tenga en cuenta que:

 

Ejemplo

 

¿Cómo validar correctamente los parámetros de entrada mediante Expresiones regulares?

Se debe revisar:

  1. El tipo de dato. Por ejemplo: Integer, Long, String, etc...

  2. Rango de datos. Por ejemplo: si es un entero que debe contener el precio de un producto, este NO debe ser negativo, por tanto el valor mínimo es 0.

  3. Formato del dato. Por ejemplo, en caso de esperar un email, la cadena de caracteres debe cumplir un formato de expresión regular del tipo: ^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$

  4. En casos especiales como por ejemplo las consultas a base de datos, se deben utilizar las consultas parametrizadas.

Crear una expresión regular

Si necesitas crear una expresión regular para tu desarrollo, primero verificar que no esta en este enlace. Si no estuviera y tienes que crearla de cero, te recomendamos estos enlaces:

https://regex101.com

https://www.debuggex.com

Ejemplo de una insuficiente validación de datos de entrada

En la siguientes imagenes podemos ver la explotación de un Cross-Site Scripting reflejado (XSS reflejado)

 

En este caso se introduce:

 

Envíamos la petición

 

Y vemos como se ha explotado con éxito el ataque.

 

En este caso no se esta validando correctamente el parámetro de entrada en el servidor y adicionalmente, tampoco se esta escapando en la presentación el código inyectado.

 

Validación de parámetros en ProtoBuf

Este protocolo de comunicación nos permite crear tipos de mensajes de una manera eficiente y rápida. Normalmente, desde el punto de vista funcional se definen los mensajes con el tipo deseado sin tener en cuenta que este protocolo tiene incorporado un sistema de validación.

Por ejemplo, podemos definir el siguiente mensaje:

La compilación del mismo:

Ejemplo extraído de la siguiente fuente: https://github.com/mwitkow/go-proto-validators

Conclusión

Como hemos visto anteriormente, es necesario que SIEMPRE, se validen los parámetros de entrada.  Para ello solo debemos verificar la existencia de algún sistema propio del lenguaje, framework, etc...

 

Referencias

https://owasp.org/www-community/OWASP_Validation_Regex_Repository

https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html