En el software, este tipo de medidas no se tiene siempre en cuenta...
Ejemplo: extracto de la documentación de BEA WebLogic (2004):
Since most security for Web applications can be implemented by a system administrator, application developers need not pay attention to the details of securing the application unless there are special considerations that must be addressed in the code. For programming custom security into an application, WebLogic Server application developers can take advantage of BEA-supplied Application Programming Interfaces (APIs) for obtaining information about subjects and principals (identifying information for users) that are used by WebLogic Server. The APIs are found in the
weblogic.securitypackage.
Muchos sistemas de software no se construyen con la seguridad en mente.
Las tecnologías reactivas como los cortafuegos pueden aliviar algunos ataques a los servidores, pero no abordan el problema principal: software mal hecho
Para poner en práctica la seguridad del software hay que:

¿Cuáles son los puntos más importantes de cara a la seguridad?
Esta asignatura trata sobre la seguridad en los siguientes puntos...
Vulnerabilidad provocada por no validar la entrada del usuario:
Implementación en Python:
def greet_user(name):
print(f"Hola {name}!")
# Supongamos que la entrada proviene de un parámetro web
user_input = "<script>alert('XSS')</script>"
greet_user(user_input)
Solución más segura:
import html
def greet_user(name):
safe_name = html.escape(name)
print(f"Hola {safe_name}!")
Provocado por ofrecer a los atacantes la posibilidad de escribir arbitrariamente en memoria.
Implementación en C:
void trouble() {
int a = 32; /* integer */
char line[128]; /* character array */
gets(line); /* read a line from stdin */
}
Solución más segura:
void trouble() {
int a = 32; /* integer */
char line[128]; /* character array */
fgets(line, sizeof(line), stdin); /* read a line from stdin */
}
Estructura en memoria:
0xNN es la dirección de comienzo de la pila (stack)trouble()...Marco de pila antes de la ejecución:

Marco de pila después de ejecución normal:

Marco de pila tras un ataque por buffer overflow:

¿A qué se debe este buffer overflow?
Buffer overflow provocado por el (mal) uso de APIs vulnerables:
gets(), strcpy(), etc.
¿Cómo evitar el mal uso de APIs vulnerables?
¿Todos los problemas de seguridad se pueden resolver
con análisis de código?
Menos del 50%
— según Chest & West: Secure Programming with Static Analysis, 2007 —
¿Qué otras actividades relacionadas con el desarrollo tienen implicaciones en la seguridad?
Arquitectura y diseño
Un servicio web que ejecuta consultas SQL directamente desde parámetros HTTP:
Implementación en Python:
@app.route('/user')
def user_info():
name = request.args['name']
query = f"SELECT * FROM users WHERE name='{name}'"
return db.execute(query)
¿Por qué el diseño es clave para la seguridad?
El diseño asegura la mantenibilidad
Ann Campbell: For secure code, maintainability matters
El 60% de las CWE no son exactamente vulnerabilidades...
La vista CWE-699: Software Development contiene debilidades de categorías diversas...
CWE Top 25 Most Dangerous Software Weaknesses
Al alza:
A la baja:


¿Qué requisito de seguridad puede verse incumplido
si no se cumple la CWE-581?
Disponibilidad
Robert C. Martin: La arquitectura de un sistema software es la forma dada al sistema por aquellos que lo construyen. La forma es la división del sistema en componentes, la disposición de esos componentes y la manera en que se comunican entre sí.
Para un enfoque completo a la seguridad del software hay que combinar revisiones de código y análisis arquitectónico
¿Qué tiene que ver la arquitectura en todo esto de la seguridad?
La arquitectura software decide los componentes que conforman un sistema software y sus interacciones
¿Son importantes otras actividades relacionadas con el desarrollo de software?
Mucho, pero no las trataremos aquí
No nos interesan (tanto)...
Sí nos interesan aquellos aspectos del desarrollo de software que, en principio, no parecen relacionados con la seguridad, pero que la ponen en riesgo si no se hacen bien.
¿Así que hay que saber programación y técnicas de desarrollo de software para poder garantizar la seguridad?
¿No hay un respiro para quienes no les gusta programar?
Threat modeling: diagramas y diseño de la arquitectura
Métodos de threat modeling en arquitectura software:
¿Conocéis Windsurf? ¿Github Copilot?
¿Es otro respiro para quienes no les gusta programar?
server-side-request.pytemporary-file-creation.pycryptographic-key-generation.pyGarantizar el comportamiento de todo elemento de una aplicación ante cualquier situación de uso por incorrecta o imprevisible que ésta pueda parecer
¿La programación defensiva garantiza un software seguro?
No lo garantiza, porque al hablar de seguridad se supone la existencia de un adversario
La seguridad del software significa crear programas que se comportan correctamente incluso en presencia de un comportamiento malicioso.
void printMsg(FILE* file, char* msg) {
fprintf(file, msg);
}
void printMsg(FILE* file, char* msg) {
fprintf(file, msg);
}
¿Cómo arreglamos el hecho de que algún argumento de la función pueda ser null? Aplicar programación defensiva...
Aplicando programación defensiva:
void printMsg(FILE* file, char* msg) {
if (file == NULL) {
logError("attempt to print message to null file");
} else if (msg == NULL) {
logError("attempt to print null message");
} else {
fprintf(file, msg);
}
}
NOTA: Hay alternativas mejores para el tratamiento de null, como los Optionals
¿Desde la perspectiva de la seguridad, esta solución que aplica programación defensiva es suficiente?
Desde la perspectiva de la seguridad, esta solución no es suficiente.
Un atacante podría especificar un string en un formato maligo,
pensado para provocar un buffer overflow.
Recordemos el marco de pila de una llamada a función...
...antes de la ejecución:

...tras un ataque por buffer overflow:

Las organizaciones que desarrollan software tienen grupos de ingenieros de calidad - Quality Assurance (QA) dedicados a escribir y ejecutar tests y evaluar sus resultados.
Es casi imposible mejorar la seguridad del software solo mejorando la QA
Los tests de funcionalidad sirven para asegurarse de que los usuarios típicos con las necesidades típicas estén contentos, pero no sirven para encontrar defectos de seguridad que no estén relacionados con características específicas de seguridad.
La mayor parte de las pruebas software sirven para comparar la implementación con los requisitos, enfoque no adecuado para encontrar problemas de seguridad.
Los problemas de seguridad no suelen ser violaciones de los requisitos, sino funcionalidades no deliberadas que hacen que un programa sea inseguro.
El software fiable hace lo que se supone que tiene que hacer.
El software seguro hace lo que se supone que tiene que hacer, y nada más.

<c:if test="${param.sayHello}">
<!-- Let's welcome the user ${param.name} -->
Hello ${param.name}!
</c:if>
¿alguna vulnerabilidad?
Es vulnerable a ataques de Cross-Site Scripting (XSS)
Ninguna cantidad de pruebas de caja negra llega a ser suficiente para revelar este problema.
Las pruebas de caja negra solo pueden comenzar cuando el sistema está terminado. Tras desplegarlo, los atacantes y los defensores están en situación de igualdad. Los atacantes pueden ahora estudiar el software.
La suma de horas de todos los posibles atacantes puede superar con creces la suma de horas de los defensores haciendo pruebas. Los atacantes pueden encontrar más vulnerabilidades.
Encontrar una prueba fallida es un signo de problemas, pero pasar una prueba con éxito no significa mucho.
Imagina a un ladrón que quiere entrar en tu casa y que las bisagras de la puerta principal están hacia fuera
Imagina a un ladrón que quiere entrar en tu casa y que las bisagras de la puerta principal están hacia fuera
El pomo en sí es una medida de usabilidad
Un pestillo es una medida de seguridad Una medida mal diseñada o implementada puede comprometer la seguridad Implementar solo medidas de seguridad no garantiza la seguridad real
Muchas de las vulnerabilidades en los sistemas informáticos no son por fallos en las medidas de seguridad, sino en otros aspectos del desarrollo. Por ejemplo, usar una biblioteca vulnerable para manejo de imágenes.
• Requisitos: identificar necesidades de confidencialidad, integridad y disponibilidad. • Arquitectura y diseño: prever amenazas, modelar riesgos (por ejemplo, con STRIDE). • Construcción: escribir código robusto y revisarlo con herramientas (SAST, SCA). • Pruebas: incluir casos de uso maliciosos y pruebas de penetración. • Calidad y mantenimiento: corregir vulnerabilidades, actualizar dependencias.
También configuración y despliegue.
El programa se ejecuta correctamente, pero si se muestra en una página web, ejecutará código arbitrario (XSS).
Los problemas arquitectónicos son muy difíciles (a veces imposibles) de encontrar tan solo mirando al código Por ejemplo, la ausencia de una separación clara entre módulos con diferentes niveles de privilegio puede dar lugar a vulnerabilidades graves que no se detectan en el análisis de código.
Aquí el fallo no es solo de código: el diseño permite inyección SQL. La solución implica modificar la arquitectura (p. ej., usar un ORM o una capa de acceso controlada).
Algunos ataques (v.g. Heartbleed) tardaron en detectarse por el mal formateo del código escrito, que dificultaba su revisión humana
Si no se cumple la invariante if a.equals(b) == true then a.hashCode() == b.hashCode().. puede haber problemas cuando se almacenen objetos de esta clase en una colección. Si los objetos de la clase en cuestión se usan como claves de una Hashtable o si se insertan en un Map o un Set, es crítico que objetos iguales tengan los mismos hashcodes. Si no, puede que un objeto esté, pero no sea encontrado... NO DISPONIBILIDAD
STRIDE (Microsoft): analiza amenazas de Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service y Elevation of Privilege. LINDDUN: centrado en la privacidad Ejemplo: para el diseño de una API web, el modelo STRIDE identificaría amenazas de suplantación (S), inyección (T), denegación de servicio (D) y escalada de privilegios (E).