Validaciones de parámetros de entrada
- 1 Introducción
- 2 ¿Por que validar los parámetros si son parámetros tipados?
- 3 Filtrado de HTML malicioso
- 4 Validación en Servidor
- 5 ¿Cómo validar correctamente los parámetros de entrada mediante Expresiones regulares?
- 6 Crear una expresión regular
- 7 Ejemplo de una insuficiente validación de datos de entrada
- 8 Validación de parámetros en ProtoBuf
- 9 Referencias
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:
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:
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.
Mediante librerías de validación de parámetros como ESAPI de OWASP.
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:
El tipo de dato. Por ejemplo: Integer, Long, String, etc...
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.
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}$
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:
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