Hoy vamos a bajar a tierra con sencillos ejemplos y una estructura clara, esos principios de seguridad que nos guían en la protección de los sistemas informáticos desde el propio diseño de los componentes software que los sustentan.
Cuando se piensa en desarrollo seguro, desarrollo de software seguro, se suele acudir al código, buscando unas líneas de código concretas, o buenas practicas que nos salven de un posible incidente de seguridad. Esta práctica es muy beneficiosa para el software, pero también es incompleta. Intentar abordar la seguridad de unos sistemas informáticos desde únicamente el punto de vista de líneas de código, suele querer reducir la cuestión más de la cuenta. Un sistema seguro lo es en su conjunto y sus interrelaciones, la arquitectura de este debe ser conocida y revisada desde el punto de vista de la seguridad y la gestión de riesgos, haciendo tanto énfasis en el análisis como en la síntesis de este.
La mayoría de las aplicaciones son vulnerables a causa de un diseño inseguro en su origen, llevado a cabo sin un adecuado control de seguridad durante esta fase de diseño, del desarrollo de software.
EC-Council
Es por esta razón que conocer y seguir los principios de seguridad es tan valioso, puesto que son el conocimiento básico y guía principal de proteger un sistema, a cualquier nivel y en todo momento: tanto para revisar y modificar unas líneas de código, como para tomar una decisión de diseño sobre la funcionalidad de una aplicación, o sobre la arquitectura de todo el sistema. El resultado de revisar y modificar los documentos de diseño desde el punto de vista de la seguridad, y añadirle unos controles adecuados (requisitos de seguridad, revisión de cumplimiento, redacción de casos de abuso), dejara preparada la guía a seguir durante el resto de fases, para que los arquitectos y desarrolladores puedan consultar estas directrices e implementen el código en forma segura.
Otros recursos: por aquí te dejo unos enlaces, si quieres conocer más acerca de conceptos básicos de ciberseguridad, o del control de seguridad en otras fases del desarrollo de software, como por ejemplo la fase previa al diseño, la de análisis, puedes leer acerca de la ingeniería de requisitos de seguridad, que mediante dar forma a los requisitos de seguridad de un componente software prepara y apoya su posterior elaboración de diseño. O también acerca de como verificar, si un diseño seguro realmente se esta implementando de esta manera, con una auditoría de código.
¿Qué es una auditoría de código?
Principio de diseño seguro: Seguir el principio de mínimo privilegio
Un usuario o proceso, debe tener disponible la información y recursos que requiere para el desempeño de su función y no más. La funcionalidad disponible debe ser solo y exclusivamente la necesaria, cualquier posibilidad de acceso o ejecución de una acción que no se necesita, se elimina. Concede permisos, a partir de determinar su requerimiento para una tarea especifica.
Un ejemplo físico es que no dispongas de una llave de un almacén de tu empresa, si no requieres entrar allí. Algunos ejemplos son evidentes como eliminar la cuenta de un usuario que se ha ido de la empresa, hasta otros que no lo son tanto, como por ejemplo: en una aplicación que maneja facturas, si yo soy un operador que trabaja solo con ciertos clientes, ¿por que debería de tener acceso a la totalidad de las facturas que existen?, si las facturas no deben eliminarse en ningún caso, ¿por que la aplicación podría tener esta funcionalidad?. Incluso un usuario administrador, solo debe tener posibilidad de administrar lo que necesita administrar, no mas.
- Ejemplo de búsqueda en una auditoría: En desarrollo seguro es habitual encontrarse con una aplicación que se conecta al servidor de bases de datos con privilegios totales, cuando no requiere tener este permiso y solo va a trabajar con algunas tablas de una de las bases de datos.

Principio de diseño seguro: Establecer seguridad por defecto
La configuración por defecto en una aplicación o sistema, y por lo tanto el comportamiento derivado de esta, debe ser primero el más restrictivo posible, y si se modifica a una configuración más permisiva, esto debe de hacerse después, con una acción especifica para ello. Esta configuración habitualmente no es la más cómoda para los usuarios, pero debe encontrarse el correcto equilibrio entre mantener el sistema seguro y que este no les resulte muy incomodo a los usuarios.
Como ejemplo físico, algunas puertas tienen un sistema de cierre, lo que las hace cerrarse solas después de que las hayas abierto para pasar, esta puerta estará cerrada salvo cuando se requiera específicamente que este abierta; si alguien quiere mantenerla abierta por que lo necesita, tendrá que hacerlo de forma especifica. De igual manera la configuración inicial de un sistema o usuario debería estar «cerrada» o ser restrictiva, para después si este lo necesita irla «abriendo» de forma especifica.
Un ejemplo especifico de esto en desarrollo seguro que implica un gran riesgo, es la necesidad en muchos frameworks de desarrollo de software y sus practicas, de ir especificando en cada uno de los métodos de un controlador (servicios web) que este método lleva autenticación, con una etiqueta concreta como por ejemplo «[Authorize]». Esto desde el punto de vista de la seguridad es fatídico, por que implica que el desarrollador debe irse acordando de forma especifica de usar esta etiqueta para no dejar una vulnerabilidad en la aplicación; cuando debería de ocurrir justo lo contrario, debería de tener que apuntar de forma especifica, los métodos que deban estar expuestos al público previos a una autenticación. Precisamente estos métodos públicos, para el caso de una aplicación que implementa autenticación, habitualmente serán muy pocos; y además detectar que dejaste una puerta cerrada es fácil, te lo recordarán con el propio hecho de tener que abrirla.
- Ejemplo para una consultoría en desarrollo seguro: Para una aplicación, añádela en un entorno de pruebas, la configuración más restrictiva posible de seguridad, desde agregar al servidor de aplicaciones todas las cabeceras HTTP de seguridad, hasta añadir un filtro a nivel global del proyecto que compruebe autenticación para todos los métodos de los controladores. Después de eso y a través de unas pruebas, puede observarse que ha dejado de funcionar, para ver en detalle por que ha sido en cada caso y detectar los puntos en los que debe hacerse una excepción.
Un ejemplo de directivas de configuración de apache:
Header always set Strict-Transport-Security "max-age=4838400; includeSubdomains;" Header always set X-XSS-Protection "1; mode=block" Header always set X-Frame-Options "DENY" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin" Header always set Permissions-Policy "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()" Header always set Content-Security-Policy "object-src 'none'; script-src 'nonce-{random}' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:; base-uri 'none'; report-uri https://your-report-collector.example.com/"
Un ejemplo de forzar autenticación para todos los métodos en un proyecto ASP.NET Core MVC:
services.AddMvc(config => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); config.Filters.Add(new AuthorizeFilter(policy)); });
Principio de diseño seguro: Implementar seguridad en profundidad
El concepto de seguridad o defensa en profundidad, se basa en la premisa de que todo componente de un sistema puede ser vulnerado, y por tanto no se debe delegar la seguridad de un sistema en un único método o componente de protección. No es bueno depender de una única capa de protección por que esta puede fallar, y además por muy buena que sea, en el momento de ser vulnerada dejara de forma inmediata al sistema sin ninguna otra capa de seguridad. En un sistema informático, puede detectarse y dar respuesta mejor, frente a un atacante que ha pasado una capa de seguridad y esta intentando vulnerar la siguiente. Mientras que si tienes solo una, aunque sea muy segura, no tendrás tiempo ni capacidad de respuesta si esta es superada.
En un ejemplo físico, aunque un recinto posea una valla que aporte seguridad, después se pone una puerta, y según el contexto después habrá otras, o una caja fuerte; no se tiene por que confiar en una única capa de protección. En un sistema informático, a veces surge la duda de cuan seguro es haber dependido, para la protección frente a un tipo de ataque concreto como puede ser un XSS (Cross-site scripting), de la protección del WAF (Web application firewall); por que por ejemplo en unos ejercicios de hacking ético se ha intentado explotar una vulnerabilidad existente en una aplicación web, pero la explotación no ha sido posible por el bloqueo del WAF. Cualquier componente del sistema puede vulnerarse potencialmente, e incluso no estar disponible por un error técnico o mantenimiento no planificado, en un entorno que requiere protección, nunca es aceptable depender de una única capa de seguridad.
- Ejemplo para una consultoría en desarrollo seguro: Para una aplicación web, revisa que todos los controles de seguridad que se ejecutan en el front-end en JavaScript, después se validan de nuevo cuando llega al server la petición HTTP con el contenido final real de esta. En este caso citado ni siquiera podríamos decir que hay dos capas de seguridad efectivas, puesto que la dispuesta en la parte cliente que se ejecuta en el navegador del usuario, se ejecuta en un área no confiable y por lo tanto es muy fácil de evitar.

Principio de diseño seguro: Reducir la superficie de ataque
La superficie de exposición o de ataque que tiene una aplicación o sistema, se compone de todos aquellos puntos en los que un atacante, ya sea mediante una prueba manual o un proceso automático puede interactuar, introduciendo datos y/o extrayéndolos. A la forma de estos puntos de interacción se les llama vectores de ataque. Un principio básico de seguridad es eliminar o reducir todos estos puntos de la superficie de ataque que exponen al sistema, a la menor superficie posible.
Un ejemplo físico de esto sería disponer de una puerta en un recinto, en un lugar donde nadie va a requerir entrar o salir. Si nadie necesitará usarla, la puerta no debería estar abierta nunca y por extensión tampoco tiene razón para existir, desde el punto de vista de la seguridad es un potencial riesgo innecesario. En unos sistemas informáticos, ¿para que tener el puerto FTP abierto si no requiere usarse?, o ¿para que tener una aplicación o una funcionalidad de esta que ya no se usa, ofreciendo servicio?, si algún día se requiriera puede habilitarse bajo una petición y de forma especifica.
- Ejemplo para una consultoría en desarrollo seguro: Disponer en un entorno de pruebas, el despliegue de la aplicación sobre una infraestructura bastionada/fortificada, después mediante la ejecución de las pruebas observar que mínima superficie debes exponer para que la aplicación sea funcional.
Principio de diseño seguro: Asegurar el eslabón más débil
Habitualmente, un atacante fijará como objetivo el sistema más fácilmente explotable que encuentre, y la debilidad/facilidad de explotación de este estará marcada por su punto más débil. Es por ello que conocer estos puntos más débiles es lo más prioritario, la mayor criticidad de una aplicación o sistema es este punto más débil, dejando en un plano secundario el resto de los sistemas.
Por ejemplo no importarán mucho todos los excelentes mecanismos de seguridad que implemente una organización, si un usuario con privilegios de administración tiene la posibilidad de conectarse desde cualquier lugar, deshabilitar el segundo factor de autenticación y establecer una contraseña insegura por comodidad, en este caso toda la seguridad dependerá de este punto débil. En otro ejemplo, para vulnerar la confidencialidad de una comunicación, habitualmente no se intentará atacar directamente a los mensajes encriptados para romper su criptografía, en cuanto estos tengan una mínimamente aceptable. En lugar de esto será más practico para el atacante descubrir vulnerabilidades en los endpoints de la comunicación, alguna particularidad en la implementación o sencillamente engañar a los usuarios.
- Ejemplo de ejercicios que llevar a cabo: Que la organización lleve a cabo ejercicios de red team y que se ejecuten pentest o pruebas de penetración de forma continua, ofrece la valiosa información de identificar estos eslabones débiles. En este enfoque se simula el comportamiento malicioso y las acciones antijuridicas de un ataque real, por lo que el descubrimiento de vulnerabilidades viene marcado por economizar esfuerzos y ante todo aprovechar el punto más débil. También puede aproximarse a este enfoque una auditoría de código o análisis estático de código, en su descubrimiento de vulnerabilidades.

Principio de diseño seguro: Validar los datos de entrada
Todos los datos de entrada a la aplicación o sistema deben ser controlados en cuanto a su validación de tamaño y forma. Gran parte de los posibles ataques a sistemas informáticos se basan en introducir unos datos que el sistema no controla y no espera. Esto es a causa de confiar en un comportamiento normal y legitimo por parte de los usuarios. Una aplicación protegida en cambio, es la que considera que cada entrada de cada dato puede ser maliciosa. En este articulo se trata en detalle.
El ejemplo físico podría ser la existencia de un torno de control en una entrada que te deja pasar o no, según la lectura de tu tarjeta de identificación, pero que sin embargo te permite saltarlo fácilmente por encima sin que esto se detecte e impida. Otro ejemplo para detectar esta carencia de control incluso sin conocimientos específicos es comportarse de una manera no habitual que pueda no ser esperada para una aplicación informática, por ejemplo acceder a su login e introducir como contraseña un texto de un libro que conste de 60.000 caracteres, después probar con más tamaño. Cabe la posibilidad de que esta aplicación no tenga controlada en cuanto a tamaño y forma esta entrada de datos y ejecute la funcionalidad con normalidad procediendo a realizar un computo hash de este dato y lo compare con el de la contraseña correcta, de igual manera subir archivos enormes, fuzzing, jugar con conjuntos de caracteres especiales, comandos, etc… Si una aplicación se fortifica desde su origen con una adecuado control de datos de entrada, se cierran muchas posibilidades para el atacante.
- Ejemplo de revisión en una auditoría: realiza una revisión del código «source to sink«, fijándote en un dato desde su entrada al sistema, siguiendo el flujo de ejecución a través del código y terminando este, en su modificación, cálculo, preservación, devolución, etc… Puedes observar si hay un control de este dato en cuanto a su tamaño y forma, puede no haberlo, incluso de haberlo puedes estudiar si es adecuado y completo. Somete estos controles a pruebas dinámicas, por supuesto en un entorno dedicado específicamente a pruebas de seguridad, observa y registra la respuesta que te da.

Principio de diseño seguro: No delegar en ella, pero gestionar la seguridad por oscuridad
Lo único malo de una capa de protección basada en seguridad por oscuridad, será depositar confianza en ella, disponiendo únicamente de esta, sin que exista además un control de seguridad activo y efectivo que impida la explotación de una vulnerabilidad. El caso de una aplicación que tiene una vulnerabilidad grave, pero ingenuamente no se protege adecuadamente y se piensa que esta no va a aprovecharse, sencillamente por que es compleja de detectar y se desconoce de forma externa al proyecto. Por lo demás la seguridad por oscuridad es aliada. Lo contrario de la seguridad por oscuridad es la exposición de información, se trata de la cantidad de información que un potencial atacante puede llegar a conocer sobre un sistema y gestionar la exposición u ocultación de esta. Cuanta menos información tiene un atacante menos posibilidades tiene.
Un ejemplo muy simple es si un sistema ofrece banners de las tecnologías que implementa en las cabeceras HTTP:
HTTP/1.1 200 OK Cache-Control: private Content-Length: 10684 Content-Type: text/html; charset=utf-8 Server: Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-AspNetMvc-Version: 5.1 X-Powered-By: ASP.NET
En otro ejemplo, si un sistemas ofrece servicio público mediante una API, por propia funcionalidad deberás ofrecer una documentación a los usuarios sobre como deben de consumir estos servicios web. Pero por otro lado no tienes por que ofrecer información sobre como este sistema esta implementado de forma interna, su entorno tecnológico, arquitectura, etc… Existe un debate curioso, sobre si hacer público el código de un proyecto aumenta la seguridad de este, al poder más profesionales de la ciberseguridad descubrir y notificar vulnerabilidades; o por el contrario es mejor estrategia mantener secreto sobre la parte interna del proyecto. En este sentido el contexto puede cambiar mucho la decisión sobre una estrategia u otra, ¿Puedes estimar cuántas personas van a depositar realmente tiempo y esfuerzo en el descubrimiento de vulnerabilidades?, ¿vas a incentivarlo?. Cuando expones información al público la expones tanto a potenciales colaboradores, como a potenciales atacantes. Otro interesante aspecto es: ¿Qué te interesa exponer?, puedes publicar una parte de tu sistema para pruebas, pero no tiene por que ser una exposición total; su despliegue final para uso en producción, puede tener una parcial capa extra de seguridad por oscuridad.
Esta exposición u ocultación de información no solo existe entre la esfera de personas externas a una organización y el personal interno, también existe a todos los niveles. Por ejemplo para un software que tiene funcionalidades criticas, pongamos el caso de un servicio sanitario donde existe la funcionalidad de expedir recetas; solo un limitado personal podrá expedir recetas legalmente. Al ser esto así, es evidente que la seguridad del software debe controlar que un usuario interno, pero no autorizado a expedir una receta de un medicamento, pueda llegar a ejecutar esta funcionalidad; por otro lado, ni siquiera debería de poder llegar a ver donde se encuentra esta funcionalidad, puesto que la exposición de esta información sería innecesaria y solo podría provocar un intento o interés en hacerlo.
- Ejemplo de ejercicios que llevar a cabo: la máxima expresión de esto es la ciberinteligencia, puedes llevar a cabo técnicas y herramientas OSINT para poder ver cuanta información expone una organización, la fase de reconocimiento de un pentest, etc… Por otro lado, para entender esto de forma profunda y a un mayor nivel, es muy interesante conocer los básicos del ciclo tradicional de inteligencia, un factor muy importante de forma general para cualquier tipo de ataque y defensa.

Principio de diseño seguro: Seguir una separación de privilegios
La separación de privilegios consiste en definir diferentes roles y permisos asociados a estos, a diferentes usuarios; dividiendo con ello la capacidad y responsabilidad de tomar acciones sobre él sistema. De esta manera también se delimita el impacto de un incidente de seguridad que implique a estos usuarios. Cuantos más permisos tiene una única persona sobre el sistema, mayor dependencia tiene el sistema sobre esta. Es un factor clave en el control de seguridad interno, sobre fraude, errores internos, etc…
Un ejemplo es un usuario que dispone de permisos totales sobre activos del sistema, y además de forma total sobre todos los activos. El sistema depende absolutamente de que por ejemplo suplanten la identidad de esta persona, o de que la propia persona decida cometer un fraude. Reduciendo al absurdo con un ejemplo, imagina una persona que pudiera disparar un misil nuclear con solo apretar un botón, sin ninguna otra dependencia que su salud mental. La mayor expresión de inseguridad y carencia de control en este sentido, es cuando existe un usuario con unos privilegios excesivos sobre un sistema, y este además es compartido en su uso por varias personas.
- Ejemplo de búsqueda en una auditoría: revisa los permisos del usuario de sistema operativo sobre los que se ejecuta el proceso de servicio de aplicación. Revisa que puede llegar a hacer sobre la aplicación y el sistema, el usuario que tenga mayores privilegios; ante la grave situación de que suplanten su identidad, ¿Qué podría llegar a pasar?, ¿Cómo se daría cuenta el responsable de seguridad?, ¿Se podría llegar a ofrecer una respuesta ante la emergencia?.

Principio de diseño seguro: Mantener la simplicidad del sistema
El principio KISS, del inglés «Keep It Simple, Stupid!», es un acrónimo usado como principio de diseño. El principio KISS establece que la mayoría de sistemas funcionan mejor si se mantienen simples que si se hacen complejos; por ello la simplicidad debe ser mantenida como un objetivo clave del diseño seguro, y cualquier complejidad agregada a la estrictamente necesaria, debe ser evitada. En este sentido se debe revisar el diseño para prescindir de todo aquello que no aporte un beneficio claro y sea accesorio.
Que cualquier tipo de software resulte complejo para sus administradores y usuarios, es una debilidad en si mismo y afecta a su seguridad. La complejidad que un sistema posee de forma interna, para las personas que trabajan con el, es directamente proporcional al tiempo que cuesta detectar, analizar y responder ante un incidente de seguridad. Los problemas llaman a más problemas. Un sistema que tiene elementos sobrantes, multiplica el margen de error y de aparición de vulnerabilidades de manera innecesaria; cuanto más difícil de entender o manejar es uno de sus elementos, más posibilidades hay de que ocurra un incidente, por que no se le prestará la atención requerida a su complejidad, se olvidará o abandonará su administración, etc… Esto implica detectar las partes prescindibles para eliminarlas, muy preferiblemente en la fase de diseño.
Un ejemplo físico sería tener que vigilar y controlar la seguridad de una extensión enorme de terreno, o de un laberinto con innumerables entradas y salidas. Un ejemplo para una aplicación es no disponer de una documentación aceptable, tener más código del necesario y no revisar la complejidad ciclomática de este, dejar código comentado o que ya no se usa (código inútil u obsoleto), de forma acumulativa; tener un diseño de base de datos que se ha construido mal desde un inicio, obligando a crear nuevas tablas que llegan a duplicar información, etc… Otro ejemplo sería un diseño de permisos que va más allá de un RBAC simple, y permite a un administrador de grupo crear roles específicos, habilitando/deshabilitando permisos para cada acción y cada rol, o incluso para un usuario nominal concreto, con el laberinto que puede llegar a generar eso, ¿Quién lo mantendrá?, ¿Cómo de fácil será un error u olvido?.
- Ejemplo de revisión en una auditoría o consultoría de desarrollo seguro: se debe revisar toda la documentación del diseño del software cuanto antes, esta es habitualmente redactada por parte del arquitecto y/o responsable de desarrollo del proyecto. Revisar este diseño de la futura construcción del sistema, desde el punto de vista de la seguridad, desde el punto de vista de la no generación de vulnerabilidades, de poder monitorizar de forma efectiva y gestionar la seguridad con agilidad. La búsqueda de la toma de decisión de arquitectura optima, señalará los elementos prescindibles para eliminarlos y mantener el diseño lo más simple posible. Elaborar un modelo de amenazas y unos casos de abuso a los que someter al sistema, nos servirá para transmitir los riesgos a todos los stackholders o participantes del proyecto de desarrollo de software.
Principio de diseño seguro: Implementar un correcto manejo de errores y excepciones
El comportamiento de una aplicación, como responde a cada situación, a lo esperado y a lo no esperado, importa en cuanto a su seguridad. La existencia de un manejo de errores deficiente, supone y muestra debilidad por parte de la aplicación; una respuesta incorrecta o un mensaje de error desacertado, que expone información interna a personal no autorizado, puede llamar la atención de un atacante, provocando un intento de ataque o incluso apoyando su preparación. Estos errores en si mismos, también pueden provocar un incidente de seguridad, con una parada del servicio que produzca una corrupción o perdida de datos, que interrumpa la disponibilidad de un componente o de todo el sistema, etc…

La aplicación debe diseñarse y programarse de manera que las excepciones sean previstas y manejadas correctamente, teniendo de esta manera una resiliencia y tolerancia a errores. Una excepción es un evento inusual, que altera la ejecución esperada del software; estas condiciones excepcionales requieren un procesamiento diferente y especial. El código que sigue buenas practicas en cuanto a manejo de errores, esta estructurado de forma que encapsula cada parte de código susceptible de excepciones, y recoge estas con código destinado a tratar estas de la forma más especifica posible, fallando de forma segura y controlada, registrando lo ocurrido y realizando unas últimas acciones necesarias, como liberando o devolviendo a un estado previo un recurso. Según lo ocurrido y el contexto del software se diseñara su respuesta, puede llegarse incluso a bloquear o detener el servicio de aplicación hasta la revisión que confirme el estado de esta, para evitar males mayores como una destrucción de datos. A continuación se muestra un ejemplo en entorno tecnológico .Net C#, con las estructuras de control habitual de manejo de errores: Try, Catch y Finally.
WebClient wc = null; try { wc = new WebClient(); //downloading a web page var resultData = wc.DownloadString("http://google.com"); } catch (ArgumentNullException ex) { //code specifically for a ArgumentNullException } catch (WebException ex) { //code specifically for a WebException } catch (Exception ex) { //code for any other type of exception } finally { //call this if exception occurs or not //in this example, dispose the WebClient wc?.Dispose(); }
Como veremos en el siguiente principio, el registro de manera interna de información sobre un error o evento de seguridad es beneficioso de forma interna, para personal autorizado. Muy diferente es lo que debemos exponer durante estos errores al exterior, que resultará en una información mucho mas controlada y limitada, siendo esta la mínima posible para que el usuario pueda responder a la situación y continuar, por ejemplo cambiando algo de la acción realizada o contactando con el equipo de soporte para solucionarlo.
- Ejemplo de revisión de buenas practicas en una auditoría: revisar que el código esta estructurado de una manera adecuada desde su análisis estático y estos controles frente a excepciones disponen una respuesta adecuada a los diferentes errores. Poner a prueba el software en un entorno controlado, sometiéndolo a casuísticas excepcionales mediante pruebas dinámicas y observar si responde de forma segura, si no sufre impacto, si no expone información interna, etc… Entre estas pruebas, además de las especificas para ese software, están los ataques de denegación de servicio y el fuzzing.
Principio de diseño seguro: Implementar trazabilidad, registro de log y eventos de seguridad
Que registro guarda una aplicación de lo que ocurre en ella, y como notifica de los problemas, importa en cuanto a su seguridad. Cuando tiene lugar un incidente de seguridad, el tiempo que tarda este incidente en identificarse, notificarse, y la información que puede ofrecerse al responsable de seguridad que va a organizar la respuesta a este, son decisivos. Es por esta razón de control de seguridad, y también por control de calidad que se registran en log los eventos que van ocurriendo durante la ejecución del software. Lo ideal es que se registren unos eventos de seguridad y estos se getionen con unas notificaciones asociadas, la efectividad de estos puede ponerse a prueba como cualquier otra funcionalidad de una aplicación; identificar y entender un ataque en proceso o ya terminado, depende de ello.

Un simple ejemplo es la información que guarda una aplicación sobre los intentos fallidos de autenticación, si un usuario «se equivoca» con su contraseña 500 veces, independientemente del control de seguridad que realice, ¿Este dato se guarda?, ¿Cómo notifica la aplicación de esto?. Poder saber que el código manejo una excepción inusual, que usuario realizo una operación importante sobre el sistema, desde que IP pública estableció la conexión, etc…
- Ejemplo de revisión en una auditoría: realizar una revisión del log para ver que forma tiene, observando que registro de eventos contempla, simular un ataque en un entorno controlado para pruebas y ver que información sobre este podemos encontrar después. Esta capacidad de la aplicación para informar de lo que ocurre se relaciona con la gestión de la seguridad de toda la organización a un mayor nivel, apoyando en gran medida a: la monitorización continua que lleva a cabo un SOC (Centro de operaciones de seguridad), la respuesta a un incidente que ofrece un CERT (Equipo de Respuesta ante Emergencias Informáticas) y la capacidad de un posterior análisis informático forense.
Principio de diseño seguro: Diseñar con un bajo acoplamiento y una alta cohesión
Un mejor o peor diseño del software no solo afecta a su calidad, también afecta a su seguridad, un diseño deficiente abre margen a errores, incidentes de seguridad, problemas para su mantenimiento, etc… Dentro de la ingeniería de software el acrónimo SOLID repasa cinco principios para un buen diseño, aquí haremos foco en dos: mantener un bajo acoplamiento y una alta cohesión entre componentes.
El acoplamiento en informática es la forma y nivel de interdependencia entre módulos de software, una medida de qué tan cercanamente conectados están dos rutinas o módulos de software. Un bajo acoplamiento implica que unos componentes software y las partes del código que los conforman, no tienen una gran dependencia entre ellos y puedes llegar a diferenciarlos y delimitarlos con facilidad, con lo que su revisión y modificación será más sencilla. Por ejemplo, buscar el punto del código responsable de un error no implicará una dificultad añadida a la propia tarea de diagnostico. Para conseguir un bajo acoplamiento hay que cuidar el diseño simple y adecuado de su arquitectura e implementar buenas practicas y limpieza en el código. Por ejemplo seguir el principio de única responsabilidad, mediante el cual una clase y cada uno de sus métodos tienen una única responsabilidad, se ocupan de una única función y no más, con lo que el código queda simple y delimitado entre diferentes funciones. Un indicador sencillo para detectar estos problemas será la complejidad ciclomática, esta nos ayudará a localizar y evitar ese temido monolito de código ininteligible, también llamado carajal, horror, etc… Mi forma preferida de llamarlo, es la descrita en este comentario de código a continuación, que es real, me lo encontré en una aplicación que tenia unas diferentes casuísticas imposibles de abarcar por completo: » // Aquí esta la madre del cordero».

La cohesión en cambio es positiva y define la unión adecuada de dos componentes software (módulos o partes del código), atendiendo esta unión a la calidad de la relación relación entre ambos, con un propósito unificado y en una forma practica y completa. Para conseguir una alta cohesión, de igual manera que ocurría con el bajo acoplamiento, hay que cuidar el diseño simple y adecuado de su arquitectura e implementar buenas practicas y limpieza en el código. Un código con una alta cohesión tiene una gran coherencia, una forma elegante en su relación, en la forma de las llamadas y las devoluciones, lo que resulta en una fácil legibilidad y mantenimiento.

En resumen un código acoplado implica una unión entre sus partes confusa y aparatosa, donde es difícil diferenciar estas partes que se solapan y entender el código, cuesta diferenciarlo fácilmente y separarlo en partes; por otro lado una baja cohesión implicaría partes del código que tienen una relación incoherente, con diferencias en sus propósitos, variables y llamadas sobrantes e innecesariamente complejas. Al reducir acoplamiento y aumentar cohesión se mejora el diseño y las implicaciones de este.
- Ejemplo de revisión en una auditoría: realizar auditorías del código, que revisen las buenas practicas y la estructura del código, revisión de los patrones de diseño implementados, revisión de complejidad ciclomática, análisis de las interacciones e interdependencias entre componentes software, la forma de las llamadas y devoluciones, etc… Prestar atención a los resultados de calidad de las herramientas SAST además de a los de seguridad estrictamente. Buscar vulnerabilidades y su explotación a partir de un origen en su propio diseño inadecuado.