
Construye Software Seguro (implementación para Java)
Continuamos la serie de artículos sobre cómo construir software seguro (consulta el artículo anterior), donde definimos un conjunto de etapas estándar para que cualquier pieza de software se cree en la empresa con confianza y seguridad.
Este artículo muestra un ejemplo de implementación para componentes Java, con Maven como herramienta de build y GitLab como CI/CD.
El proyecto
Primero, partamos de un proyecto de pruebas creado con Spring Initializr:
https://github.com/albert0-hernandez/medium-ssdlc-spring-boot-secure
Echa un vistazo a la sección de configuración del build definida en el pom de Maven:
<plugin>
<groupId>org.openclover</groupId>
<artifactId>clover-maven-plugin</artifactId>
<!-- OMMITED-->
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<!-- OMMITED-->
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<!-- OMMITED-->
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<!-- OMMITED -->
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<!-- OMMITED -->
</plugin>
El proceso
Puedes ver que se incluyen varios plugins:
- PMD: una herramienta de pruebas estáticas de seguridad de aplicaciones (SAST)
- SpotBugs: una segunda herramienta de pruebas estáticas de seguridad de aplicaciones (SAST)
- Clover: una herramienta que genera informes de cobertura de pruebas
- Owasp: herramienta de análisis de composición de software (SCA)
- Enforcer: plugin de Maven para hacer cumplir reglas durante el build
En el mismo proyecto también puedes echar un vistazo al archivo .gitlab-ci.yml.
include:
- remote: 'https://raw.githubusercontent.com/albert0-hernandez/dev-utils/refs/heads/main/java/gitlab/.java-component.yml'
¿Esperabas ver ahí la definición completa del pipeline? ¡Vamos! Sigamos el principio DRY y externalicemos la definición del pipeline para reutilizarla en cada componente Java que necesitemos construir en nuestra organización.
Nota: hay distintas formas de enlazar la definición del pipeline según tus restricciones de seguridad. Consulta la sección de la documentación de GitLab
El pipeline
Ahora que la mesa está puesta, ejecutemos el pipeline y analicémoslo (ten en cuenta que GitLab llama stages a las fases).

1. Etapa de build
Aquí está la fase estándar de compilar el código Java. Nada menos, pero nada más. ¡Simplemente compilar el código fuente!
2. Etapa de lint de seguridad
Una vez compilado el código, en esta etapa es donde ocurre toda la magia del SSDLC. Usamos distintas herramientas para evaluar la seguridad.
Las tres herramientas se ejecutan en paralelo para optimizar el tiempo de build y para dar un feedback mejor y más claro cuando una herramienta detecta algún problema de seguridad que hay que resolver.
Primero, con el paso (job) de PMD ejecutamos un análisis estático de código con el que detectamos errores de forma temprana, mejoramos la calidad del código, facilitamos su mantenimiento y nos aseguramos de que cumple los estándares y las buenas prácticas. Recomiendo encarecidamente un conjunto de reglas PMD común para todos los proyectos, con el fin de normalizar el estándar en la organización. Así es como se define en el plugin, con el conjunto de reglas alojado en otro repositorio.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>${maven.plugins.pmd}</version>
<configuration>
<excludeRoots>
<excludeRoot>target/generated-sources</excludeRoot>
</excludeRoots>
<rulesets>
<ruleset>https://raw.githubusercontent.com/albert0-hernandez/dev-utils/refs/heads/main/java/pmd/pmd-ruleset.xml</ruleset>
</rulesets>
</configuration>
</plugin>
Puedes fijar en la definición del plugin una regla de umbral: si alguna de las dependencias actuales tiene una vulnerabilidad de 8 o más, el build fallará y romperá el pipeline.
También es posible establecer tus propios valores según tus estándares de seguridad, o incluso configurar supresiones cuando sea necesario.
Nota: lo expuesto es la configuración más básica de este plugin. En escenarios reales, descargar y recrear la base de datos NVD en cada build y aplicar después el análisis lleva demasiado tiempo. Prometo dedicar en un futuro próximo un artículo entero a configuraciones más avanzadas de este plugin.
3. Etapa de test
Una vez compilado y analizado el software, podemos pasar a probar el código. Llegamos a esta etapa sabiendo que todo el análisis de seguridad está completo, así que ahora toca comprobar que el código hace lo que debe hacer.
Ejecutar el plugin Clover en la fase de pruebas nos permite obtener durante el build informes y métricas rápidas para controlar el proyecto. También podemos definir un umbral de cobertura que el proyecto debe alcanzar.
Ten en cuenta que el equipo debería revisar los artefactos recogidos por GitLab con el informe de cobertura.

4. Etapa de deploy
Por último, la etapa final es el despliegue en tu repositorio interno usando el plugin estándar de Maven.
Si miras con más detalle la etapa de deploy, quizá se te haya escapado un pequeño detalle: incluso en la fase de Maven aplicamos una medida de seguridad rápida pero importante. El pipeline solo despliega las releases desde la rama main (ramas definidas).
deploy:
stage: deploy
script:
- VERSION=$(./mvnw $MAVEN_CLI_OPTS --quiet org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -DforceStdout)
- export phase=$(if [[ "$VERSION" == *SNAPSHOT ]]; then echo deploy; else echo install; fi)
- ./mvnw $MAVEN_CLI_OPTS clean $phase -DskipTests
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always
expire_in: 1 day
paths:
- target/app.war
- "*/target/app.war"
- target/batch.jar
- "*/target/batch.jar"
deploy-ga:
stage: deploy
only:
refs:
- main
except:
- schedules
script: ./mvnw $MAVEN_CLI_OPTS clean deploy package -DskipTests
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
when: always
expire_in: 1 day
paths:
- target/app.war
- "*/target/app.war"
- target/batch.jar
- "*/target/batch.jar"
Con esto nos aseguramos de que los componentes publicados se crean desde la rama main, donde las revisiones de merge validan ese código. (Sin duda deberían aplicarse también otras reglas, pero se asume que tu repositorio de releases tiene cierto nivel de madurez).
Conclusión:
Este artículo nos muestra que es sencillo y rápido crear componentes de software seguros, con un análisis continuo y rápido, sin ningún esfuerzo adicional para el equipo de desarrollo. En este caso, la definición de hecho (DOD) en términos de seguridad se cumple cuando el pipeline termina con éxito. Esto permite a los equipos y a las organizaciones desarrollar software de forma más proactiva, constante y eficiente. En el futuro podremos revisar cómo se empaqueta y despliega hoy este componente en producción y cómo proteger el resto de componentes.