Lista de verificación de seguridad de código

Imagine que su valor de mercado cae un 80% en nueve horas. Eso es exactamente lo que le ocurrió a Holograph. Una reciente filtración de datos expuso una debilidad oculta en el código del contrato inteligente de Holograph, un duro recordatorio de que la ciberdelincuencia está en auge. Aquí es donde una auditoría de seguridad del código se vuelve innegociable.

Las empresas que manejan datos sensibles tienen la responsabilidad de priorizar medidas de seguridad robustas. Ya se trate de un gigante del comercio electrónico, una agencia gubernamental o un proveedor de servicios financieros, esta guía le proporcionará los conocimientos y las herramientas que necesita para mejorar la seguridad de su código. Muchas normativas exigen una sólida protección de los datos, y las auditorías le ayudarán a cumplirlas.

Este artículo proporciona una lista de comprobación exhaustiva para llevar a cabo una auditoría de seguridad del código en profundidad. Siguiendo estos pasos, puede mejorar significativamente la seguridad de su código y mantener intacta la confianza de sus clientes e inversores.

Guía para una auditoría exhaustiva de la seguridad del código

Una auditoría de seguridad del código es una inmersión profunda en el código de su aplicación, en busca de vulnerabilidades ocultas. En los sectores de alto riesgo, incluso un pequeño fallo de seguridad puede tener consecuencias devastadoras. Una auditoría de seguridad del código ayuda a identificar y abordar estas cuestiones antes de que se conviertan en un problema grave.

Lista de verificación de seguridad de código

Comprobaciones preliminares

Antes de entrar de lleno en la auditoría de seguridad del código, es fundamental conocer las posibles amenazas y vulnerabilidades que acechan a la aplicación. Estos pasos iniciales ayudan a sentar las bases para un proceso de revisión exhaustivo y eficaz.

Comprender el contexto de la aplicación:

  • Reúna información sobre el propósito, la arquitectura y la pila tecnológica de la aplicación.
  • Identifique los componentes y módulos clave que requieren una revisión de seguridad.
  • Comprender el modelo de amenazas, incluidos los posibles atacantes y sus capacidades.
  • Revisar la documentación de seguridad existente y las evaluaciones de seguridad previas.
  • Identificar los requisitos normativos y de cumplimiento pertinentes para la aplicación.

Control de versiones y gestión de cambios:

  • Asegurarse de que el código se almacena en un sistema de control de versiones seguro, como Git.
  • Revisar el historial de confirmaciones para conocer los cambios recientes e identificar posibles problemas de seguridad.
  • Compruebe que los cambios en el código se revisan y aprueban mediante un proceso formal.
  • Comprobar la presencia de mensajes de confirmación y documentación relacionados con la seguridad.
  • Identificar cualquier confirmación directa en áreas sensibles del código base sin revisión por pares.

Procesos de creación y despliegue:

  • Revise los procesos de creación y despliegue en busca de buenas prácticas de seguridad.
  • Garantizar que las secuencias de comandos de compilación y los archivos de configuración sean seguros y no expongan información confidencial.
  • Verificar el uso de herramientas automatizadas de pruebas de seguridad en la canalización CI/CD.
  • Comprobar el manejo seguro de secretos y credenciales durante los procesos de creación y despliegue.
  • Identificar cualquier paso manual en el proceso de despliegue que pudiera introducir riesgos de seguridad.

Colaboración y comunicación en equipo:

  • Establecer canales y protocolos de comunicación claros para el proceso de revisión de la seguridad.
  • Asegúrese de que todos los miembros del equipo que participan en la revisión son conscientes de sus funciones y responsabilidades.
  • Programe reuniones periódicas para debatir los resultados y coordinar las medidas correctoras.
  • Proporcionar formación y recursos a los miembros del equipo sobre prácticas de codificación segura y técnicas de revisión de la seguridad.

Calidad del código y comprobación de vulnerabilidades

En este paso, nos aseguraremos de que el código sea de la máxima calidad y eliminaremos cualquier punto débil que pueda ser pirateado. Revisaremos el código para encontrar problemas de seguridad comunes y asegurarnos de que todo está codificado siguiendo las mejores prácticas.

Legibilidad y mantenimiento del código:

  • Comprobar que el código sea claro, conciso y autoexplicativo, con los comentarios apropiados.
  • Asegúrese de que las funciones y los métodos tienen un único propósito y no son excesivamente complejos.
  • Verificar el uso coherente de las convenciones de nomenclatura y el formato del código en toda la base de código.
  • Identificar y refactorizar el código duplicado para reducir la posibilidad de errores.
  • Garantizar una estructura de código modular para facilitar las pruebas y el mantenimiento.
// Malas prácticas
public void ProcessOrder(Order order) {
    ValidateOrder(order);
    SaveOrder(order);
    SendConfirmationEmail(order);
}

// Buenas prácticas
public void ProcessOrder(Order order) {
    ValidateOrder(order);
    SaveOrder(order);
    NotifyCustomer(order);
}

private void NotifyCustomer(Order order) {
    SendConfirmationEmail(order);
    LogNotification(order);
}

El código monolítico que no se divide en partes más pequeñas puede dar lugar a duplicación de código y aumenta el riesgo de errores durante los cambios. Refactorice grandes bloques de código en métodos o clases más pequeños y reutilizables

Análisis estático:

  • Utilizar herramientas de análisis estático para escanear el código en busca de posibles vulnerabilidades de seguridad.
  • Identifique problemas comunes de codificación, como desbordamientos de búfer, condiciones de carrera y fugas de memoria.
  • Asegúrese de que las herramientas de análisis estático están configuradas para aplicar las normas de codificación y las directrices de seguridad.
  • Revisar y abordar las conclusiones de los informes de análisis estático.
  • Integrar el análisis estático en el proceso de integración continua (CI) para realizar comprobaciones continuas.

Normas de codificación y mejores prácticas:

  • Verificar el cumplimiento de las prácticas de codificación específicas del lenguaje y estándar del sector.
  • Comprobar el uso de bibliotecas y marcos de codificación seguros.
  • Garantizar que el código se adhiere al principio de mínimo privilegio.
  • Validar que no se utilizan funciones obsoletas o inseguras.
  • Promover el uso de técnicas de codificación defensivas para anticipar y manejar entradas o estados inesperados.
// Malas prácticas
public void ProcessInput(string input) {
    Console.WriteLine("Processing: " + input.Substring(5));
}

// Buenas prácticas
public void ProcessInput(string input) {
    if (string.IsNullOrEmpty(input) || input.Length < 5)
    {
        throw new ArgumentException("Input must be at least 5 characters long.");
    }
    Console.WriteLine("Processing: " + input.Substring(5));
}

Ejemplo de codificación defensiva que hace que el código sea más seguro, pero también más fácil de depurar y mantener

Uso seguro de las características del lenguaje:

  • Comprobar el uso correcto de las características específicas del lenguaje, como el manejo de excepciones en Java o la comprobación de errores en Go.
  • Asegúrese de que la gestión de memoria se realiza correctamente para evitar fugas y corrupción (relevante para lenguajes como C/C++).
  • Comprobar el uso seguro de construcciones de programación concurrente para evitar condiciones de carrera y bloqueos.
  • Garantizar que se aplica la seguridad de tipos para evitar vulnerabilidades relacionadas con los tipos.
  • Validar que las operaciones de entrada/salida se gestionan de forma segura para evitar ataques de inyección.
// Malas prácticas
char* buffer = new char[100];
strcpy(buffer, "This is a buffer.");
// Missing delete[] buffer;

// Buenas prácticas
#include 
std::unique_ptr buffer(new char[100]);
strcpy(buffer.get(), "This is a buffer.");
// No need to manually delete, handled by unique_ptr

Ejemplo de mala o buena gestión de la memoria en C/C++. Los problemas de memoria pueden causar fallos en la aplicación, degradación del rendimiento y vulnerabilidades de seguridad

Reconocimiento de patrones de vulnerabilidad:

  • Identificar y abordar patrones de vulnerabilidad comunes, como credenciales o secretos codificados en el código fuente.
  • Inspeccione el uso inseguro de generadores de números aleatorios (asegúrese de que se utilizan generadores de números aleatorios criptográficos cuando sea necesario).
  • Asegúrese de que no hay entradas de usuario no digitalizadas que se pasen directamente a consultas de bases de datos o argumentos de línea de comandos.
  • Documente cualquier caso de canales de comunicación inseguros sin cifrado.
  • Vigilancia de controles de acceso insuficientes en la lógica de la aplicación.
  • Documente y comparta estos patrones con el equipo de desarrollo para aumentar la concienciación y prevenir futuras incidencias.
// Malas prácticas (SQL Injection risk)
string query = "SELECT * FROM Users WHERE Username = '" + username + "'";

// Buenas prácticas
string query = "SELECT * FROM Users WHERE Username = @username";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@username", username);

El uso directo de entradas no desinfectadas puede provocar inyecciones SQL, inyecciones de comandos y otros tipos de ataques de inyección

Análisis de la complejidad del código:

  • Utilice herramientas para medir la complejidad del código, como la complejidad ciclomática.
  • Identifique áreas del código muy complejas o “puntos calientes” que sean propensas a errores y problemas de seguridad.
  • Simplifique las rutas de código complejas para mejorar la legibilidad y reducir la probabilidad de errores.
  • Garantizar que la lógica de seguridad crítica sea sencilla y fácil de revisar.
  • Refactorizar el código cuando sea necesario para reducir la complejidad y mejorar la claridad.
// Malas prácticas
public void ProcessOrder(Order order) {
    ValidateOrder(order);
    if (order.IsValid) {
        SaveOrder(order);
        SendConfirmationEmail(order);
    }
}

// Buenas prácticas
public void ProcessOrder(Order order) {
    if (!ValidateOrder(order)) return;
    SaveOrder(order);
    SendConfirmationEmail(order);
}

private bool ValidateOrder(Order order) {
    // Validation logic
    return order.IsValid;
}

Refactorización de métodos grandes en métodos más pequeños, cada uno de los cuales desempeña una única responsabilidad, lo que mejora la comprobabilidad, la legibilidad y el mantenimiento

Documentación y comentarios:

  • Asegúrese de que el código relevante para la seguridad está bien documentado, explicando la justificación de los controles de seguridad.
  • Verificar que los comentarios no exponen información sensible o detalles de implementación de seguridad.
  • Garantizar que la documentación incluye directrices para una interacción segura con el código, por ejemplo, cómo llamar correctamente a una función que realiza operaciones sensibles.
  • Validar que todas las implementaciones criptográficas y procesos de gestión de claves están documentados exhaustivamente.
  • Garantizar que los comentarios en línea se utilicen juiciosamente para aclarar segmentos de código complejos sin saturar la base de código.

Validación de entradas

Una validación adecuada de las entradas es fundamental para garantizar que la aplicación sólo procesa los datos válidos y esperados. Esto ayuda a prevenir una variedad de ataques, como la inyección SQL, cross-site scripting (XSS), y desbordamientos de búfer. Todas las entradas deben estar correctamente validadas y desinfectadas antes de ser procesadas, y he aquí cómo comprobarlo.

Validación del lado del cliente:

  • Verifique que la validación del lado del cliente está implementada para proporcionar información inmediata a los usuarios.
  • Asegúrese de que la validación del lado del cliente no se utiliza únicamente con fines de seguridad.
  • Comprobar la presencia de reglas de validación coherentes entre el cliente y el servidor.

Validación del lado del servidor:

  • Garantizar que todas las entradas se validan en el lado del servidor, independientemente de la validación del lado del cliente.
  • Comprobar si las entradas se validan con listas de permisos.
  • Comprobar si se pueden aplicar listas de denegación como medida secundaria.
  • Garantizar la validación adecuada de todos los tipos de entrada, incluidas cadenas, números, fechas y archivos.
// Malas prácticas
public bool ValidateUsername(string username) {
    // Checks only if input is not null or empty
    return !string.IsNullOrEmpty(username);
}

//Buenas prácticas
public bool ValidateUsername(string username) {
    // Allow only alphanumeric characters and underscores
    Regex allowedPattern = new Regex(@"^\w+$");
    return allowedPattern.IsMatch(username);
}

Uso de patrones de validación genéricos y excesivamente permisivos frente al uso de listas de permitidos para especificar explícitamente los caracteres o patrones permitidos

Saneamiento de entradas:

  • Verificar que las entradas HTML, JavaScript y SQL están correctamente desinfectadas para evitar ataques de inyección.
  • Asegúrese de que se utilizan bibliotecas o funciones especializadas para gestionar las tareas comunes de desinfección.
  • Garantizar que las entradas desinfectadas se utilizan de forma coherente en toda la aplicación.

Comprobaciones de longitud y formato:

  • Verificar que se comprueba la longitud de las entradas para evitar desbordamientos de búfer y agotamiento de recursos.
  • Asegurarse de que las entradas se ajustan a los formatos esperados, como direcciones de correo electrónico, números de teléfono y URL.
  • Compruebe si se han implementado expresiones regulares para la validación de formatos complejos, asegurándose de que son eficaces y seguras.
// Malas prácticas
public IActionResult SubmitForm(string userInput) {
    // No length check
    Database.Save(userInput); // Risk of buffer overflow or inefficient database use
}

//Buenas prácticas
public IActionResult SubmitForm(string userInput) {
    if (userInput.Length > 100) {
        return BadRequest("Input too long.");
    }
    Database.Save(userInput); // Input length is controlled
}

Compruebe siempre la longitud de las entradas antes de procesarlas para asegurarse de que cumplen los requisitos de su aplicación y evitar el uso indebido de recursos

Análisis de valores límite:

  • Compruebe la validación de las entradas en función de los valores límite, como los valores máximos y mínimos permitidos.
  • Asegúrese de que los valores fuera de los límites se gestionan correctamente y no provocan fallos en la aplicación.
  • Valide las entradas numéricas para asegurarse de que se encuentran dentro de los rangos aceptables y están correctamente tipificadas (por ejemplo, números enteros frente a números de coma flotante).
// Malas prácticas
public IActionResult CreateAccount(User user) {
    if (user.Age < 18) { // No upper boundary check
        return BadRequest("You must be at least 18 years old.");
    }
    UserRepository.Add(user);
}

//Buenas prácticas
public IActionResult CreateAccount(User user) {
    if (user.Age < 18 || user.Age > 99) {
        return BadRequest("Invalid age. Age must be between 18 and 99.");
    }
    UserRepository.Add(user);
}

Ignorar los valores límite durante la validación puede provocar comportamientos inesperados o errores cuando se introducen valores extremos

Comprobación de tipos:

  • Asegúrese de que las entradas se comprueban para los tipos de datos correctos, tales como cadenas, enteros, booleanos.
  • Compruebe si se utilizan funciones y bibliotecas seguras desde el punto de vista tipográfico para aplicar las restricciones de tipo de datos.
  • Validación de entradas JSON y XML para garantizar que se ajustan a los esquemas y estructuras esperados.
// Malas prácticas
public void ProcessJson(string jsonString) {
    var data = JsonConvert.DeserializeObject(jsonString); // No validation against schema
    // Process data
}

// Buenas prácticas
public IActionResult ProcessJson(string jsonString) {
    JSchema schema = JSchema.Parse(File.ReadAllText("MyObjectSchema.json"));
    JObject jsonObject = JObject.Parse(jsonString);
    if (!jsonObject.IsValid(schema)) {
        return BadRequest("Invalid JSON format.");
    }
    var data = jsonObject.ToObject();
    // Process data
}

Aceptación de datos JSON o XML sin validación con respecto a un esquema, lo que podría provocar el paso de estructuras de datos inadecuadas

Codificación:

  • Asegurarse de que los datos de entrada se codifican correctamente antes de ser procesados o almacenados.
  • Comprobar si se utilizan las técnicas de codificación adecuadas para los distintos contextos, como la codificación URL, la codificación HTML y la codificación base64.
  • Comprobar que los datos codificados se descodifican correctamente y se validan antes de seguir procesándolos.
// Malas prácticas
public string GetUserProfileHtml(string username) {
    return $"
Hello, {username}!
"; // Risk of XSS if username contains HTML/JS } // Buenas prácticas public string GetUserProfileHtml(string username) { return $"
Hello, {HttpUtility.HtmlEncode(username)}!
"; // Safely encode to prevent XSS }

Visualización de entradas de usuario no codificadas en HTML, lo que puede dar lugar a vulnerabilidades de secuencias de comandos en sitios cruzados (XSS)

Carga de archivos:

  • Valide el tipo de archivo, el tamaño y el contenido antes de aceptar la carga de archivos.
  • Asegúrese de que los archivos cargados se almacenan en una ubicación segura con acceso restringido.
  • Compruebe si se ha implementado el escaneado de virus y la detección de malware para los archivos cargados.
  • Compruebe que los nombres de los archivos y las rutas están desinfectados para evitar ataques a través de directorios.

Entradas de terceros:

  • Validar y desinfectar las entradas recibidas de fuentes de terceros, como API y webhooks.
  • Asegúrese de que los datos de terceros se tratan con el mismo nivel de escrutinio que los datos introducidos por el usuario.
  • Verifique la integridad y autenticidad de los datos recibidos de fuentes externas.

Autenticación

La autenticación es crucial para verificar la identidad del usuario y garantizar que sólo los usuarios legítimos puedan acceder al sistema. Una autenticación adecuada mantiene a salvo de miradas indiscretas tu información y cualquier otra cosa que haya dentro del sistema. Una reciente filtración de datos de Snowflake confirmó que los usuarios con autenticación de factor único pueden ser la puerta de entrada para que los hackers comprometan cientos de otras cuentas.

Gestión de contraseñas:

  • Asegúrese de que las contraseñas se almacenan utilizando algoritmos hash fuertes como bcrypt, Argon2 o Scrypt.
  • Verifique la aplicación de las políticas de contraseñas, incluidos los requisitos de longitud mínima, complejidad y caducidad.
  • Compruebe si existen mecanismos seguros de restablecimiento y recuperación de contraseñas que no expongan información confidencial.
  • Asegúrese de que se pide a los usuarios que cambien las contraseñas predeterminadas en el primer inicio de sesión.
// Malas prácticas
public bool IsValidPassword(string password) {
    return !string.IsNullOrEmpty(password); // Only checks if password is not empty
}

// Buenas prácticas
public bool IsValidPassword(string password) {
    var hasMinimum8Chars = password.Length >= 10;
    var hasUpperCase = password.Any(char.IsUpper);
    var hasLowerCase = password.Any(char.IsLower);
    var hasDecimalDigit = password.Any(char.IsDigit);
    var hasSpecialChar = password.Any(p => !char.IsLetterOrDigit(p));

    return hasMinimum8Chars && hasUpperCase && hasLowerCase && hasDecimalDigit && hasSpecialChar;
}

Implantar y aplicar políticas de contraseñas que exijan una longitud mínima e incluyan requisitos de complejidad

Autenticación multifactor (AMF):

  • Asegúrese de que se implanta y aplica la AMF para las cuentas de usuario y operaciones críticas.
  • Verifique el uso de segundos factores seguros, como tokens de hardware, códigos SMS o aplicaciones de autenticación.
  • Garantizar que los mecanismos de MFA son resistentes a ataques comunes como phishing y man-in-the-middle.
  • Validar la implantación de la AMF para varios flujos de autenticación, como el inicio de sesión y el restablecimiento de contraseña.

Mecanismos de inicio de sesión seguro:

  • Validar la implementación de formularios de inicio de sesión seguros, garantizando que los datos se transmiten a través de HTTPS.
  • Garantizar que los intentos de inicio de sesión están limitados para evitar ataques de fuerza bruta.
  • Verificar el manejo seguro de los testigos de sesión y las cookies durante el proceso de autenticación.
  • Garantizar que los mensajes de error de inicio de sesión no revelen si el nombre de usuario o la contraseña son incorrectos.
// Malas prácticas
public IActionResult Login(string username, string password) {
    if (Authenticate(username, password)) {
        // User authenticated
    } else {
        // Authentication failed
    }
}

// Buenas prácticas
public IActionResult Login(string username, string password) {
    if (!IsAllowedToAttempt(username)) {
        return BadRequest("Too many failed login attempts. Please try again later.");
    }

    if (Authenticate(username, password)) {
        ResetAttemptCounter(username);
        // User authenticated
    } else {
        IncrementAttemptCounter(username);
        // Authentication failed
    }
}

Permitir un número ilimitado de intentos de inicio de sesión sin ninguna comprobación frente a la aplicación de un límite de velocidad para evitar ataques de fuerza bruta

Autenticación basada en tokens:

  • Garantizar la generación, el almacenamiento y la validación seguros de tokens de autenticación, como JWT y OAuth.
  • Verificar que los tokens están firmados y cifrados mediante algoritmos seguros.
  • Inspeccione si se implementan tokens de corta duración y tokens de actualización para minimizar el impacto del robo de tokens.
  • Asegurarse de que los tokens se invalidan cuando el usuario cierra la sesión o ésta expira.

Autorización

Una vez autenticado el usuario, la autorización comprueba qué puede hacer. Esta parte de la aplicación se asegura de que sólo los usuarios con los permisos adecuados puedan acceder a determinadas cosas o realizar ciertas acciones. Cuando revisamos esto, esto es lo que buscamos.

Control de acceso basado en roles (RBAC):

  • Asegurarse de que las funciones y los permisos de los usuarios están claramente definidos y aplicados.
  • Verificar que se realizan comprobaciones de control de acceso en el lado del servidor para cada operación sensible.
  • Garantizar que los usuarios sólo puedan realizar acciones y acceder a recursos que coincidan con sus funciones.
// Malas prácticas
public bool CanAccessResource(string role)
{
    if (role == "Admin" || role == "User") // Unclear distinction
        return true;
    return false;
}

// Buenas prácticas
public bool CanAccessResource(string role)
{
    if (role == "Admin")
        return true;
    if (role == "User")
        return false; // Specific access defined per role
}

Definición ambigua de funciones frente a funciones y permisos claramente definidos

Principio de mínimo privilegio:

  • Verificar que la aplicación aplica el principio de mínimo privilegio, concediendo a los usuarios los permisos mínimos necesarios.
  • Asegúrese de que las acciones sensibles requieren autorización o confirmación adicional.
  • Compruebe si se aplica la separación de funciones para evitar conflictos de intereses.
  • Averigüe si se revisan y auditan periódicamente las funciones y permisos de los usuarios para identificar privilegios excesivos.

Listas de control de acceso (ACL):

  • Asegúrese de que se implementan las ACL para controlar el acceso a los recursos en función de las funciones y atributos de los usuarios.
  • Verificar el almacenamiento y manejo seguros de las ACL.
  • Comprobar la aplicación coherente de las ACL en toda la aplicación.
  • Comprobar si las ACL se actualizan periódicamente para reflejar los cambios en las funciones y permisos de los usuarios.

Control de acceso basado en atributos (ABAC):

  • Verificar la aplicación de ABAC, asegurándose de que las decisiones de acceso se basan en los atributos del usuario y las condiciones del entorno.
  • Garantizar que las políticas de ABAC están claramente definidas y se revisan periódicamente.
  • Validar que las normas ABAC se aplican de forma coherente en toda la aplicación.
  • Garantizar que las políticas sean flexibles para adaptarse a los cambios en las funciones de los usuarios y las condiciones del entorno.

Generación y almacenamiento de testigos de sesión:

  • Comprobar el uso de generadores de números aleatorios criptográficamente seguros para la creación de tokens.
  • Comprobar que los testigos de sesión tienen la longitud y complejidad suficientes para evitar ataques de adivinación.
  • Garantizar que los testigos de sesión se almacenan de forma segura en el lado del cliente, utilizando cookies seguras con los atributos adecuados, como HttpOnly, Secure, SameSite.
  • Comprobar que los testigos de sesión no se exponen en URL u otras ubicaciones inseguras.
// Malas prácticas
public string GenerateSimpleToken() {
    return Guid.NewGuid().ToString().Substring(0, 8);  // Too short and not complex enough
}

// Buenas prácticas
public string GenerateSecureToken() {
    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) {
        byte[] tokenData = new byte[64]; // 512 bits
        rng.GetBytes(tokenData);
        return Convert.ToBase64String(tokenData);  // Long and complex
    }
}

Garantizar que los testigos de sesión tengan la longitud y complejidad suficientes para evitar ataques de adivinación

Expiración e invalidación de sesiones:

  • Implemente políticas de caducidad de sesión para limitar la duración de las sesiones, reduciendo el riesgo de secuestro.
  • Compruebe que las sesiones se invalidan cuando el usuario cierra la sesión o está inactivo.
  • Garantizar que los testigos de sesión se invalidan al cambiar la contraseña u otros eventos de seguridad críticos.
  • Implementar mecanismos para detectar y terminar sesiones duplicadas o concurrentes del mismo usuario.
  • Implementar tiempos de espera de sesiones inactivas para cerrar automáticamente la sesión de los usuarios tras un periodo de inactividad.
  • Verificar que los tiempos de espera de las sesiones están configurados adecuadamente para equilibrar la seguridad y la comodidad del usuario.
// Malas prácticas
public void ChangePassword(string newPassword) {
    // Password is changed, but session remains valid
    UpdatePassword(newPassword);
}

// Buenas prácticas
public void Logout() {
    Session.Abandon();  // Invalidate the session upon logout
}

public void ChangePassword(string newPassword) {
    UpdatePassword(newPassword);
    Session.Abandon();  // Invalidate the session after a password change
}

Invalidación de tokens de sesión ante eventos sensibles a la seguridad para evitar el secuestro de sesiones

Auditoría y registro:

  • Asegúrese de que los eventos de autenticación y autorización se registran con fines de supervisión y auditoría.
  • Verificar que los registros incluyen detalles como el ID de usuario, la acción realizada y la marca de tiempo.
  • Garantizar que los registros no contengan información confidencial y se almacenen de forma segura.

Seguridad de las funciones administrativas:

  • Garantizar que las funciones e interfaces administrativas estén protegidas con mecanismos de autenticación y autorización sólidos.
  • Compruebe que las acciones de administración se registran y supervisan para detectar actividades sospechosas.
  • Compruebe si se han implementado medidas de seguridad adicionales, como MFA y listas blancas de IP, para las cuentas de administrador.
  • Asegúrese de que las funciones administrativas están segmentadas y sólo son accesibles para los usuarios autorizados.

Protección de datos

La protección de datos garantiza que la información sensible se maneja de forma segura a lo largo de todo su ciclo de vida, desde su recogida y almacenamiento hasta su tratamiento y eliminación. A continuación le explicamos cómo puede proteger los datos de sus usuarios para evitar accesos no autorizados, infracciones y fugas de datos.

Cifrado de datos:

  • Asegúrese de que los datos confidenciales se cifran tanto en reposo como en tránsito utilizando algoritmos de cifrado potentes (AES-256, TLS 1.2/1.3) y protocolos de comunicación (HTTPS, SSH).
  • Verificar que las claves de cifrado se gestionan de forma segura, con prácticas adecuadas de rotación y almacenamiento de claves.
  • Garantizar que sólo el personal autorizado tenga acceso a las claves de cifrado y otras credenciales de seguridad.
  • Garantizar que los campos de la base de datos que contienen información confidencial están cifrados.
// Malas prácticas
string encryptionKey = "1234567890123456";  // Hard-coded encryption key

//Buenas prácticas
// Using Azure Key Vault for managing encryption keys
var keyVault = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(securityTokenService));
var secret = await keyVault.GetSecretAsync("https://myvault.vault.azure.net/secrets/mysecret/");

Utilizar un sistema seguro de gestión de claves, como Azure Key Vault o AWS Key Management Service, para gestionar las claves de cifrado

Enmascaramiento y anonimización de datos:

  • Asegúrese de que los datos sensibles se enmascaran o anonimizan cuando se muestran en interfaces de usuario o registros.
  • Implementar técnicas de enmascaramiento de datos para entornos que no sean de producción para evitar la exposición de datos reales.
  • Verificar la eficacia de las técnicas de anonimización para evitar la reidentificación de personas.
  • Garantizar que los datos sensibles no queden expuestos en mensajes de error o registros de depuración.

Almacenamiento seguro de datos:

  • Verificar que los datos sensibles se almacenan en entornos seguros y de acceso controlado.
  • Asegúrese de que las bases de datos y los sistemas de archivos están configurados con los parámetros de seguridad adecuados.
  • Examinar si se aplican planes de copia de seguridad y recuperación en caso de catástrofe que incluyan el almacenamiento seguro de los datos de copia de seguridad.
  • Probar los procesos de copia de seguridad y recuperación para garantizar que los datos pueden restaurarse de forma segura.

Integridad de los datos:

  • Aplicar controles para garantizar la integridad de los datos durante su almacenamiento y transmisión.
  • Verificar el uso de hashes criptográficos para detectar la manipulación de datos.
  • Garantizar que los procesos de validación de datos incluyan comprobaciones de integridad.
  • Supervisar y registrar los problemas de integridad de los datos con fines de auditoría y resolución de problemas.

Retención y eliminación de datos:

  • Garantizar el cumplimiento de las políticas de retención de datos, almacenando los datos sólo el tiempo necesario.
  • Comprobar si existen técnicas seguras de eliminación de datos para eliminar de forma permanente los datos confidenciales cuando ya no se necesiten.
  • Verificar que los procesos de eliminación de datos son exhaustivos y están documentados.

Cumplimiento de la normativa:

  • Asegurarse de que la aplicación cumple con las normativas de protección de datos pertinentes, como GDPR, CCPA e HIPAA.
  • Verificar la implementación de mecanismos de consentimiento del usuario para la recopilación y el procesamiento de datos.
  • Garantizar que los usuarios puedan ejercer sus derechos de protección de datos, como el acceso, la corrección y la supresión.
  • Comprobar si las prácticas y políticas de protección de datos están documentadas para demostrar su cumplimiento durante las auditorías.

Supervisión y registro:

  • Comprueba si se han implantado sistemas de supervisión para detectar accesos no autorizados a datos sensibles.
  • Revise los registros en busca de indicios de violaciones de datos u otros incidentes de seguridad.

Respuesta a la violación de datos:

  • Asegúrese de que existe un plan de respuesta a la violación de datos y de que se comprueba periódicamente.
  • Verificar que el plan incluye procedimientos para identificar, contener y mitigar las violaciones de datos.

Tratamiento de datos por terceros:

  • Asegurarse de que los proveedores de servicios externos cumplen los requisitos de protección de datos.
  • Verificar que los datos compartidos con terceros se encriptan y se transmiten de forma segura.
  • Comprobar si se aplican acuerdos de intercambio de datos que describan responsabilidades y medidas de seguridad.

Tratamiento de errores

Una gestión de errores adecuada garantiza que la aplicación gestione los errores con elegancia, mantenga la funcionalidad y no exponga información confidencial. ¿Cuenta su aplicación con sólidos mecanismos de gestión de errores? Ya es hora de averiguarlo.

Gestión eficaz de errores:

  • Asegúrese de que la aplicación gestiona los errores con elegancia sin exponer información confidencial.
  • Compruebe que se muestran mensajes de error fáciles de usar, evitando detalles técnicos que podrían ayudar a los atacantes.
  • Compruebe que los mecanismos de gestión de errores evitan caídas de la aplicación y mantienen la funcionalidad en la medida de lo posible.

Errores de validación de entrada:

  • Validar que todos los errores de validación de entrada se gestionan adecuadamente, proporcionando información clara a los usuarios.
  • Garantizar que los errores de validación no revelen información sensible o detalles lógicos de la aplicación.
  • Compruebe que los errores de validación se gestionan de forma coherente en toda la aplicación.
// Malas prácticas
if (!UserExists(username)) {
    return Error("Username not found.");
}
if (!CorrectPassword(username, password)) {
    return Error("Incorrect password.");
}

// Buenas prácticas
if (!Authenticate(username, password)) {
    return Error("Invalid username or password.");
}

Utilizar mensajes de error genéricos que no revelen qué parte de la autenticación falló

Gestión de excepciones:

  • Compruebe que todas las excepciones se capturan y gestionan correctamente, evitando que la aplicación se bloquee.
  • Asegúrese de que los bloques try-catch se utilizan adecuadamente para gestionar las excepciones sin exponer los detalles al usuario.
  • Compruebe si se han implementado gestores de excepciones globales para gestionar errores inesperados y registrarlos de forma segura.

Gestión de recursos:

  • Asegurarse de que recursos como conexiones a bases de datos, archivos y sockets de red se cierran y liberan correctamente después de que se produzca un error.
  • Compruebe que se evitan las fugas de recursos mediante la implementación de una gestión de errores sólida para la gestión de recursos.

Escenarios específicos de gestión de errores:

  • Validar que la aplicación gestiona correctamente escenarios de error específicos, como fallos de red, errores de base de datos y cortes de servicios de terceros.
  • Asegúrese de que existen mecanismos de emergencia para mantener la funcionalidad de la aplicación durante estos errores.

Registro

Las prácticas de registro seguras son cruciales para supervisar el funcionamiento de la aplicación, diagnosticar problemas y respaldar las investigaciones de seguridad. Recuerde que los registros deben ser detallados, seguros y conformes a la normativa.

Sensibilidad de los registros:

  • Asegúrese de que nunca se registra información confidencial, como contraseñas, claves API y datos personales.
  • Compruebe que los registros se desinfectan para eliminar o enmascarar cualquier información confidencial antes de su almacenamiento.
  • Compruebe si existen políticas de registro que definan qué información puede registrarse y cuál no.

Registros completos y detallados:

  • Asegurarse de que los registros capturan suficientes detalles para diagnosticar problemas y rastrear actividades.
  • Verificar que los registros incluyen información crítica como marcas de tiempo, ID de usuario, direcciones IP y detalles de errores.
  • Garantizar que las entradas de registro sean coherentes y estén estructuradas para facilitar el análisis.
// Malas prácticas
Log.Error("Operation failed"); // Bad: No context or details provided

// Buenas prácticas
Log.Error($"Operation failed at {DateTime.UtcNow} by UserID {userID}. Error: {errorDetail}");

Los registros que carecen de los detalles necesarios dificultan la resolución de problemas

Seguridad del almacenamiento de registros:

  • Verificar que los registros se almacenan en una ubicación segura con acceso restringido.
  • Garantizar que los registros están protegidos contra la manipulación mediante la aplicación de controles de integridad o protecciones criptográficas.
  • Comprobar si se aplican políticas de retención para archivar o eliminar de forma segura los registros tras un periodo determinado.
// Malas prácticas
var logFilePath = @"C:\SharedFolder\applog.txt";

// Buenas prácticas
var logFilePath = @"C:\SecureLogs\applog.txt";
FileSystemAccessRule rule = new FileSystemAccessRule("LogAccessGroup", FileSystemRights.ReadData, AccessControlType.Allow);

Almacenamiento de registros en una carpeta compartida frente al almacenamiento de registros en una ubicación segura con acceso restringido en función de las funciones

Supervisión de registros y alertas:

  • Asegurarse de que los mecanismos de registro incluyen supervisión en tiempo real para detectar y responder a los eventos de seguridad.
  • Verificar que las alertas están configuradas para errores críticos, incidentes de seguridad y actividades sospechosas.
  • Compruebe si se han implementado herramientas de supervisión para analizar los datos de registro y generar información procesable.

Rotación y mantenimiento de registros:

  • Comprobar si se aplican políticas de rotación de registros para gestionar el tamaño de los archivos de registro y mantener el rendimiento.
  • Garantizar que los registros antiguos se archivan de forma segura y pueden recuperarse para su análisis en caso necesario.
  • Verificar que los procesos de rotación y archivo de registros no interrumpan la funcionalidad de registro.

Bibliotecas de terceros y dependencias

Para crear funciones más rápidamente, los desarrolladores suelen utilizar bibliotecas de terceros. Pero al igual que un ladrillo defectuoso debilita un muro, una biblioteca vulnerable o una integración insegura pueden exponer tu aplicación. Un ejemplo de ello es Cylance, cuyos datos fueron robados de una plataforma de terceros. Por eso los componentes externos deben mantenerse adecuadamente para minimizar estas consecuencias.

Gestión de dependencias:

  • Asegúrese de que todas las bibliotecas y dependencias de terceros se gestionan mediante un gestor de paquetes, como npm, pip o Maven.
  • Compruebe que existe una política clara para añadir nuevas dependencias, incluidas evaluaciones y aprobaciones de seguridad.
  • Comprobar la presencia de un archivo de manifiesto de dependencias (por ejemplo, package.json, requirements.txt) que enumere todas las dependencias y sus versiones.

Control de versiones y actualizaciones:

  • Compruebe si las bibliotecas de terceros están actualizadas a las últimas versiones seguras.
  • Compruebe que las actualizaciones de dependencias se prueban en un entorno de ensayo antes de desplegarse en producción.
  • Supervise los avisos de seguridad y las bases de datos de vulnerabilidades (por ejemplo, CVE, NVD) en busca de actualizaciones de las dependencias utilizadas.
  • Utilizar herramientas automatizadas de análisis de vulnerabilidades, como Snyk, Dependabot, OWASP Dependency-Check, para detectar vulnerabilidades conocidas en bibliotecas de terceros.
  • Compruebe la reputación y la actividad de mantenimiento de los autores y colaboradores de la biblioteca.

Cumplimiento de licencias:

  • Garantizar que todas las bibliotecas de terceros cumplen las políticas de licencias de la organización.
  • Verifique que las licencias de todas las dependencias estén documentadas y revisadas para comprobar su conformidad legal.
  • Vigile las bibliotecas con licencias restrictivas o incompatibles que puedan dar lugar a problemas legales.

Aislamiento y Sandboxing:

  • Compruebe si las bibliotecas de terceros están aisladas en un entorno controlado para limitar su impacto en la aplicación.
  • Verificar si se aplican técnicas de sandboxing para ejecutar código de terceros con permisos restringidos.
  • Asegurarse de que los componentes de terceros no tienen acceso innecesario a datos confidenciales o recursos del sistema.

Revisión y auditoría del código:

  • Revisar el código fuente de las bibliotecas de terceros críticas para identificar posibles problemas de seguridad.
  • Garantizar que las bibliotecas de terceros se someten a auditorías de seguridad periódicas por parte de sus mantenedores o auditores externos.
  • Compruebe que las bibliotecas disponen de un proceso transparente para informar y abordar las vulnerabilidades de seguridad.

Configuración y personalización:

  • Asegúrese de que las bibliotecas de terceros están configuradas de forma segura de acuerdo con las mejores prácticas.
  • Si procede, inspeccione todas las personalizaciones y revise sus implicaciones para la seguridad.

Supervisión y alertas:

  • Compruebe si se han implementado herramientas de supervisión para realizar un seguimiento del rendimiento y el comportamiento de las bibliotecas de terceros en producción.
  • Compruebe si se han configurado alertas para actividades anómalas o problemas de rendimiento relacionados con componentes de terceros.
  • Revise los registros y las métricas para identificar posibles incidentes de seguridad relacionados con bibliotecas de terceros.

Auditoría de seguridad del código: Qué esperar de este servicio

Su producto necesita una exhaustiva auditoría de seguridad del código. Está comparando proveedores, pero quizá se pregunte: ¿qué obtendré exactamente? ¿Cómo utilizo los resultados? Nosotros podemos ayudarle. En Redwerk, desglosamos los resultados de la auditoría de código en pasos claros y viables.

1. Informe fácil de entender

Le proporcionamos una lista de problemas con explicaciones y los motivos por los que son importantes. El informe está bien estructurado y se divide en revisión de la arquitectura, revisión de la base de datos, calidad del código, cobertura de las pruebas y revisión de la seguridad.

Este informe se convierte en su hoja de ruta para solucionar problemas y mejorar su producto. Por ejemplo, ayudamos a Complete Network a identificar un problema de seguridad crítico relacionado con el almacenamiento de datos confidenciales en una carpeta de acceso público. También compartimos tres formas sencillas de mejorar el rendimiento de la aplicación junto con otras mejoras en la calidad del código, lo que se tradujo en un aumento del 80% en la capacidad de mantenimiento.

2. Recomendaciones de los expertos

No nos limitamos a señalar problemas, sino que ofrecemos soluciones. Nuestro informe incluye recomendaciones sobre cómo solucionar cada problema y mejorar la calidad general del código. Además, clasificamos las vulnerabilidades en función de su gravedad, para que sepa qué debe solucionar primero.

Por ejemplo, al revisar una aplicación de mapeo de redes, identificamos que la calidad del código era la principal culpable. La arquitectura era sólida y no había problemas de seguridad. Sin embargo, el código backend estaba escrito de una forma que encarecería el desarrollo futuro y la incorporación de nuevos desarrolladores.

3. Arréglelo usted mismo o deje que le ayudemos

Usted tiene el control Utiliza nuestro informe con cualquier desarrollador: tu equipo interno, autónomos o cualquiera en quien confíes. Pero si quieres que te echemos una mano, podemos solucionar los problemas nosotros mismos. Nuestro informe incluye incluso una estimación del tiempo necesario para solucionar todos los errores.

Detenga los problemas de código antes de que empiecen. Póngase en contacto con nosotros para obtener una imagen clara del estado de su aplicación.