Skip to content
Xabi edited this page Apr 9, 2024 · 40 revisions

1 Introducción

Hdiv es una solución de seguridad para aplicaciones web y APIs que cuenta con dos productos: uno para detectar vulnerabilidades de seguridad en el software y otro que protege ese software para que los sistemas estén securizados y poder automatizar esa seguridad sin que dependa de la acción directa de las personas.

UDA, como conjunto de utilidades para el desarrollo de aplicaciones de Gobierno Vasco se ha integrado la solución de protección que propone Hdiv para mejorar estructuralmente el desarrollo seguro de las aplicaciones web y ofrece una librería de seguridad que integra x38 y Hdiv.

La integración de UDA y Hdiv a través de una librería parametriza el comportamiento de Hdiv y posibilita el uso de mecanismos durante el desarrollo de software. En los siguientes apartados se describen los pasos necesarios para la instalación y la descripción de cómo adoptar esos mecanismos.

2 Validaciones

La librería de Hdiv realiza tres tipos de validaciones:

  • Control de acceso
  • Validaciones de editables
  • Validaciones de integridad

La configuración de seguridad se podrá modificar desde la consola modificada teniendo esta prioridad sobre la definida en la propia aplicación.

2.1 Control de acceso

Se trata de determinar qué URLs quedan accesibles para el usuario. Por defecto ninguna URL será permitida a no ser que se habilite desde la parte servidora de la aplicación. Más adelante se muestra cómo habilitar los accesos.

2.2 Validación de editables

Se trata de validar los datos enviados en campos de texto. Existe una validación por defecto que podrá ser completada por el desarrollador.

2.3 Validación de integridad

Se trata de validar los datos enviados en campos de texto. Existe una validación por defecto que podrá ser completada por el desarrollador.

3 Instalación de la librería

La librería de Hdiv forma parte de la x38 desde la versión 5.0.0. Se cargarán las dependencias necesarias al hacer uso de la x38.

3.1 Importación de dependencias

La carga de la librería se realiza mediante la configuración en el fichero de dependencias pom.xml:

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<com.ejie.x38.version>5.4.1-RELEASE</com.ejie.x38.version>
	<com.ejie.hdiv.ce.version>5.1.0-RELEASE</com.ejie.hdiv.ce.version>
	<org.springframework.version>4.3.22.RELEASE</org.springframework.version>
	<org.springframework.security.version>4.2.11.RELEASE</org.springframework.security.version>
	<org.logback.version>1.2.11</org.logback.version>
	<org.slf4j.version>1.7.30</org.slf4j.version>
	<org.apache.tiles.version>3.0.8</org.apache.tiles.version>
	<org.jackson.version>2.7.9.5</org.jackson.version>
</properties>
<dependencies>
	<!-- Hdiv -->
	<dependency>
		<groupId>com.ejie.hdiv</groupId>
		<artifactId>hdiv-config</artifactId>
		<version>${com.ejie.hdiv.ce.version}</version>
	</dependency>
	<dependency>
		<groupId>com.ejie.hdiv</groupId>
		<artifactId>hdiv-core</artifactId>
		<version>${com.ejie.hdiv.ce.version}</version>
	</dependency>
	<dependency>
		<groupId>com.ejie.hdiv</groupId>
		<artifactId>hdiv-services</artifactId>
		<version>${com.ejie.hdiv.ce.version}</version>
	</dependency>
	<dependency>
		<groupId>com.ejie.hdiv</groupId>
		<artifactId>hdiv-spring-mvc</artifactId>
		<version>${com.ejie.hdiv.ce.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.hateoas</groupId>
		<artifactId>spring-hateoas</artifactId>
		<version>0.23.0.RELEASE</version>
	</dependency>
	...

3.2 Configurar filtro y listener

Añadir el listener y el filtro de Hdiv en el web.xml. El filtro debe ser el primero en registrarse. Para ello, se situará por delante del filtro de UDA:

<!-- Hdiv Init Listener -->
<listener>
	<listener-class>com.ejie.hdiv.listener.InitListener</listener-class>
</listener>
<!-- Hdiv Validator Filter -->
<filter>
	<filter-name>ValidatorFilter</filter-name>
	<filter-class>com.ejie.x38.hdiv.filter.EjieValidatorFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>ValidatorFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

También se ha de modificar el mvc-config.xml para que el component-scan encuentre el archivo de configuración:

<!-- Crea un bean por cada clase anotada con @Component en el paquete 'com.ejie.x21a' -->
<context:component-scan base-package="com.ejie.x21a" />

3.3 Configurar clase de parametrización

Activado el filtro y el listener de Hdiv, será necesario añadir una clase de configuración para parametrizar el comportamiento de la librería dentro de la aplicación. A su vez esta clase extenderá de la clase UDA4HdivConfigurerAdapter incluida en la librería x38 donde se particulariza la adaptación de Hdiv en los desarrollos con UDA. Un ejemplo básico sería el siguiente:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.ejie.x38.hdiv.aspect")
public class UDA4HdivConfig extends UDA4HdivConfigurerAdapter {

	@Override
	protected String getHomePage() {
		return "/";
	}

	@Override
	protected String getLoginPage() {
		return "/mockLoginPage";
	}
	
	@Override
	protected String getErrorPage() {
		return "/error";
	}
	
	@Override
	public void customConfigure(final SecurityConfigBuilder builder) {
	}

	@Override
	public void addCustomExclusions(final ExclusionRegistry registry) {
	}

	@Override
	public void addCustomRules(final RuleRegistry registry) {
	}

	@Override
	public void customConfigureEditableValidation(final ValidationConfigurer validationConfigurer) {
		// Upload PIF
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/upload/pifForm")
			.forParameters("base_url", "hadoop_folder_path")
			.rules("partialUrl"))
			.setAsClientParameter(true);
		
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/upload/pifForm")
			.forParameters("securityToken")
			.rules("text"))
			.setAsClientParameter(true);
		
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/upload/pifForm")
			.forParameters("hadoop_preserve_name")
			.rules("boolean"))
			.setAsClientParameter(true);
		
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/upload/pifForm")
			.forParameters("y31_ttl")
			.rules("numeric"))
			.setAsClientParameter(true);
		
		// Multifiltro
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/multiFilter")
			.forParameters("mapping", "tableID", "containerClass", "labelClass", "defaultContainerClass", "defaultCheckboxClass")
			.rules("text"))
			.setAsClientParameter(true);
			
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/multiFilter/getDefault")
			.forParameters("filterSelector", "user")
			.rules("text"))
			.setAsClientParameter(true);
			
		((EjieEditableValidationConfigurer) validationConfigurer
			.addValidation(".*/multiFilter/getAll")
			.forParameters("filterSelector", "user")
			.rules("text"))
			.setAsClientParameter(true);
	}
}

Más adelante en el punto Configuración se explicará con más detalle la configuración de la librería.

En el método getHomePage se establecerá la URL por la que se accede a la aplicación.

En getLoginPage se especifica la URL de la página de login.

Cuando sea necesario ajustar la configuración interna puede hacerse mediante customConfigure.

Para añadir exclusiones de validación se hará uso del método addCustomExclusions.

Si se necesitan crear nuevas reglas de validación, se hará uso del método addCustomRules. Aquí se especificará un nombre de validación y los patrones aceptados y/o rechazados.

Un ejemplo de validación en la que se aceptan únicamente valores numéricos sería:

registry.addRule("numerico").acceptedPattern("^[0-9]+$");

Para asignar las reglas existentes a URLs o parámetros se hará uso de customConfigureEditableValidation.

Un ejemplo de asignación de la regla de valores booleanos sería el siguiente:

((EjieEditableValidationConfigurer) validationConfigurer
	.addValidation(".*/multiFilter/getAll")
	.forParameters("filterSelector", "user")
	.rules("text"))
	.setAsClientParameter(true);

El atributo CLIENT_PARAMETERS se refiere a parámetros creados directamente en el cliente sin ser enviados desde el servidor (JavaScript).

Cambiar el archivo mvc-config.xml para que el base scan encuentre el archivo de configuración.

4 Mecanismos de protección

A continuación, se detallan los mecanismos de protección que habilita la librería de Hdiv y que habrá que tener en cuenta en el desarrollo de la aplicación.

4.1 Habilitar acceso

Para permitir el acceso a URLs será necesario que estas estén creadas mediante el tag de Spring spring:url o permitidas desde el controlador. El método aconsejado es mediante el tag de Spring, siendo la habilitación desde el controlador el método utilizado para URLs creadas en cliente (JavaScript).

  • Ejemplo caso 1 (recomendado):

     <spring:url value="/patrones/feedback" var="feedback" htmlEscape="true"/>
     <a class="dropdown-item" href="${feedback}">
     	<i class="mdi mdi-message-alert" aria-hidden="true"></i>
     	<spring:message code="feedback" />
     </a>
  • Ejemplo caso 2:

     @UDALink(name = "getAllIds")
     @GetMapping(value = "/allIds")
     public @ResponseBody List<Resource<Usuario>> getAllIds(
     		@RequestParam(value = "q", required = true) String param,
     		@RequestParam(value = "c", required = true) boolean startsWith) {
     	TableUsuarioController.logger.info("[GET - find_ALL_ID] : Obtener CPs de Usuario");
     	Usuario usuario = new Usuario(param);
     	return ResourceUtils.fromListToResource(this.tableUsuarioService.findAllIds(usuario, startsWith));
     }

Las peticiones realizadas a URLs no generadas de las siguientes maneras serán bloqueadas.

4.1.1 Spring:url

Esta forma de generar las URLs hace que el servidor sea el encargado de crearlas y de registrarlas. En las URLs se añadirá un parámetro de seguridad encargado de identificar el enlace.

Incorrecta:

<a class="nav-link" href="/appContext/url">

Adecuada:

<spring:url value="/url" var="url"/>
<a class="nav-link" href=${url}>

Las URLs que coinciden con el siguiente listado están excluidas de validación en la configuración de la librería, y por lo tanto no requieren del tag de Spring: /scripts/.*, /styles/.*, /fonts/.*, /error, /*.gif

4.1.2 Link allower

Se ha implementado una herramienta que permite habilitar URLs desde el controlador: @UDALink.

Esta herramienta se utiliza en forma de anotación. Consta de dos partes, identificación del link y accesos permitidos. Al acceder al método del controlador con esta anotación, se habilitará el acceso a los links definidos en linkTo. Por ejemplo, un método anotado de la siguiente manera permite el acceso a las peticiones de los métodos marcados como UDALink edit y remove:

@UDALink(name = "get", linkTo = {
	@UDALinkAllower(name = "edit" ), 
	@UDALinkAllower(name = "remove" )
})

Una condición para el uso de esta anotación, es que en el caso de existir path variables en el mapping de los métodos a habilitar, el nombre de las path variables debe coincidir con el nombre de atributo del objeto enviado en la response.

Solo se permitirá el acceso a los métodos que coincida esta regla, es decir, siguiendo con el ejemplo anterior no se podrá editar o eliminar ningún objeto que no haya sido devuelto en el método con nombre get:

@UDALink(name = "remove")
@DeleteMapping(value = "/{id}")

El atributo id debe existir en el objeto devuelto por el método get.

Si el método al que se quiere dar acceso se encuentra en otro controlador, se hara uso de el atributo linkClass de la anotación UDALinkAllower dentro de UDALink:

@UDALink(name = "get", linkTo = {
	@UDALinkAllower(name = "edit", linkClass = CustomOtherController.class)
})

Hay casos en los que no todas las entidades devueltas por el método del controlador deben tener acceso a los links anotados. Para discriminar parte de estos recursos, se hará uso del atributo allower:

@UDALink(name = "get", linkTo = {
	@UDALinkAllower(name = "edit", allower = CustomController.class)
})

Este atributo define la clase en la que se encuentra el predicado con la lógica de clasificación. El predicado debe ser un método público y estático que devuelva un objeto de tipo LinkPredicate<Tipo_De_Entidad> con la implementación del método test. El método test recibe el objeto LinkInfo como parámetro:

public class LinkInfo<T> {
	private final String linkId;
	private final T entity;
	private final HttpServletRequest request;
}

A continuación se muestra un ejemplo de implementación:

public static LinkPredicate<Entidad> getEntidadLinkPredicate() {
	return new LinkPredicate<Entidad>() {
		@Override
		public boolean test(LinkInfo<Entidad> linkInfo) {
			return !linkInfo.getEntity().isClosed();
		}
	};
}

4.2 Envío de datos

El envío de datos al servidor será de dos tipos:

  • Formularios con tags de Spring
  • Peticiones tipo REST

4.2.1 Formularios con tags de Spring

Los formularios deben hacer uso de los tags de Spring y definir el campo action:

<spring:url value="/url" var="url"/>
<form:form id="form_id" action="${url}">

Es importante incluir el taglib de x38-form:

<%@ taglib prefix="form" uri="/WEB-INF/tld/x38-form.tld"%>

Los datos estáticos de los formularios como combos, radios, checkbox, etc., se deberán cargar en tiempo de renderizado. Lo más común es emplear el modelo para añadir estos datos como atributos:

Controller

Map<String,String> att = new LinkedHashMap<String,String>();
att.put("value1", "value1Name");
att.put("value2", "value2Name");
model.addAttribute("attname", att);

JSP

<form:select path="name" id="estado_filter_table" class="rup-combo" items="${attname}"/>

Controller

model.addAttribute("attname2", att2ValueList);

JSP

<form:select path="name2" class="rup-combo">
	<form:option value="">&nbsp;</form:option>
	<form:options items="${attname2}" itemLabel="desc" itemValue="key"/>
</form:select>

Es importante comprender que los datos permitidos en un formulario están asociados al action del mismo. Si se modifica la URL del formulario, esta no corresponderá con los parámetros permitidos y la petición será bloqueada.

La librería considera que los campos definidos como hidden no son editables, con lo que si estos sufren modificaciones (su valor no corresponde al del momento de renderizado del formulario), la petición será bloqueada.

Si hay campos ocultos en los que las modificaciones están permitidas, estos deberán ser inputs ocultos:

<form:input path="parameter" style="display: none"/>

4.2.2 Envío de entidades tipo REST

Las entidades deberán ser de tipo SecureIdContainer o SecureIdentifiable y siempre serán representadas por un identificador único. Este identificador viajará cifrado y se proporcionará el valor original en el campo con nombre nid.

SecureIdentifiable: En caso de que la entidad posea un atributo llamado id o requiera de clave compuesta. Se crearán los métodos getId y setId.

SecureIdContainer: Si la entidad tiene un atributo identificativo (código cuyo nombre de atributo es distinto de id) o si el objeto no es una entidad en sí misma, pero contiene una, esta implementará SecureIdentifiable. Para conocer el atributo identificador, se anotará con @TrustAssertion(idFor = EntidadActual.class).

Un ejemplo de SecureIdContainer.

Clase entidad:

public class Entidad implements java.io.Serializable, SecureIdContainer {
	@TrustAssertion(idFor = Entidad.class)
	private Integer codEntidad;
}

Clase que no es una entidad en sí, pero hace uso de una:

public class EntidadDAO implements java.io.Serializable, SecureIdContainer {
	@TrustAssertion(idFor = Entidad.class)
	private Integer codEntidad;
}

Al enviar las entidades a cliente, se harán en forma de Resource.

new Resource<Entidad>(entidad);
public @ResponseBody Resource<Entidad> get(@PathVariable @TrustAssertion(idFor = Entidad.class) String id)
public @ResponseBody TableResourceResponseDto<Entidad> filter()

La librería x38 hace uso de la clase TableResourceResponseDto para enviar las entidades como Resources y esta clase es compatible con el componente tabla.

4.2.3 Permitir subentities

Si una entidad tiene subentidades y queremos que todas las claves vayan cifradas, deberemos añadir el atributo allowSubEntities=true:

@UDALink(name = "get", linkTo = { 
	@UDALinkAllower(name = "edit", allowSubEntities=true),
	@UDALinkAllower(name = "remove"), 
	@UDALinkAllower(name = "getProvincias", linkClass=TableComarcaController.class, allowSubEntities=true), 
	@UDALinkAllower(name = "getApellidos", linkClass=TableUsuarioController.class),
	@UDALinkAllower(name = "filter")
})
@GetMapping(value = "/{id}")
public @ResponseBody Resource<X21aAlumno> get(@PathVariable @TrustAssertion(idFor = Entidad.class) BigDecimal id) {
	X21aAlumno x21aAlumno = new X21aAlumno();
	x21aAlumno.setId(id);
	x21aAlumno = this.x21aAlumnoService.find(x21aAlumno);
	TableX21aAlumnoController.logger.info("[GET - findBy_PK] : Obtener X21aAlumno por PK");
	return new Resource<X21aAlumno>(x21aAlumno);
}

4.2.4 Cifrar en el model

Las claves primarias de una clave con Hdiv pueden ir cifradas desde el controlador a través del ModelAndView de Spring, a continuación se muestra un ejemplo:

//caso1
model.addAttribute("bandeja", bandeja);

//caso2
model.addAttribute("bandejaCifrada", new IdentifiableModelWrapperImpl<Bandeja>(bandejaCifrada, "codBandeja"));

En el primer caso se mandan los datos como siempre, solo que la entidad no va cifrada, en el segundo caso la entidad iría cifrada, para ello la definición es la siguiente, el nombre de la variable, el WrapperImpl con el tipo de tu propia clase, la variable y por último el nombre del campo clave de tu clase. A la hora de mostrarlo en la JSP quedaría así:

<!-- caso 1 -->
<form:form modelAttribute="bandeja" id="bandeja_filter_form" action="${url}" method="POST">
	<form:hidden path="codBandeja" id="codBandeja">
</form:form>

<!-- caso 2 -->
<form:form modelAttribute="bandejaCifrada" id="bandejaCifrada_filter_form" action="${url}" method="POST">
	<form:hidden path="codBandeja" id="codBandejaCifrada" value="${bandejaCifrada.entity.codBandeja}">
</form:form>

En el caso 1, haremos el populate de todos los los datos de forma convencional, por ejemplo, informando al modelAtribute.

En el caso 2, para poder usar la clave primaria, para mostrar o para luego reutilizarla, invocaremos a la variable ${bandejaCifrada.entity.codBandeja} o ${bandejaCifrada.id}.

4.3 Path variables

Las URLs de peticiones que hagan uso de path variables deberán cumplir con el siguiente patrón:

  • La última variable corresponderá al identificador de la entidad.
  • Las variables en la URL que correspondan a la entidad y no estén modificados deberán tener el mismo nombre que el atributo de la propiedad.
  • Se recomienda que las variables estáticas de la URL sean una enumeración para controlar el valor de los mismos. Por ejemplo una variable con nombre estado cuyos valores se esperan entre activo e inactivo, se recomienda que sea un enumerado al menos en el path.
  • Es necesario anotar también con @TrustAssertion(idFor = Entidad.class).

Por ejemplo:

@PathVariable @TrustAssertion(idFor = Entidad.class) String id

En caso de que el valor de una variable no quiera ser validado por integridad, se definirá un idFor = NoEntity.class en la anotación @TrustAssertion del path variable:

@PathVariable @TrustAssertion(idFor = NoEntity.class) Integer value

5 Configuración

La librería ofrece la posibilidad de añadir configuración a la aportada por defecto. Las validaciones se configurarán desde la clase de configuración que extiende de UDA4HdivConfigurerAdapter.

A continuación, se exponen las opciones de configuración:

5.1 Url exclusions - Start Pages

Las URLs añadidas como urlexclusions no serán validadas. Se establecerá la URL (puede contener expresiones regulares) y se podrá especificar el verbo del método HTTP al que aplica.

@Override
public void addCustomExclusions(final ExclusionRegistry registry) {
	registry.addUrlExclusions("/url.html").method("GET");
}

Como parte especial de URLs excluídas de validación se encuentran las URLs de entrada a la aplicación y login. Estas se configuran de la siguiente manera:

@Override
protected String getHomePage() {
	return "/";
}

@Override
protected String getLoginPage() {
	return "/loginPage";
}

5.2 Text fields validation

Se permite especificar la validación de valor de parámetro por nombre de parámetro y URL. Esta regla se compone de la URL en la que se aplica la validación, el patrón aceptado, el rechazado y el nombre que se da a la regla.

En la regla se podrá especificar el patrón aceptado y/o el rechazado, por ejemplo:

@Override
public void configure(SecurityConfigBuilder builder) {
	builder.textFieldIntegration(TextFieldIntegration.LIBRARY_ONLY);
}

5.2.1 AcceptedPattern

Patrón de valores aceptados en el parámetro que se compone de expresiones regulares. Por defecto UDA aplica los siguientes:

@Override
public final void addRules(final RuleRegistry registry) {
	registry.addRule("numeric").acceptedPattern("^[0-9]+$");
	registry.addRule("text").acceptedPattern("[a-zA-Z0-9@.\\-_~]*$").rejectedPattern("(\\s|\\S)*(--)(\\s|\\S)*");
	registry.addRule("textWhitespaces").acceptedPattern("[a-zA-Z0-9@.\\-_~ ]*$").rejectedPattern("(\\s|\\S)*(--)(\\s|\\S)*");
	registry.addRule("fulltext").acceptedPattern("[a-zA-Z0-9@.,\\-_\\:~\\$]*$").rejectedPattern("(\\s|\\S)*(--)(\\s|\\S)*");
	registry.addRule("fulltextWhitespaces").acceptedPattern("[a-zA-Z0-9@.,\\-_\\:~ ]*$").rejectedPattern("(\\s|\\S)*(--)(\\s|\\S)*");
	registry.addRule("valueList").acceptedPattern("\\[([^()])*\\]");
	registry.addRule("url").acceptedPattern("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
	registry.addRule("partialUrl").acceptedPattern("[a-zA-Z0-9@.\\-_\\/]*$").rejectedPattern("(\\s|\\S)*(--)(\\s|\\S)*");
	registry.addRule("boolean").acceptedPattern("(\\W|^)(true|false)(\\W|$)");
	registry.addRule("order").acceptedPattern("(\\W|^)(asc|desc| ,asc| ,desc)*(\\W|$)");
	registry.addRule("locale").acceptedPattern("(\\W|^)(es|eu|en|fr)(\\W|$)");
	registry.addRule("method").acceptedPattern("(\\W|^)(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH)(\\W|$)");

	//Custom rule to be used in 'modify' requests
	registry.addRule(Constants.MODIFY_RULE_NAME).rejectedPattern("-");

	addCustomRules(registry);
}

5.2.2 RejectedPattern

Patrón de valores rechazados en el parámetro que se compone de expresiones regulares.

A continuación, se muestra un ejemplo de regla de validación de editables:

@Override
public void addCustomRules(final RuleRegistry registry)
	registry.addRule("customValidation").acceptedPattern("^[a-zA-Z0-9@.\\-_]*$");
}

@Override
public void customConfigureEditableValidation(final ValidationConfigurer validationConfigurer){
	((EjieEditableValidationConfigurer) validationConfigurer
		.addValidation("/tableLocalidad/editForm")
		.forParameters("pkValueIdPadre")
		.rules("fulltext"))
		.setAsClientParameter(true);
}

5.3 Parameter exclusions - Start Parameters (no recomendado)

Al igual que se ofrece una opción para evitar la validación por URLs, se permite excluir las validaciones de parámetros en concreto. Estas exclusiones pueden especificarse por URL siguiendo una expresión regular.

A continuación se muestra un ejemplo de configuración para la exclusión del parámetro paramName para todas las URLs que empiezan por section/:

@Override
public void addExclusions(ExclusionRegistry registry) {
	registry.addParamExclusions("paramName").forUrls("/section/.*");
}

5.4 Parámetros generados en cliente

En ocasiones, hay parámetros que son generados en el cliente y por lo tanto, desconocidos para la parte servidora. Una petición con este tipo de parámetros sería bloqueada por la librería por tratarse de datos no autorizados por el servidor. Si es necesario que estos parámetros sigan siendo creados en la parte cliente (JavaScript, HTML) se deberá añadir una validación indicando que se tratan de parámetros creados en el cliente y son esperados por el servidor.

Esta configuración se hará de las siguientes maneras:

  • Parámetro generado en cliente con validación por defecto:

     @Override
     public void customConfigureEditableValidation(final ValidationConfigurer validationConfigurer){
     	((EjieEditableValidationConfigurer) validationConfigurer
     		.addValidation("/.*")
     		.forParameters("paramName"))
     		.setAsClientParameter(true);
     }
  • Parámetro generado en cliente con validación personalizada:

     @Override
     public void customConfigureEditableValidation(final ValidationConfigurer validationConfigurer){
     	((EjieEditableValidationConfigurer) validationConfigurer
     		.addValidation("/.*")
     		.forParameters("paramName")
     		.rules("customValidation"))
     		.setAsClientParameter(true);
     		.disableDefaults();
     }

5.5 Excepciones propagadas

Para el manejo de excepciones personalizadas se puede usar un hander propio, para ello la definición en el mvc-config.xml quedaría de esta forma:

<!-- Configurar Excepciones propagadas en los Controller -->
<bean class="com.ejie.x38.hdiv.config.WebConfig">
	<property name="handlers">
		<list>
			<bean class="com.ejie.x21a.handler.X21aExceptionHandler" />
		</list>
	</property>
</bean>

5.6 Peticiones multipart

Para la subida de ficheros, hay que definir en el mvc-config.xml el siguiente bean o uno personalizado que extienda de este:

<!-- Permite la subida de ficheros -->
<bean id="multipartResolver" class="com.ejie.hdiv.web.multipart.HdivCommonsMultipartResolver" />

6 Modificación de respuesta

El objeto de respuesta que se usa en las tablas ha cambiado y ahora ya no se puede devolver un objeto, sino un Resource del objeto, en el caso del siguiente ejemplo, es de la entidad Usuario:

@UDALink(name = "edit", linkTo = { @UDALinkAllower(name = "filter") })
@PutMapping(value = "/edit")
public @ResponseBody Resource<Usuario> edit(@RequestBody Usuario usuario) {
	Usuario usuarioAux = this.tableUsuarioService.update(usuario);
	logger.info("Entity correctly updated!");
	return new Resource<Usuario>(usuarioAux);
}

7 Mensajes de control (errores más comunes)

Cuando Hdiv bloquea una petición, lo justifica devolviendo en la respuesta un mensaje con el motivo. A continuación, se informa acerca de los errores más frecuentes:

7.1 Unauthorized access: INVALID_PAGE_ID

Unauthorized access: INVALID_PAGE_ID

Este error sucede porque se define una URL sin el tag de Spring URL.

7.2 Unauthorized access: INVALID_PARAMETER_NAME

Cuando el formulario contiene un campo que no existe en la entidad o un campo que no haya sido renderizado en la JSP:

Unauthorized access: INVALID_PARAMETER_NAME

7.3 Unauthorized access: INVALID_PARAMETER_VALUE

Cuando se intenta añadir desde el cliente un campo nuevo en un formulario:

Unauthorized access: INVALID_PARAMETER_VALUE

Si no está definido en la entidad como atributo o si estando definido en la entidad, no aparece declarado de forma explícita en el formulario, Hdiv bloquearía las peticiones.

7.4 Unauthorized access: INVALID_HDIV_PARAMETER_VALUE

Si el argumento HDIV_STATE ha sido modificado o se ha perdido:

Unauthorized access: INVALID_HDIV_PARAMETER_VALUE

7.5 Unauthorized access: INVALID_HID_VALUE

Si el identificador de una entidad es editado, este es el error que devolverá Hdiv:

Unauthorized access: INVALID_HID_VALUE

7.6 Unauthorized access: INVALID_BODY_FORMAT

Se produce cuando se detecta una malformación en el body (JSON) y como consecuencia, ocurre una excepción en la validación:

Unauthorized access: INVALID_BODY_FORMAT

7.7 Unauthorized access: URL not allowed

Si la URL no ha sido declarada como permitida desde el controlador:

Unauthorized access: URL not allowed

8 Cambios importantes

Hasta ahora, había sido una práctica común generar campos en el cliente que posteriormente se enviarían al servidor o incluso generar y editar identificadores de los registros. Con la inclusión de la librería de Hdiv, todas estas prácticas quedan prohibidas y serán bloqueadas en caso de ser llevadas a cabo.

Un cambio necesario para todas las aplicaciones que hasta ahora habían permitido la creación o modificación de identificadores desde el cliente, es la creación de secuencias en base de datos para que la inserción de registros puede ser llevada a cabo correctamente desde el DAO. Por ejemplo, la siguiente secuencia es utilizada en la x21a:

CREATE SEQUENCE "X21B"."USUARIO_SEQ" MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOPARTITION;

Un ejemplo de la inserción de identificadores a partir de una secuencia usada en la x21a:

public Usuario add(Usuario usuario) {
	// Obtenemos el identificador de la entidad mediante una secuencia
	final String nextId = jdbcTemplate.queryForObject("SELECT USUARIO_SEQ.NEXTVAL FROM DUAL", String.class);
	usuario.setId(nextId);

	String query = "INSERT INTO USUARIO (ID, NOMBRE, APELLIDO1, APELLIDO2, EJIE, FECHA_ALTA, FECHA_BAJA, ROL, FECHA_MODIF) VALUES (?,?,?,?,?,?,?,?,sysdate)";
	this.jdbcTemplate.update(query, usuario.getId(), usuario.getNombre(), usuario.getApellido1(), usuario.getApellido2(), usuario.getEjie(), usuario.getFechaAlta(), usuario.getFechaBaja(), usuario.getRol());
	return usuario;
}

9 Desactivación

Es posible desactivar la protección de la librería, para ello, es necesario realizar los siguientes cambios:

  • Eliminar la clase de configuración Uda4HdivConfig.
  • Eliminar Hdiv Init Listener y Hdiv Validator Filter del web.xml.
  • Sustituir bean com.ejie.x38.hdiv.config.WebConfig por com.ejie.x38.control.exception.MvcExceptionResolverConfig en el mvc-config.xml.
  • Sustituir clase del bean customSerializer por com.ejie.x38.serialization.CustomNidSerializer.
  • Sustituir clase del bean secureSerializerModule por com.ejie.x38.hdiv.serialization.EjieNidModule.
  • En jackson-config.xml añadir dentro del bean udaMappingJackson2HttpMessageConverter la propiedad extraModules con una lista que contenga la referencia al bean secureSerializerModule:
    <bean id="udaMappingJackson2HttpMessageConverter" class="com.ejie.x38.serialization.UdaMappingJackson2HttpMessageConverter">
    	<property name="supportedMediaTypes" ref="jacksonSupportedMediaTypes" />
    	<property name="udaModule" ref="udaModule" />
    	<property name="extraModules" >
    		<list>
    			<ref bean="secureSerializerModule" />
    		</list>
    	</property>
    </bean>
Clone this wiki locally