La seguridad es una prioridad fundamental en el proyecto Spring Boot Course. Este documento describe nuestras políticas de seguridad, cómo reportar vulnerabilidades y las mejores prácticas de seguridad implementadas en el proyecto.
- Versiones Soportadas
- Reporte de Vulnerabilidades
- Mejores Prácticas de Seguridad
- Seguridad en Dependencias
- Configuración Segura
- Seguridad en Desarrollo
- Seguridad en Producción
- Auditorías de Seguridad
Actualmente damos soporte de seguridad a las siguientes versiones del proyecto:
| Versión | Soportada | Estado |
|---|---|---|
| 0.0.x | ✅ | En desarrollo activo |
| < 0.0.1 | ❌ | OK Configuración |
| < 0.0.2 | ❌ | OK TDD |
Nota: Este es un proyecto educativo en desarrollo activo. Todas las versiones futuras recibirán actualizaciones de seguridad hasta que se marquen como obsoletas.
Si descubres una vulnerabilidad de seguridad en este proyecto, por favor repórtala de manera responsable:
- NO abras un issue público para vulnerabilidades de seguridad
- Envía un correo electrónico a: lgzarturo@gmail.com
- Incluye la siguiente información:
- Descripción detallada de la vulnerabilidad
- Pasos para reproducir el problema
- Versiones afectadas
- Impacto potencial
- Sugerencias de mitigación (si las tienes)
- Confirmación: Confirmaremos la recepción del reporte en un plazo de 48 horas
- Evaluación: Evaluaremos la vulnerabilidad y su impacto en un plazo de 7 días
- Corrección: Trabajaremos en una solución y la publicaremos según la
severidad:
- Crítica: 1-3 días
- Alta: 7-14 días
- Media: 14-30 días
- Baja: 30-90 días
- Divulgación: Una vez corregida, publicaremos un security advisory en GitHub
- Reconocimiento: Agregaremos tu nombre al security advisory (si lo deseas)
Este proyecto usa Spring Boot 3.5.6 que incluye mejoras de seguridad importantes:
- Jakarta EE 10: Migración completa a
jakarta.*namespace - Validación de Entrada: Uso de
spring-boot-starter-validationcon Bean Validation - Actuator Seguro: Endpoints de Actuator con configuración restringida
- CORS Configurado: Política CORS definida en
WebConfig - Exception Handling Global: Manejo centralizado de errores
- Spring Security con JWT para autenticación
- Autorización basada en roles (RBAC)
- Rate limiting para APIs
- CSRF protection
- Security headers (Content-Security-Policy, X-Frame-Options, etc.)
implementation("org.springframework.boot:spring-boot-starter-actuator")Riesgos:
- Exposición de información sensible del sistema
- Endpoints administrativos accesibles públicamente
Mitigación:
# src/main/resources/application.yaml
management:
endpoints:
web:
exposure:
include: health,info,metrics # Solo endpoints necesarios
endpoint:
health:
show-details: when-authorized # Detalles solo con autorizaciónRecomendaciones:
- ✅ Limitar endpoints expuestos en producción
- ✅ Proteger endpoints sensibles con autenticación
- ✅ Usar
management.server.portdiferente en producción - ✅ Habilitar solo los endpoints necesarios
runtimeOnly("com.h2database:h2")
runtimeOnly("org.postgresql:postgresql")H2 Console - Solo para Desarrollo:
spring:
h2:
console:
enabled: false # SIEMPRE false en producciónPostgreSQL - Producción:
- ✅ Usar variables de entorno para credenciales
- ✅ Nunca hardcodear contraseñas en el código
- ✅ Usar conexiones SSL/TLS
- ✅ Implementar connection pooling seguro
- ✅ Aplicar principio de mínimos privilegios en la base de datos
Ejemplo de Configuración Segura:
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 10
connection-timeout: 30000
jpa:
show-sql: false # false en producción
properties:
hibernate:
format_sql: false # false en producciónimplementation("org.springframework.boot:spring-boot-starter-data-jpa")Prevención de SQL Injection:
- ✅ Usar consultas parametrizadas (JPQL, Criteria API)
- ✅ Evitar construcción manual de SQL con concatenación de strings
- ✅ Validar y sanitizar todas las entradas de usuario
- ❌ NO usar
@Querycon concatenación de strings
Ejemplo Seguro:
// ✅ CORRECTO - Parámetro con nombre
@Query("SELECT r FROM Reserva r WHERE r.email = :email")
fun findByEmail(@Param("email") email: String): List<Reserva>
// ❌ INCORRECTO - Vulnerable a SQL Injection
@Query("SELECT r FROM Reserva r WHERE r.email = '${email}'")
fun findByEmailUnsafe(email: String): List<Reserva>implementation("org.springframework.boot:spring-boot-starter-validation")Uso de Bean Validation:
data class CreateReservaRequest(
@field:NotBlank(message = "El email es requerido")
@field:Email(message = "Email inválido")
val email: String,
@field:NotNull(message = "La fecha es requerida")
@field:Future(message = "La fecha debe ser futura")
val fecha: LocalDate,
@field:Positive(message = "El número de huéspedes debe ser positivo")
@field:Max(value = 10, message = "Máximo 10 huéspedes")
val numeroHuespedes: Int
)Recomendaciones:
- ✅ Validar TODOS los inputs del usuario
- ✅ Usar anotaciones de validación en DTOs
- ✅ Implementar validaciones custom cuando sea necesario
- ✅ Validar tamaño de arrays y colecciones
- ✅ Validar tipos de datos y rangos
implementation("io.sentry:sentry-spring-boot-starter-jakarta")Configuración Segura:
sentry:
dsn: ${SENTRY_DSN}
environment: ${SPRING_PROFILES_ACTIVE}
send-default-pii: false # NO enviar información personal
traces-sample-rate: 0.1 # 10% sampling en producciónPrevención de Fuga de Información:
- ✅ NO enviar PII (Personally Identifiable Information)
- ✅ Filtrar datos sensibles en logs
- ✅ Configurar
beforeSendpara sanitizar datos - ✅ Usar diferentes entornos (dev, staging, prod)
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")Prevención de Vulnerabilidades:
- ✅ Deshabilitar características peligrosas de deserialización
- ✅ Usar DTOs en lugar de entidades JPA directamente
- ✅ Validar JSON con esquemas
- ❌ NO exponer campos sensibles en respuestas
Configuración Segura:
@Configuration
class JacksonConfig {
@Bean
fun objectMapper(): ObjectMapper {
return ObjectMapper()
.registerModule(KotlinModule.Builder().build())
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NONE)
.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.PUBLIC_ONLY)
}
}developmentOnly("org.springframework.boot:spring-boot-devtools")Importante:
- ✅ DevTools se deshabilita automáticamente en producción
- ✅ Verificar que no esté en el classpath de producción
- ✅ NO incluir
developmentOnlydependencies en builds de producción
NUNCA incluir credenciales en el código fuente. Usar variables de entorno:
# application.yaml
spring:
datasource:
url: ${DB_URL:jdbc:h2:mem:testdb}
username: ${DB_USERNAME:sa}
password: ${DB_PASSWORD:}
sentry:
dsn: ${SENTRY_DSN:}
app:
jwt:
secret: ${JWT_SECRET}
expiration: ${JWT_EXPIRATION:3600000}Estructura Recomendada:
application.yaml # Configuración base
application-dev.yaml # Desarrollo (local)
application-test.yaml # Tests
application-prod.yaml # Producción (sin credenciales)
En .gitignore:
# Secrets y configuración local
application-local.yaml
application-secret.yaml
*.env
.env
.env.local
@Configuration
class WebConfig : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
.allowedOrigins(
System.getenv("ALLOWED_ORIGINS") ?: "http://localhost:3000"
)
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600)
}
}# Habilitar verificación de checksums
gradle wrapper --gradle-version 8.x --verification-mode lenientAgregar plugin para detectar vulnerabilidades:
// build.gradle.kts (futuro)
plugins {
id("org.owasp.dependencycheck") version "9.0.0"
}
dependencyCheck {
formats = listOf("HTML", "JSON")
failBuildOnCVSS = 7.0f
}# Ver dependencias desactualizadas
./gradlew dependencyUpdates
# Actualizar Gradle Wrapper
./gradlew wrapper --gradle-version latest// ✅ CORRECTO - Usar tipos no-nulos por defecto
fun processUser(email: String, name: String) {
// ...
}
// ❌ INCORRECTO - Evitar nulls innecesarios
fun processUser(email: String?, name: String?) {
email?.let { /* ... */ }
}// ✅ CORRECTO - Inmutable
data class User(
val id: Long,
val email: String,
val name: String
)
// ❌ INCORRECTO - Mutable
data class User(
var id: Long,
var email: String,
var name: String
)import org.springframework.web.util.HtmlUtils
fun sanitizeInput(input: String): String {
return HtmlUtils.htmlEscape(input.trim())
}import java.nio.file.Paths
fun isValidFilePath(path: String): Boolean {
val normalizedPath = Paths.get(path).normalize().toString()
return !normalizedPath.contains("..")
}// Con Bucket4j o similar
@RateLimiter(name = "api", fallbackMethod = "fallback")
@GetMapping("/api/resource")
fun getResource(): ResponseEntity<*> {
// ...
}- Environment Variables: Todas las credenciales en variables de entorno
- H2 Console: Deshabilitado (
spring.h2.console.enabled=false) - Debug Logging: Deshabilitado (
logging.level.root=WARN) - SQL Logging: Deshabilitado (
spring.jpa.show-sql=false) - Actuator Endpoints: Solo necesarios y protegidos
- HTTPS: Configurado con certificados válidos
- CORS: Restringido a dominios específicos
- Error Messages: No exponer stack traces completos
- Database: Credenciales seguras, conexión SSL
- Dependencies: Actualizadas sin vulnerabilidades conocidas
- DevTools: No incluido en producción
# application-prod.yaml
server:
port: 8443
ssl:
enabled: true
key-store: ${SSL_KEYSTORE_PATH}
key-store-password: ${SSL_KEYSTORE_PASSWORD}
key-store-type: PKCS12
error:
include-message: never
include-binding-errors: never
include-stacktrace: never
include-exception: false
spring:
h2:
console:
enabled: false
jpa:
show-sql: false
properties:
hibernate:
format_sql: false
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
management:
endpoints:
web:
exposure:
include: health,metrics
endpoint:
health:
show-details: never
logging:
level:
root: WARN
com.lgzarturo.springbootcourse: INFO- Sentry: Monitoreo de errores y excepciones
- Actuator + Prometheus: Métricas de aplicación
- Spring Boot Admin (futuro): Dashboard de monitoreo
- Log Aggregation (futuro): ELK Stack o similar
# Detekt - Análisis estático de Kotlin
./gradlew detekt
# KTLint - Estilo de código
./gradlew ktlintCheck# Gradle dependency verification
./gradlew dependencies --write-verification-metadata sha256
# OWASP Dependency Check (cuando se agregue)
./gradlew dependencyCheckAnalyze# Tests unitarios
./gradlew test
# Tests de integración
./gradlew integrationTest
# Cobertura
./gradlew jacocoTestReportTodos los cambios deben pasar por:
- Revisión de pares: Al menos un revisor
- CI/CD Checks: Tests, linters, análisis estático
- Security Review: Para cambios en configuración de seguridad
- Dependency Review: Para actualizaciones de dependencias
- CONTRIBUTING.md - Guía de contribución
- WORKFLOW.md - Flujo de trabajo de desarrollo
- ARCHITECTURE.md - Arquitectura del proyecto
Para reportar vulnerabilidades de seguridad:
Email: lgzarturo@gmail.com GitHub: @lgzarturo
Este proyecto está licenciado bajo CC-BY-4.0.
Última actualización: 2025-10-20
Recuerda: La seguridad es un proceso continuo, no un destino. Mantente actualizado con las últimas vulnerabilidades y mejores prácticas.
🔒 Security First, Always!