Saltar al contenido principal

Evaluación y seguimiento de competencias en seguridad

No se puede mejorar lo que no se mide. Antes de lanzar la formación en seguridad, necesita entender el nivel de habilidad actual de su equipo. Después de la formación, necesita verificar que funcionó. La evaluación continua garantiza que las habilidades no se deterioren.

Este artículo cubre métodos prácticos para evaluar el conocimiento de seguridad de los desarrolladores: cuestionarios, ejercicios de revisión de código, competiciones CTF y construcción de mapas de competencias que visualizan los puntos fuertes y débiles del equipo.

Serie de tres partes

Esta es la Parte 2 de la serie de formación en seguridad para desarrolladores:

  1. Currículo y recursos — qué enseñar
  2. Evaluación y mapeo de competencias (este artículo) — cómo evaluar las habilidades
  3. Guía de implementación — cómo desplegar el programa

Por qué importa la evaluación

Identificar brechas antes de la formación. La formación genérica desperdicia tiempo en temas que la gente ya conoce. La evaluación revela lo que cada desarrollador realmente necesita.

Medir la efectividad de la formación. ¿Funcionó la formación? Compare las puntuaciones antes y después de la formación para saberlo con certeza.

Seguir la mejora a lo largo del tiempo. El conocimiento de seguridad se deteriora sin práctica. La evaluación regular detecta la regresión de habilidades de forma temprana.

Justificar la inversión. La dirección quiere saber si la formación está funcionando. La evaluación proporciona métricas concretas.

Motivar a los desarrolladores. Los niveles de competencia claros dan a los desarrolladores objetivos hacia los que trabajar. La mejora es visible y gratificante.

Métodos de evaluación

Método 1: Cuestionario de conocimientos de seguridad

El enfoque más simple. Cree preguntas de opción múltiple y respuesta corta que cubran los temas clave de seguridad.

Principios de diseño del cuestionario:

PrincipioPor qué importa
Relevante para el rolNo evalúe a los desarrolladores de backend sobre seguridad en iOS
Progresión de dificultadL1 (básico) → L2 (intermedio) → L3 (avanzado)
Enfoque prácticoEjemplos de código en lugar de definiciones
Sin preguntas trampaEvalúe el conocimiento, no la comprensión lectora
Explique las respuestasOportunidad de aprendizaje incluso durante la evaluación

Estructura de la evaluación:

Duración: 45-60 minutos. Formato: En línea, supervisado o por sistema de honor.

Secciones:

  1. Conceptos básicos de seguridad (10 preguntas) — todos los roles
  2. OWASP Top 10 (10 preguntas) — todos los roles
  3. Específico del lenguaje (10 preguntas) — según el rol
  4. Autenticación/Autorización (5 preguntas) — backend/fullstack
  5. Pruebas de seguridad (5 preguntas) — todos los roles

Puntuación: 90 %+ = L3 (Experto) · 70-89 % = L2 (Practicante) · 50-69 % = L1 (Consciente) · Menos del 50 % = Formación requerida

Método 2: Ejercicio de revisión de código seguro

Más práctico que los cuestionarios. Proporcione a los desarrolladores código con vulnerabilidades intencionales y pídale que encuentren los problemas.

Cómo funciona:

  1. Prepare muestras de código con 5-10 vulnerabilidades
  2. Establezca un límite de tiempo (30-45 minutos)
  3. Pida al desarrollador que identifique y explique los problemas
  4. Puntúe en función de las vulnerabilidades encontradas y la calidad de la explicación

Ejercicio de ejemplo:

# Code Review Exercise: Find the security issues
# Time limit: 30 minutes
# Identify all security vulnerabilities and suggest fixes

from flask import Flask, request, render_template_string
import sqlite3
import pickle
import os

app = Flask(__name__)
SECRET_KEY = "super_secret_key_12345" # Issue #1

@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']

conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# Issue #2
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)
user = cursor.fetchone()

if user:
return f"Welcome {username}!" # Issue #3
return "Invalid credentials"

@app.route('/profile/<user_id>')
def profile(user_id):
# Issue #4
user_data = open(f'profiles/{user_id}.json').read()
return user_data

@app.route('/search')
def search():
query = request.args.get('q', '')
# Issue #5
template = f"<h1>Results for: {query}</h1>"
return render_template_string(template)

@app.route('/load-session')
def load_session():
data = request.cookies.get('session_data')
# Issue #6
session = pickle.loads(bytes.fromhex(data))
return str(session)

@app.route('/execute')
def execute():
cmd = request.args.get('cmd')
# Issue #7
result = os.popen(cmd).read()
return result

if __name__ == '__main__':
app.run(debug=True) # Issue #8

Clave de respuestas:

#VulnerabilidadUbicaciónGravedad
1Secreto hardcodeadoLínea 8Alta
2Inyección SQLLínea 18Crítica
3XSS potencialLínea 21Media
4Path traversalLínea 26Alta
5Inyección de plantillas del lado del servidorLínea 32Crítica
6Deserialización inseguraLínea 38Crítica
7Inyección de comandosLínea 43Crítica
8Modo debug en producciónLínea 47Media

Puntuación:

  • Vulnerabilidad encontrada: 1 punto
  • Impacto explicado correctamente: +1 punto
  • Corrección válida sugerida: +1 punto
  • Puntuación máxima: 24 puntos

Método 3: Capture The Flag (CTF)

Evaluación gamificada donde los desarrolladores explotan aplicaciones vulnerables para encontrar «banderas» (cadenas ocultas).

Beneficios:

  • Atractivo y divertido
  • Evalúa habilidades prácticas
  • Fomenta el aprendizaje mediante la exploración
  • Puede ser en equipo para la colaboración

Plataformas CTF:

PlataformaMejor paraCosteEnlace
CTFdCTF personalizado auto-alojadoGratuitoctfd.io
OWASP Juice ShopDesafíos preconstruidosGratuitoowasp.org/www-project-juice-shop
HackTheBoxMáquinas realistasDe pagohackthebox.com
PicoCTFAmigable para principiantesGratuitopicoctf.org

Organizar un CTF interno:

Duración: 2-4 horas (puede distribuirse en días).

Configuración: Implemente OWASP Juice Shop o desafíos personalizados. Cree equipos de 2-3 personas, mezcle niveles de experiencia, prepare pistas para los equipos atascados.

Desafíos por dificultad:

  • Fácil (1 estrella): 5 desafíos — XSS básico, robots.txt
  • Medio (2-3 estrellas): 5 desafíos — SQLi, bypass de autenticación
  • Difícil (4-5 estrellas): 3 desafíos — deserialización, SSRF

Puntuación: Puntos basados en la dificultad. Bonus por resoluciones tempranas. Sin penalización por pistas: fomenta el aprendizaje sobre la competición.

Después del evento: Explicación de soluciones para todos los desafíos. Reconocimiento para los mejores equipos. Notas individuales de habilidades basadas en la participación.

Método 4: Ejercicio de inyección de vulnerabilidades

Evaluación realista: usted añade intencionalmente vulnerabilidades a un PR y verifica si los desarrolladores las detectan durante la revisión.

Cómo funciona:

  1. Cree una rama con vulnerabilidades intencionales
  2. Pida al desarrollador que revise el PR
  3. Rastree qué vulnerabilidades encuentran
  4. Proporcione retroalimentación sobre los problemas no detectados

Vulnerabilidades de ejemplo para inyectar:

# Intentional vulnerabilities for testing

# 1. SQL Injection (obvious)
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")

# 2. Command injection (less obvious)
os.system(f"convert {filename} output.pdf")

# 3. SSRF (requires understanding)
requests.get(user_provided_url)

# 4. Hardcoded secret
API_KEY = "sk_live_123456789"

# 5. Insecure deserialization
data = pickle.loads(user_input)

# 6. Path traversal
file_path = f"uploads/{user_filename}"
with open(file_path, 'r') as f:
content = f.read()

Creación de preguntas para cuestionarios

Uso de IA para generar preguntas

Puede usar asistentes de IA para ayudar a crear preguntas de evaluación. Aquí tiene una plantilla de prompt:

Create 5 multiple-choice security questions for [ROLE] developers 
working with [TECHNOLOGY STACK].

Requirements:
- Include code examples where applicable
- Vary difficulty (2 L1, 2 L2, 1 L3)
- Focus on [SPECIFIC TOPIC: e.g., SQL injection, authentication]
- Provide correct answer and explanation
- Make wrong answers plausible but clearly incorrect

Format each question as:
**Q[N] ([Level]):** Question text
[Code if applicable]
a) Option A
b) Option B
c) Option C
d) Option D

**Answer:** [Letter]
**Explanation:** [Why this is correct and others are wrong]

Preguntas de ejemplo por tema

Inyección SQL (backend, datos)

P1 (L1): ¿Cuál de estos es vulnerable a inyección SQL?

# Option A
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

# Option B
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")

# Option C
User.objects.get(id=user_id)

a) Solo A b) Solo B c) A y B d) Todos

Respuesta: b Explicación: La opción B usa interpolación con f-string, incrustando directamente la entrada del usuario en la consulta. La opción A usa consultas parametrizadas (segura). La opción C usa el ORM de Django (segura).


P2 (L2): ¿Es este código seguro contra la inyección SQL?

if user_id.isdigit():
User.objects.raw(f"SELECT * FROM users WHERE id = {user_id}")[0]

a) Sí, sigue siendo vulnerable a inyección SQL b) No, la validación isdigit() lo hace seguro c) No, el ORM de Django previene la inyección SQL d) Depende del tipo de base de datos

Respuesta: b (pero discuta los casos límite) Explicación: isdigit() garantiza que solo haya dígitos, haciendo imposible la inyección. Sin embargo, este patrón es frágil: mejor usar consultas parametrizadas como defensa en profundidad. raw() con f-strings es un indicador de código problemático.


P3 (L3): Revise esta consulta parametrizada. ¿Es segura?

table_name = request.args.get('table')
cursor.execute(f"SELECT * FROM {table_name} WHERE id = %s", (user_id,))

a) Sí, las consultas parametrizadas previenen la inyección SQL b) No, el nombre de la tabla sigue siendo inyectable c) Sí, el marcador de posición protege todas las entradas d) Depende del driver de la base de datos

Respuesta: b Explicación: La parametrización solo funciona para valores, no para identificadores (nombres de tabla/columna). El table_name sigue siendo vulnerable. Solución: use una lista blanca de nombres de tabla permitidos.

XSS (frontend, backend)

P1 (L1): ¿Qué significa XSS? a) Cross-Site Scripting b) Cross-Server Security c) Client-Side Scripting d) Cross-Site Security

Respuesta: a


P2 (L2): En React, ¿cuál es vulnerable a XSS?

// Option A
<div>{userInput}</div>

// Option B
<div dangerouslySetInnerHTML={{__html: userInput}} />

// Option C
<input value={userInput} />

a) Solo A b) Solo B c) A y B d) Todos

Respuesta: b Explicación: React escapa el contenido en JSX por defecto (A y C son seguras). dangerouslySetInnerHTML omite esta protección y es vulnerable si userInput contiene HTML/JavaScript malicioso.


P3 (L2): ¿Qué cabecera Content-Security-Policy previene mejor el XSS? a) Content-Security-Policy: default-src * b) Content-Security-Policy: default-src 'self' c) Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline' d) Content-Security-Policy: script-src 'none'

Respuesta: b Explicación: 'self' permite solo recursos del mismo origen. La opción a permite todo (sin protección). La opción c permite scripts en línea (riesgo de XSS). La opción d bloquea todos los scripts, incluidos los propios.

Autenticación (todos los roles)

P1 (L1): ¿Dónde debe almacenar tokens JWT en un navegador? a) localStorage b) sessionStorage c) Cookie HttpOnly d) Parámetros de URL

Respuesta: c Explicación: Las cookies HttpOnly no son accesibles mediante JavaScript, protegiéndose contra el robo de tokens por XSS. localStorage/sessionStorage son accesibles mediante JavaScript. Los parámetros de URL se registran y se almacenan en caché.


P2 (L2): ¿Qué está mal con este almacenamiento de contraseñas?

import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()

a) MD5 es demasiado lento b) MD5 está criptográficamente roto y no se usa sal c) Debería usar base64 en lugar de hexdigest d) Nada, esto es correcto

Respuesta: b Explicación: MD5 es rápido y tiene vulnerabilidades de colisión: no es adecuado para contraseñas. Las contraseñas deben usar bcrypt, Argon2 o scrypt con salado automático.


P3 (L3): Revise esta validación de JWT. ¿Cuál es el problema de seguridad?

const decoded = jwt.verify(token, secret);
if (decoded.userId) {
// Grant access
}

a) Ningún problema — jwt.verify comprueba todo b) Falta la especificación del algoritmo — vulnerable a confusión de algoritmos c) Debería usar jwt.decode en su lugar d) Falta la comprobación de la audiencia

Respuesta: b Explicación: Sin especificar los algoritmos, el atacante podría crear un token con «alg»: «none» o cambiar de RS256 a HS256 usando la clave pública como secreto. Siempre especifique: jwt.verify(token, secret, { algorithms: ['HS256'] })

Autorización (backend)

P1 (L1): ¿Qué es IDOR?

a) Insecure Direct Object Reference — acceder a recursos adivinando IDs b) Internal Data Object Routing — patrón de API interna c) Integrated Development Object Repository — término de control de versiones d) Indirect Object Reference Design — patrón de codificación segura

Respuesta: a


P2 (L2): Este endpoint recupera datos de usuario. ¿Qué falta?

@app.route('/api/users/<user_id>/profile')
def get_profile(user_id):
user = User.query.get(user_id)
return jsonify(user.to_dict())

a) Validación de entrada en user_id b) Comprobación de autorización — verificar que el solicitante puede acceder a los datos de este usuario c) Limitación de tasa d) Requisito de HTTPS

Respuesta: b Explicación: Cualquier usuario autenticado podría acceder al perfil de cualquier otro usuario cambiando el user_id. Es necesario verificar: if current_user.id != user_id and not current_user.is_admin: abort(403)


P3 (L3): ¿Cuál es el patrón de autorización más seguro?

a) Verificar la autorización en el controlador después de recuperar los datos b) Filtrar datos en la capa de repositorio/ORM basándose en el usuario actual c) Usar middleware que aplique autorización a todos los endpoints d) Verificar la autorización en la capa de servicio antes del acceso a los datos

Respuesta: b Explicación: El filtrado a nivel de repositorio garantiza que los datos no autorizados nunca salgan de la base de datos. Otros enfoques arriesgan la exposición de datos si se omite la comprobación. Defensa en profundidad: use múltiples capas.

Construcción de mapas de competencias en seguridad para desarrolladores

Los mapas de competencias visualizan las habilidades en todo el equipo e identifican brechas.

Mapa de competencias individual

Desarrollador: Jane Smith | Rol: Desarrolladora senior | Nivel: L2

ÁreaPuntuaciónEstado
Codificación segura80 %
Autenticación90 %
Validación de entrada100 %
Criptografía40 %⚠ Brecha
Modelado de amenazas50 %
Pruebas de seguridad70 %
Gestión de dependencias80 %
Respuesta a incidentes40 %⚠ Brecha

Recomendado: Curso de criptografía, formación en respuesta a incidentes

Mapa de calor de competencias del equipo

NombreCodif. seguraAutenticaciónValid. entradaCriptografíaModelado amenazasPruebasDeps
Jane S.✓✓✓✓✓✓✓✓✓✓
John D.✓✓✓✓✓✓✓✓✓✓
Alice K.✓✓
Bob M.✓✓✓✓✓✓✓✓✓✓✓✓✓✓
Carol L.
Promedio equipo80 %75 %90 %45 % BRECHA40 % BRECHA60 %70 %

✓✓ = Fuerte (70 %+) · ✓ = En desarrollo (40-70 %) · ✗ = Brecha (menos del 40 %)

Seguimiento de competencias a lo largo del tiempo

Evaluación T1 2024

ÁreaPuntuaciónObjetivoEstado
Codificación segura80 %80 %Alcanzado
Autenticación75 %80 %En progreso
Criptografía45 %60 %Brecha
Modelado de amenazas40 %60 %Brecha
Pruebas de seguridad60 %70 %En progreso

Acciones:

  • Módulo de autenticación completado
  • Taller de modelado de amenazas (programado en febrero)
  • Formación en criptografía para el equipo (programada en marzo)

Objetivos T2: Criptografía por encima del 60 % · Modelado de amenazas por encima del 60 % · Todos los desarrolladores aprueban la evaluación intermedia

Herramientas de seguimiento de competencias

EnfoqueMejor paraEsfuerzo
Hoja de cálculoEquipos pequeños (menos de 10)Bajo
Notion/ConfluenceOrganizaciones con mucha documentaciónBajo
Plataforma de gestión de habilidadesEquipos grandesMedio
Panel personalizadoNecesidades específicasAlto

Plantilla de hoja de cálculo simple:

DesarrolladorRolOWASP Top 10Auth/AuthZCriptografíaPruebasModelado amenazasÚltima evaluación
Jane S.Senior4/55/52/53/53/52024-01-15
John D.Medio3/53/54/52/54/52024-01-15
........................

Comparativa de plataformas de evaluación

Plataformas gratuitas

PlataformaFortalezasLimitaciones
Google FormsConfiguración fácil, gratuitoSin ejecución de código, análisis básico
Microsoft FormsIntegración con Office 365Similar a Google Forms
TypeformMejor experiencia de usuarioNivel gratuito limitado
CTFd auto-alojadoControl total, gamificadoRequiere configuración y mantenimiento

Plataformas de pago

PlataformaMejor paraCoste aproximado
Secure Code WarriorFormación de seguridad empresarial$$$$
HackEDUFormación centrada en desarrolladores$$$
AvataoDesafíos prácticos$$$
AppSec EngineerFormación cloud-native$$
KontraLecciones interactivas$$

Evaluación hágalo usted mismo

Para equipos pequeños con presupuesto limitado:

  1. Cree preguntas en Markdown — control de versiones, fácil actualización
  2. Use Google Forms para la entrega — gratuito, fácil de compartir
  3. Rastree resultados en una hoja de cálculo — mapeo simple de competencias
  4. Ejecute CTF con Juice Shop — gratuito, integral

Medición de la mejora

Métricas a rastrear

MétricaCómo medirObjetivo
Puntuación media de evaluaciónResultados del cuestionarioMejora cada trimestre
Densidad de vulnerabilidadesHallazgos de SAST por KLOCDisminución con el tiempo
Tiempo de correcciónSeguimiento en JIRADisminución para errores de seguridad
Errores de seguridad en producciónRastreador de erroresMenos en cada versión
Tasa de detección en revisión de códigoSeguimiento de comentarios de revisiónMás problemas de seguridad detectados
Finalización de formaciónLMS o hoja de cálculo100 % para los obligatorios

Análisis antes/después

Informe de impacto de la formación: T2 2024

Puntuaciones de evaluación antes y después de la formación:

ÁreaAntes de la formaciónDespués de la formaciónCambio
Prevención de SQLi65 %88 %+23 %
Buenas prácticas de autenticación70 %85 %+15 %
Validación de entrada60 %82 %+22 %

Impacto real en el mundo durante el mismo período:

MétricaT1T2Cambio
Hallazgos de SAST14589−39 %
Errores de seguridad en producción125−58 %
Tiempo medio de corrección (días)84−50 %

La formación está mostrando un ROI medible. Continúe con las evaluaciones trimestrales y centre la formación en las brechas actuales.

Errores comunes

  1. Evaluar el conocimiento, no las habilidades — Incluya ejercicios prácticos, no solo teoría
  2. Evaluación de talla única — Personalice para roles y stacks tecnológicos
  3. Evaluar una vez y olvidar — La evaluación regular detecta la degradación de habilidades
  4. Penalizar las puntuaciones bajas — Esto desalienta la evaluación honesta
  5. No actuar sobre los resultados — La evaluación sin formación es inútil
  6. Hacerla demasiado larga — Máximo 45-60 minutos para los cuestionarios

Preguntas de autoevaluación

  1. ¿Cuáles son los cuatro métodos para evaluar las habilidades de seguridad de los desarrolladores?
  2. ¿Por qué el ejercicio de revisión de código es más práctico que un cuestionario?
  3. ¿Cómo se calculan los niveles de competencia a partir de las puntuaciones de evaluación?
  4. ¿Qué métricas debe rastrear para medir la efectividad de la formación?
  5. ¿Con qué frecuencia se debe evaluar a los desarrolladores?
  6. ¿Cuál es el beneficio de la evaluación al estilo CTF?

Conclusión

La evaluación solo importa si actúa en consecuencia. Una brecha de habilidades que ha medido e ignorado es peor que una que no ha medido: sabía sobre ella y no hizo nada.

Ejecute la línea base. Encuentre las brechas. Úselas para priorizar en qué se forma primero.

Qué sigue

Siguiente: guía de implementación — plan paso a paso para desplegar la formación en seguridad para desarrolladores en todo el equipo.