Crear programas C++ confiables y seguros
La publicación del gobierno de Los Estados Unidos NISTIR 8397: Guidelines on Minimum Standards for Developer Verification of Software contiene una excelente guía sobre cómo crear software confiable y seguro en cualquier lenguaje de programación.
Este documento sigue la misma estructura que NISTIR 8397. Cada sección:
- resume cómo usar productos para desarrolladores de Microsoft para C++ y otros lenguajes para satisfacer las necesidades de seguridad de esa sección, y
- proporciona instrucciones para obtener el máximo valor en cada área.
2.1 Modelado de amenazas
Resumen
El modelado de amenazas es un proceso valioso, especialmente cuando se aplica de una manera que se escala para satisfacer sus necesidades de desarrollo y que reduce el ruido.
Recomendaciones
El modelado de amenazas debe ser una parte del ciclo de vida dinámico de desarrollo de seguridad (SDL). Se recomienda que para su producto en su conjunto, para una característica específica o para un cambio importante de diseño o implementación:
- Tener un SDL sólido y dinámico que permita la interacción temprana con los equipos de desarrolladores y la asignación de derechos de enfoque.
- Aplicar el modelado de amenazas de forma específica. Aplique el modelado de amenazas a todas las características, pero comience tácticamente con características expuestas, complejas o críticas. Aplicarlo regularmente en su lugar como parte de una revisión de producto de arriba abajo.
- Aplique el modelado de amenazas al principio (al igual que con todos los requisitos de seguridad), cuando todavía hay oportunidad de cambiar el diseño. Además, los modelos de amenazas sirven como entrada para otros procesos, como la reducción de la superficie expuesta a ataques o el diseño para la seguridad. Los modelos de amenazas que se crean más adelante se encuentran en las mejores "encuestas" para la prueba de lápiz (pruebas de penetración) o áreas que necesitan pruebas de seguridad como el desconcertado. Después de crear un modelo de amenaza de línea base, planee iterar en él a medida que cambia la superficie expuesta a ataques.
- Use el inventario de activos y el cumplimiento para realizar un seguimiento adecuado de lo que constituye un producto y realizar un seguimiento de los artefactos de seguridad (incluidos los modelos de amenazas) junto con los recursos a los que se aplican. Este enfoque permite una mejor evaluación de riesgos automatizada y centrarse en los esfuerzos de seguridad en los componentes o características específicos que cambian.
- En Azure, Microsoft Threat Modeling Tool se actualizó en 2022 para el desarrollo de Azure. Para más información, vea Microsoft Threat Modeling Tool: Azure
Factores y procedimientos auxiliares
Para aplicar correctamente el modelado de amenazas y evitar el uso excesivo o el uso insuficiente, hemos encontrado que primero se deben abordar los siguientes conceptos básicos.
Enfoque de desarrollo
En primer lugar, comprenda el enfoque de desarrollo del equipo. Para los equipos con flujos de trabajo de desarrollo ágiles que insertan docenas de cambios en producción diariamente, no es práctico o razonable requerir una actualización del modelo de amenazas para cada cambio funcional. En su lugar, desde el principio al escribir los requisitos funcionales de una característica, considere la posibilidad de incluir un cuestionario de requisitos de seguridad. El cuestionario debe centrarse en preguntas específicas sobre la característica para determinar qué aspectos futuros de su SDL se aplican. Por ejemplo:
- ¿La característica realiza un cambio importante en el diseño de cómo proporcionamos aislamiento de clientes en un entorno multiinquilino? Si es así, considere la posibilidad de realizar un modelo de amenazas completo.
- ¿Una nueva característica permite cargas de archivos? Si es así, quizás lo más adecuado es una evaluación de seguridad de aplicaciones web.
- ¿Este cambio es principalmente un cambio funcional de la interfaz de usuario? Si es así, quizás no se necesite nada más allá de las herramientas automatizadas tradicionales.
Los resultados del cuestionario de seguridad informan sobre qué técnicas de SDL se deben vincular a qué unidad de desarrollo. También informa a los asociados de desarrollo de las escalas de tiempo de SDL de la característica, por lo que pueden colaborar en los momentos adecuados.
Inventario de productos
En segundo lugar, mantenga un inventario de activos sólido de los productos que se le encargan de evaluar. Los productos están creciendo en complejidad. Es habitual escribir software para dispositivos conectados que tienen:
- sensores (como el tren de pasajeros y los vehículos),
- redes basadas en bus que hablan con otros componentes del vehículo (como CANBUS o PROFIBUS),
- wireless/cellular/Bluetooth para la comunicación con dispositivos de cliente y back-end en la nube,
- aprendizaje automático en la nube que se alimenta de nuevo en el dispositivo o en una aplicación de administración de flotas,
- etcétera.
En estos productos complejos, el modelado de amenazas es fundamental. Tener un inventario de activos sólido le permite ver toda la pila de productos para ver la imagen completa y ver los lugares clave que deben evaluarse para ver cómo una característica nueva o modificada afecta a la seguridad del producto.
Granularidad e integración
Establezca sistemas para medir el cumplimiento mediante métricas claras.
- Mida periódicamente el cumplimiento del desarrollo de nivel de características. Por lo general, el cumplimiento de características debe medirse con mayor frecuencia y granularidad más pequeña, a veces incluso en el sistema del desarrollador o en el tiempo de confirmación y combinación de código.
- Evalúe periódicamente la seguridad del producto más amplio en el que se consume una característica o componente. Normalmente, las evaluaciones más amplias se realizan con una frecuencia más baja y una granularidad más amplia, como en el tiempo de prueba del módulo o del sistema.
Escala
Mantenga un sistema de inventario de recursos adecuado que capture y conserve los artefactos de seguridad y la salida de las revisiones del modelo de amenazas. Tener un inventario claro le permite evaluar las salidas de revisión de los patrones y tomar decisiones inteligentes sobre cómo refinar el programa de seguridad del producto con regularidad.
Intente combinar cuestionarios de seguridad en fase de requisitos, resultados de modelado de amenazas, resultados de evaluación de seguridad y resultados de herramientas automatizadas. Combinarlos le permite automatizar un punto de vista del riesgo relativo de un producto determinado, idealmente como "panel", para informar a los equipos de seguridad sobre qué centrarse para sacar el mejor valor del modelado de amenazas.
2.2 Pruebas automatizadas
Resumen
Las pruebas automatizadas son una manera importante de garantizar la calidad y la seguridad del código. Son una pieza integral para admitir otras áreas mencionadas en este documento, como el modelado de amenazas. Cuando se emparejan con otras prácticas de codificación seguras, ayudan a protegerse frente a errores y vulnerabilidades que se introducen en el código base.
Atributos de la clave
Las pruebas deben ser confiables, coherentes y aisladas. Estas pruebas deben abarcar la mayor parte del código posible. Todas las nuevas características y correcciones de errores deben tener pruebas correspondientes para garantizar la seguridad y confiabilidad a largo plazo del código siempre que sea posible. Ejecute pruebas automatizadas periódicamente y en tantos entornos como sea posible, para asegurarse de que se ejecutan y que cubren todas las áreas:
- El primer lugar en el que deben ejecutarse es en el equipo que realiza los cambios. La ejecución de pruebas se realiza con mayor facilidad en el IDE que se usa para editar o como script en la línea de comandos, ya que el desarrollador realiza los cambios.
- El siguiente lugar en el que deben ejecutarse es como parte del proceso de confirmación o combinación de solicitudes de incorporación de cambios.
- El último lugar para ejecutar pruebas es como parte de una canalización de integración continua e implementación continua (CI/CD) o en las compilaciones candidatas para lanzamiento.
El ámbito de las pruebas debe aumentar en cada paso, con el último paso que proporciona cobertura completa para cualquier otro paso que pueda perderse.
uso continuo y de mantenimiento
La confiabilidad de las pruebas es una parte importante del mantenimiento de la eficacia del conjunto de pruebas. Los errores de prueba deben asignarse e investigarse, con posibles problemas de seguridad que obtienen una prioridad alta y se actualizan dentro de un período de tiempo de solicitud y predeterminado. Ignorar los errores de prueba no debe ser una práctica común, pero debe requerir una justificación y aprobación sólidas. Los errores de prueba debidos a problemas dentro del propio conjunto de pruebas deben tratarse igual que otros errores, para evitar un error en la cobertura en la que se podrían perder los problemas del producto.
Tipos de pruebas, especialmente pruebas unitarias
Hay varios tipos de pruebas automatizadas y, aunque no todos son aplicables a todas las aplicaciones, un buen conjunto de pruebas contiene una selección de varios tipos diferentes. Los casos de prueba basados en código, como las pruebas unitarias, son los más comunes y integrales, siendo aplicables a todas las aplicaciones e incluyen intencionadamente tantas rutas de acceso de código como sea posible para la corrección. Estas pruebas deben ser pequeñas, rápidas y no afectar al estado de la máquina, de modo que el conjunto completo de pruebas se pueda ejecutar rápidamente y a menudo. Si es posible, ejecute pruebas en muchas máquinas que tengan configuraciones de hardware diferentes para detectar problemas que no se pueden reproducir en un solo tipo de máquina.
Visual Studio
El Explorador de pruebas de Visual Studio admite de forma nativa muchos de los marcos de pruebas de C++ más populares y tiene opciones para instalar extensiones para más marcos. Esta flexibilidad resulta útil para ejecutar un subconjunto de pruebas que abarcan el código en el que se trabaja y facilita la depuración de errores de prueba a medida que surgen. Visual Studio también facilita la configuración de nuevos conjuntos de pruebas para proyectos existentes y proporciona herramientas útiles como CodeLens para facilitar la administración de estas pruebas. Para obtener más información sobre cómo escribir, ejecutar y administrar pruebas de C/C++ con Visual Studio, vea Escribir pruebas unitarias para C/C++ - Visual Studio (Windows).
En CI/CD de Azure y GitHub
Las pruebas que realizan una comprobación más profunda y tardan más tiempo en ejecutarse, como el análisis estático, la detección de componentes, etc., son buenos candidatos para pruebas de solicitudes de incorporación de cambios o pruebas de integración continua. Azure DevOps y GitHub Actions facilitar la ejecución de validaciones automáticamente y bloquear las comprobaciones de código si se produce un error en la validación. El cumplimiento automatizado ayuda a asegurarse de que todo el código que se protege se basa en estas comprobaciones más rigurosas que se ejecutan. La validación de compilación de Azure Pipelines y Azure DevOps se describen aquí:
- Configuración y directivas de rama de Git: Azure Repos
- Definición de la combinabilidad de las solicitudes de incorporación de cambios | GitHub Docs
2.3 Análisis estático o basado en código
Resumen El código estático o el análisis binario deben estar habilitados de forma predeterminada para ser seguros de forma predeterminada. El análisis estático analiza un programa para las directivas de seguridad y seguridad necesarias en el momento en que se compila, no en tiempo de ejecución cuando se puede producir una vulnerabilidad de seguridad en el equipo del cliente. El análisis estático puede analizar el programa en formato de código fuente o en formato ejecutable compilado.
Recomendaciones Microsoft recomienda:
- Habilite el análisis estático para todos los programas de C++, tanto para el código fuente de entrada (antes de la compilación) como para los archivos binarios ejecutables (después de la compilación). "Habilitar" puede significar ejecutar análisis durante cada compilación en la máquina del desarrollador, o como una compilación independiente para inspeccionar el código más adelante o como requisito de comprobación.
- Incorpore el análisis estático en canalizaciones de CI como una forma de prueba.
- El análisis estático por definición incluye falsos positivos y estar preparado para incorporar ese hecho en el bucle de comentarios de calidad. Sea rápido para habilitar todas las advertencias de falsos positivos por adelantado. A continuación, sea proactivo para aumentar gradualmente el número de reglas para las que la base de código compila la limpieza de advertencias a medida que se agregan regularmente más reglas que marcan errores importantes a costa de falsos positivos gradualmente más altos (inicialmente, antes de que también se limpie la base de código para esas reglas).
- Use siempre las versiones compatibles más recientes de Visual Studio y configure el entorno de ingeniería para consumir rápidamente las versiones de revisión más recientes en cuanto estén disponibles, sin demorar en la siguiente fase de desarrollo o ciclo.
Herramientas clave Tener en cuenta y usar lo siguiente:
- Documentación de análisis de código: C++ y .NET
/analyze
: Compilador de Visual C++/W4
y/WX
: Compilador de Visual C++- Usar los comprobadores C++ Core Guidelines
- CodeQL | GitHub
- Guía del usuario de Binskim | GitHub
- Vea también (solo Windows): anotaciones SAL
Notas:
/analyze
permite el análisis estático del código de C++ en tiempo de compilación para identificar vulnerabilidades críticas de código de seguridad y confiabilidad. Debe habilitarse en toda la escala de tiempo de desarrollo de un programa de C++. Para empezar, habilite al menos "Microsoft Native Recommended" de forma predeterminada como línea base mínima. A continuación, consulte la documentación sobre cómo especificar más reglas, especialmente las reglas C++ Core Guidelines, según lo requieran las directivas de ingeniería. La funcionalidad Análisis estático de código fuente está disponible tanto en el IDE de Visual C++ como en las herramientas de compilación de la línea de comandos./W4
y/WX
deben habilitarse siempre que sea posible, para asegurarse de compilar el código de forma limpia en niveles altos de advertencia (W4
) y tratar las advertencias como errores que deben corregirse (WX
). Estas opciones permiten buscar errores de datos no inicializados que otras herramientas de análisis estáticos no pueden comprobar, ya que los errores solo se vuelven visibles después de que el back-end del compilador realice el análisis interprocedural y la inserción.- El análisis binario BinSkim garantiza que los proyectos permitan una amplia gama de características de seguridad. BinSkim genera PDB y otras salidas que facilitan la comprobación de la cadena de custodia y la respuesta eficaz a los problemas de seguridad. Microsoft recomienda ejecutar la herramienta BinSkim para analizar todos los archivos binarios ejecutables (
.sys
,.dll
o.exe
) generados o consumidos por los programas. La Guía del usuario de BinSkim incluye una lista de los estándares de seguridad admitidos. Microsoft recomienda corregir todos los problemas notificados como "errores" por la herramienta BinSkim. Los problemas notificados como "advertencias" deben evaluarse de forma selectiva, ya que resolverlos puede tener implicaciones de rendimiento o puede que no sea necesario.
En Azure y GitHub CI/CD Microsoft recomienda habilitar siempre el código fuente y el análisis estático binario en escenarios de CI/CD de versión. Ejecute el análisis de código fuente inmediatamente en el equipo del desarrollador local, o al menos para cada confirmación o solicitud de incorporación de cambios, para detectar los errores de origen lo antes posible y minimizar los costos generales. Los errores de nivel binario tienden a introducirse más lentamente, por lo que puede ser suficiente ejecutar análisis binarios en escenarios de CI/CD de versión preliminar menos frecuentes (como compilaciones nocturnas o semanales).
2.4 Revisión de secretos codificados de forma codificada
Resumen
No codifique los secretos de forma dura en el software. Puede encontrar y quitar secretos del código fuente de forma eficaz mediante herramientas confiables que pueden examinar toda la base de código fuente. Una vez que encuentre secretos, muévalos a un lugar seguro siguiendo las directrices para el almacenamiento seguro y el uso de secretos.
Problema
"Secretos" significa entidades que establecen la identidad y proporcionan acceso a los recursos, o que se usan para firmar o cifrar datos confidenciales. Algunos ejemplos son las contraseñas, las claves de almacenamiento, las cadenas de conexión y las claves privadas. Es tentador mantener secretos en el producto de software para que se puedan obtener fácilmente cuando lo necesite el software. Sin embargo, estos secretos codificados de forma dura pueden provocar incidentes de seguridad graves o catastróficos, ya que se detectan fácilmente y se pueden usar para poner en peligro el servicio y los datos.
Prevención
Los secretos codificados de forma rígida en el código fuente (como texto sin formato o blob cifrado) son una vulnerabilidad de seguridad. Estas son instrucciones generales sobre cómo evitar secretos en el código fuente:
- Use una herramienta de comprobación previa para examinar y detectar posibles secretos codificados de forma rígida en el código antes de enviarlos al control de código fuente.
- No coloque credenciales de texto no cifrado en el código fuente ni en los archivos de configuración.
- No almacene credenciales de texto no cifrado en SharePoint, OneNote, recursos compartidos de archivos, etc. O compártelos por correo electrónico, mensajería instantánea, etc.
- No cifre un secreto con una clave de descifrado fácil de detectar. Por ejemplo, no almacene un archivo PFX junto con un archivo que contenga su contraseña.
- No cifre un secreto con un descifrado débil. Por ejemplo, no cifre un archivo PFX con una contraseña débil o común.
- Evite colocar credenciales cifradas en el código fuente. En su lugar, use marcadores de posición en el origen y deje que el sistema de implementación los reemplace por secretos de almacenes aprobados.
- Aplique los mismos principios a los secretos en entornos como pruebas, almacenamiento provisional, etc., como lo hace en las implementaciones de producción. A menudo, los adversarios tienen como destino sistemas que no son de producción, ya que son menos administrados y, a continuación, los usan para dinamizar en producción.
- No comparta secretos entre implementaciones (por ejemplo, pruebas, ensayo, producción).
Aunque no está relacionado directamente con los secretos codificados de forma codificada, recuerde también proteger los secretos para la prueba, el desarrollo y la producción:
- Rotar los secretos periódicamente y cada vez que se hayan expuesto. Tener una capacidad demostrada para rotar o volver a implementar secretos es evidencia de un sistema seguro. En particular, la ausencia de esta capacidad es aún más sólida evidencia de una vulnerabilidad inevitable.
- No entrecese a la lógica común del desarrollador de que "mis credenciales de prueba no crean riesgo". En la práctica, casi siempre lo hacen.
- Considere la posibilidad de alejarse de secretos (por ejemplo, contraseñas, claves de portador) en su totalidad en preferencia de las soluciones controladas por RBAC/identity como una buena solución de ingeniería que puede dar lugar a una mala administración de secretos por completo.
Detección
Los componentes heredados del producto pueden contener secretos codificados de forma rígida ocultos en su código fuente. A veces, los secretos de las máquinas de escritorio de los desarrolladores pueden entrar en la rama remota y combinarlos en la rama de versión, lo que filtra los secretos involuntariamente. Para detectar secretos que podrían ocultarse en el código fuente, puede usar herramientas que puedan examinar el código para detectar secretos codificados de forma rígida:
Corrección
Cuando se encuentran credenciales en el código fuente, la necesidad urgente inmediata es invalidar la clave expuesta y realizar un análisis de riesgos basado en la exposición. Incluso si el sistema necesita mantenerse en ejecución, puede habilitar un administrador de secretos para la corrección mediante estos pasos:
- Si la corrección permite cambiar a identidades administradas o requiere quitar en un administrador de secretos, como Azure Key Vault (AKV), hágalo primero. A continuación, vuelva a implementar con la identidad o clave actualizadas.
- Invalide el secreto expuesto.
- Realizar una evaluación de riesgos o auditorías de posibles daños debidos a riesgos.
Para proteger las claves criptográficas y otros secretos que usan las aplicaciones y servicios en la nube, use Azure Key Vault con una directiva de acceso adecuada.
Si una exposición pone en peligro determinados datos o PII del cliente, podría requerir otros requisitos de cumplimiento o informes.
Quite los secretos invalidados ahora del código fuente y reemplácelos por métodos alternativos que no expongan los secretos directamente en el código fuente. Busque oportunidades para eliminar secretos siempre que sea posible mediante herramientas como Azure AD. Puede actualizar los métodos de autenticación para aprovechar las ventajas de las identidades administradas a través de Azure Active Directory. Use solo almacenes aprobados para almacenar y administrar secretos como Azure Key Vault (AKV). Para más información, vea:
Azure DevOps (AzDO)
Los usuarios de AzDO pueden examinar su código mediante GitHub Advanced Security para Azure DevOps (GHAzDO). GHAzDO también permite a los usuarios evitar exposiciones secretas habilitando la protección de inserción en sus repositorios, detectando posibles exposiciones antes de que se filtren. Para más información sobre cómo detectar secretos codificados de forma rígida en el código en Azure DevOps, vea Análisis de secretos para Github Advanced Security para Azure DevOps en cada uno de los vínculos siguientes:
- Seguridad avanzada de GitHub para Azure DevOps
- Examen de secretos para GitHub Advanced Security para Azure DevOps
- Versión preliminar de Microsoft Defender para DevOps
In GitHub
El análisis de secretos está disponible en GitHub.com en dos formas:
- Alertas de análisis de secretos para asociados. Se ejecuta automáticamente en todos los repositorios públicos. Cualquier secuencia que coincida con los patrones que hayan proporcionado los socios del escaneo de secretos se reportarán directamente al socio relevante.
- Alertas de examen de secretos para los usuarios. Puede habilitar y configurar el análisis adicional de repositorios que pertenecen a organizaciones que usan GitHub Enterprise Cloud y tienen una licencia para GitHub Advanced Security. Estas herramientas también admiten repositorios privados e internos.
GitHub proporciona patrones conocidos de secretos para asociados y usuarios que se pueden configurar para satisfacer sus necesidades. Para obtener más información, vea:
Nota:
GitHub Advanced Security para Azure DevOps ofrece el mismo análisis secreto, análisis de dependencias y soluciones de análisis de código CodeQL que ya están disponibles para los usuarios de GitHub y los integra de forma nativa en Azure DevOps para proteger Azure Repos y Pipelines.
Recursos adicionales
- Examen de credenciales | Código de Microsoft con cuaderno de estrategias de ingeniería.
- detect-secrets: Herramienta de examen de credenciales | GitHub: un módulo con nombre adecuado para detectar secretos dentro de una base de código.
- Ejecución de detect-secrets en Azure Pipelines.
- Git-secrets | GitHub awslabs: evita que confirme contraseñas y otra información confidencial en un repositorio de Git.
- Administración de secretos | Microsoft Code with Engineering Playbook: proporciona instrucciones generales sobre cómo se deben administrar los secretos.
2.5 Ejecutar con comprobaciones y protección proporcionadas por el sistema operativo y el lenguaje
Resumen
La protección binaria se realiza aplicando controles de seguridad en tiempo de compilación. Entre ellas se incluyen mitigaciones que:
- evitar vulnerabilidades que se puedan aprovechar en el código,
- habilite las detecciones en tiempo de ejecución que desencadenan defensas de seguridad en la explotación y
- habilite la producción y el archivado de datos para ayudar a limitar los daños causados por incidentes de seguridad.
Los consumidores binarios deben participar en las características de seguridad de Windows para obtener la ventaja completa de la protección.
Microsoft proporciona un conjunto de instalaciones específicas de proyectos de C++ para ayudar a los desarrolladores a escribir y enviar código más seguro y seguro. Los desarrolladores de C++ también deben cumplir los estándares de seguridad comunes a los lenguajes que generan código ejecutable. Microsoft mantiene BinSkim, un comprobador binario OSS público que ayuda a aplicar el uso de muchas protecciones descritas en esta sección. Para obtener más información sobre BinSkim, consulte guía de usuario de Binskim | GitHub
Los controles de nivel binario difieren según dónde se aplican en el proceso de ingeniería. Debe distinguir entre las opciones del compilador y del enlazador que: son estrictamente tiempo de compilación, modificar la generación de código con sobrecarga en tiempo de ejecución y modificar la generación de código para lograr la compatibilidad con las protecciones del sistema operativo.
La configuración del desarrollador debe preferir habilitar tanto análisis estático como sea posible, habilitar la producción de datos privados para acelerar la depuración, etc. Las compilaciones de versión se deben ajustar a una combinación adecuada de seguridad, rendimiento y otros problemas de generación de código. Los procesos de versión deben configurarse para generar y administrar correctamente datos de compilación públicos frente a datos de compilación consumidos de forma privada (por ejemplo, símbolos públicos frente a privados).
Mantenerse al día: Use siempre compiladores y herramientas actualizados.
Compile todo el código con conjuntos de herramientas actuales para beneficiarse de la compatibilidad con lenguajes actualizados, el análisis estático, la generación de código y los controles de seguridad. Dado que los compiladores afectan a todos los componentes generados, la posibilidad de regresión en la actualización de herramientas es relativamente alta. El uso de compiladores obsoletos crea un riesgo determinado de acción correctiva al responder a un incidente de seguridad, ya que es posible que los equipos no tengan tiempo suficiente para actualizar los compiladores. Microsoft recomienda que los equipos desarrollen la instalación para actualizar y probar periódicamente las actualizaciones del compilador.
Usar métodos de desarrollo seguros, versiones de lenguaje, marcos o API
El código debe usar metodologías de desarrollo, versiones de lenguaje, marco, API, etc., que minimicen el riesgo fomentando la seguridad y la simplicidad en C++, entre las que se incluyen:
- Vea Biblioteca de compatibilidad de directrices de C++ Core Guidelines (GSL) para obtener instrucciones para escribir código de C++ moderno, seguro y coherente que siga los procedimientos recomendados y evite problemas comunes.
- Vea implementación de Microsoft GSL para funciones y tipos que las C++ Core Guidelines sugieren usar.
- Contenedores de C++ seguros para recursos, protecciones de desbordamiento de memoria de la biblioteca en tiempo de ejecución de C (CRT): se prefieren
std::vector
ystd::string
, que son seguras para recursos. Si debe usar datos de C, use la versiones seguras de las funciones de CRT, que están diseñadas para ayudar a evitar daños en la memoria debido al uso incorrecto del búfer y a comportamientos de lenguaje no definidos. - La biblioteca SafeInt protege contra el desbordamiento entero en operaciones matemáticas y de comparación.
Consumir dependencias seguras
Los archivos binarios no deben vincularse a bibliotecas y dependencias no seguras. Los equipos de desarrollo deben realizar un seguimiento de todas las dependencias externas y resolver las vulnerabilidades de seguridad identificadas o las CVE en estos componentes mediante la actualización a versiones más seguras cuando estén sujetas a esas vulnerabilidades.
Maximizar garantías de procedencia de código y eficacia de la respuesta de seguridad
La compilación debe habilitar garantías de provenencia de código sólidas para ayudar a detectar e impedir la introducción de puertas traseras y otro código malintencionado. Los datos resultantes, también críticos para la depuración y la investigación, deben archivarse para todas las versiones de software para impulsar una respuesta de seguridad eficaz si están en peligro. Los siguientes modificadores del compilador generan información crítica para una respuesta de seguridad:
/ZH:SHA_SHA256
en Visual C++: Garantiza que se usa un algoritmo criptográficomente seguro para generar todos los hashes de archivo de origen de PDB./Zi
,/ZI
(Formato de información de depuración) en visual C++: Además de publicar símbolos eliminados para recopilar datos de bloqueo y otros escenarios de uso público, asegúrese de que las compilaciones generan y archivan archivos PDB privados para todos los archivos binarios publicados. Las herramientas de análisis binario requieren símbolos completos para comprobar si se habilitaron muchas mitigaciones de seguridad en tiempo de compilación. Los símbolos privados son críticos en la respuesta de seguridad y reducen los costos de depuración e investigación cuando los ingenieros compiten por evaluar y limitar los daños cuando se produce una vulnerabilidad de seguridad./SOURCELINK
en Visual C++ Linker: Incluir archivo Sourcelink en PDB: El vínculo de origen es un sistema independiente del control de código fuente y del lenguaje que proporciona depuración de código fuente para archivos binarios. La depuración de origen aumenta considerablemente la eficacia del intervalo de validaciones de seguridad preliminares y respuesta a incidentes posteriores a la versión.
Habilitar errores del compilador para evitar problemas en el tiempo de creación de código
La compilación debe habilitar comprobaciones del compilador relevantes para la seguridad como errores importantes, por ejemplo:
/sdl
en Visual C++: Habilite comprobaciones de seguridad adicionales eleva muchas advertencias relevantes para la seguridad en errores y habilita características avanzadas de generación de código segura.- BinSkim BA2007.EnableCriticalCompilerWarnings | GitHub mantiene una lista de advertencias del compilador de C/C++ recomendadas por Microsoft que siempre deben habilitarse y elevarse a errores.
Marcar archivos binarios como compatibles con mitigaciones de seguridad en tiempo de ejecución del sistema operativo
La configuración del compilador y del enlazador debe participar en características de generación de código que detecten y mitiguen la ejecución de código malintencionada, entre las que se incluyen:
- Prevención de daños en la pila
/SAFESEH
: La imagen tiene controladores de excepciones seguros: Genera una tabla de los controladores de excepciones seguros de la imagen para archivos binarios x86./GS
: Comprobación de seguridad del búfer: Detecta algunas saturaciones del búfer que sobrescriben las direcciones de retorno, las direcciones del controlador de excepciones o determinados tipos de parámetros.
- Colocación de la ejecución de código independiente
/DYNAMICBASE
: Usar selección aleatoria del diseño del espacio de direcciones: Genera imágenes ejecutables que se pueden volver a base aleatoriamente en tiempo de carga./HIGHENTROPVA
y/LARGEADDRESSAWARE
: Admite ASLR de 64 bits y controla direcciones grandes: Permite el uso de todo el espacio de direcciones de 64 bits para la reebasión de imágenes.
- Integridad del flujo de código
/guard:cf
: Habilitar protección de flujo de control: Inserta comprobaciones en tiempo de ejecución para destinos de llamadas indirectas./CETCOMPAT
: Pila de sombras CET compatible: Marca una imagen ejecutable como compatible con la implementación de Microsoft de la tecnología de cumplimiento de flujo de control (CET) de Intel característica Shadow Stack./guard:ehcont
: Habilitar metadatos de continuación de EH: Genera una lista de direcciones virtuales relativas seguras (RVA) de todos los destinos de continuación de control de excepciones.
- Prevención de ejecución de datos
/NXCOMPAT
: Compatible con la prevención de ejecución de datos: Marca una imagen ejecutable de 32 bits como compatible con la característica de prevención de ejecución de datos (DEP) de Windows. Las compilaciones de 64 bits son compatibles con DEP de forma predeterminada.)
Impedir la divulgación de información confidencial
La configuración del compilador debe participar en la prevención de detección de información confidencial. En los últimos años, los investigadores han descubierto fugas de información no deseadas que se originan con características de hardware como la ejecución especulativa.
En el nivel de software, los datos confidenciales se pueden transmitir a los atacantes si se filtran inesperadamente. Si no se inicializan búferes sin inicializar y otro uso incorrecto del búfer, se pueden filtrar datos confidenciales privados a los atacantes que llaman a la API de confianza. Esta clase de problema se controla mejor mediante la habilitación de análisis estáticos adicionales y el uso de contenedores de recursos seguros como se ha descrito anteriormente.
/Qspectre
: Mitigación de ataques de canal lateral de ejecución especulativa: Inserta instrucciones de barrera que ayudan a evitar la divulgación de datos confidenciales producidos por la ejecución especulativa. Estas mitigaciones deben habilitarse para el código que almacena datos confidenciales en la memoria y funciona a través de un límite de confianza. Microsoft siempre recomienda medir el impacto en el rendimiento con las pruebas comparativas adecuadas al habilitar mitigaciones de Spectre debido a la posibilidad de introducir comprobaciones en tiempo de ejecución en bloques o bucles críticos para el rendimiento. Estas rutas de acceso de código pueden deshabilitar las mitigaciones a través del modificadorspectre(nomitigation)
declspec
. Los proyectos que habilitan/Qspectre
también deben vincularse a bibliotecas que también se compilan con estas mitigaciones, incluidas las bibliotecas en tiempo de ejecución de Microsoft.
2.6 Casos de prueba de caja negra
Resumen
Las pruebas de caja negra no dependen de conocer el funcionamiento interno del componente probado. Las pruebas de caja negra están diseñadas para probar la funcionalidad de un extremo a otro de las características del producto en cualquier nivel o nivel. Las pruebas de caja negra pueden ser pruebas funcionales, pruebas de IU, pruebas de rendimiento y pruebas de integración. Las pruebas de caja negra son valiosas para medir la confiabilidad general y la corrección funcional, y asegurarse de que el producto se comporta según lo previsto.
Relación con otras secciones
Estos tipos de pruebas basadas en requisitos son útiles para validar las suposiciones realizadas en el modelo de amenazas y cubrir posibles amenazas como se muestra en esa sección. Estas pruebas son útiles para probar la integración entre componentes independientes del producto, especialmente las que están a través de límites de confianza, como se describe en el modelo de amenazas. Los casos de prueba de caja negra también son útiles para probar todo tipo de casos perimetrales para la validación de entrada del usuario. Probar casos perimetrales conocidos y casos de error son útiles. La separación aproximada también es útil para probar casos menos obvios.
Automatización y regresión
Ejecute estas pruebas periódicamente y compare los resultados con las ejecuciones anteriores para detectar cambios importantes o regresiones de rendimiento. Además, ejecutar estas pruebas en muchas máquinas y configuraciones de instalación diferentes puede ayudar a cubrir cualquier problema que pueda surgir de diferentes arquitecturas o cambios de configuración.
volcados de memoria
Estas pruebas ayudan a encontrar problemas con la confiabilidad, poder probar muchos escenarios diferentes que podrían encontrarse con bloqueos, colapsos, interbloqueos, etc. Al recopilar volcados de memoria como parte de los errores de prueba, puede importar los volcados directamente en Visual Studio para investigar aún más qué partes del código están afectando a estos problemas. Si ejecuta pruebas funcionales desde Visual Studio, puede replicar y depurar fácilmente los errores viendo exactamente dónde dentro del cuadro negro se produce un error en la prueba y puede probar las correcciones rápidamente.
Para empezar a trabajar con las pruebas de depuración, vea Depurar pruebas unitarias con el Explorador de pruebas: Visual Studio (Windows)
En Azure
Azure DevOps también puede ayudar a administrar y validar estas pruebas con el uso de planes de prueba. Estas pruebas se pueden usar para garantizar la aprobación con validación manual y para ejecutar pruebas automatizadas asociadas a los requisitos del producto. Puede encontrar más información sobre Azure Test Plans y su uso para ejecutar pruebas automatizadas aquí:
- ¿Qué es Azure Test Plans? Herramientas de prueba manuales, exploratorias y automatizadas. - Azure Test Plans
- Ejecución de pruebas automatizadas desde planes de prueba: Azure Test Plans
2.7 Casos de prueba basados en código
Resumen
Los casos de prueba basados en código son una parte integral del mantenimiento de la seguridad y confiabilidad del producto. Estas pruebas deben ser pequeñas y rápidas y no deben tener un impacto entre sí para que se puedan ejecutar en paralelo. Las pruebas basadas en código son fáciles para que los desarrolladores se ejecuten localmente en su máquina de desarrollo en cualquier momento que realicen cambios en el código sin preocuparse por ralentizar su ciclo de desarrollo.
Tipos y relación con otras secciones
Entre los tipos comunes de casos de prueba basados en código se incluyen:
- pruebas unitarias,
- pruebas parametrizadas para cubrir funciones con varios tipos de entrada,
- pruebas de componentes para mantener separados cada componente de prueba, y
- pruebas simuladas para validar partes del código que se comunican con otros servicios, sin expandir el ámbito de la prueba para incluir esos servicios.
Estas pruebas se basan en el código interno que se escribe, mientras que las pruebas de caja negra se basan en los requisitos funcionales externos del producto.
Objetivo
A través de estas pruebas, el objetivo es lograr un alto nivel de cobertura de pruebas sobre el código. Debe realizar un seguimiento activo de esta cobertura y dónde existen brechas. A medida que agrega más pruebas que ejercen más rutas de acceso de código, aumenta la confianza general en la seguridad y confiabilidad del código.
Visual Studio
Las herramientas del explorador de pruebas de Visual Studio facilitan la ejecución de estas pruebas con frecuencia y obtienen comentarios sobre las tasas de errores y las tasas de superación y errores rápidamente. Muchos de los marcos de pruebas también admiten características de CodeLens para ver el estado de la prueba en la ubicación de la propia prueba, lo que facilita la adición y el mantenimiento del conjunto de pruebas. El Explorador de pruebas también facilita la administración de estas pruebas, lo que permite grupos de pruebas, listas de reproducción de pruebas personalizadas, filtrado, ordenación, búsqueda, etc.
Para más información, vea:
- Aspectos básicos de las pruebas unitarias: Visual Studio (Windows): introducción e información general
- Ejecutar pruebas unitarias con el Explorador de pruebas: Visual Studio (Windows), una visión más profunda de lo que está disponible para ayudar a administrar el conjunto potencialmente grande de pruebas unitarias con el Explorador de pruebas
Visual Studio también incluye herramientas para realizar el seguimiento de la cobertura de código. Estas herramientas le permiten asegurarse de que los cambios de código que realice están cubiertos por las pruebas existentes o para agregar nuevas pruebas para cubrir rutas de acceso de código nuevas y no probadas. Las herramientas también muestran el porcentaje de cobertura de código para asegurarse de que se mantiene por encima de un nivel de destino para la confianza en la calidad general del código.
Para obtener información sobre estas herramientas, vea Pruebas de cobertura de código: Visual Studio (Windows)
En Azure
Azure DevOps también puede ayudar en el seguimiento de los resultados de cobertura de código para todo el producto como parte del proceso de canalización de compilación. Para más información, vea Revisión de la cobertura de código: Azure Pipelines.
2.8 Casos históricos de pruebas
Resumen
Los casos de prueba históricos, también conocidos como casos de prueba de regresión, impiden que los problemas antiguos vuelvan a aparecer y aumenten la cobertura general de las pruebas del producto. Debe asegurarse de que cuando se corrija un error, el proyecto también agrega un caso de prueba correspondiente. Con el tiempo, a medida que se realizan correcciones, la solidez general del conjunto de pruebas seguirá mejorando, lo que garantiza una mayor confiabilidad y seguridad.
Cualidades clave y relación con otras secciones
Dado que prueban las regresiones de errores, estas pruebas deben ser rápidas y fáciles de ejecutar, por lo que se pueden ejecutar junto con Casos de prueba basados en código y contribuir a la cobertura general del código del producto. Junto con esto, el uso de ejemplos reales de clientes para inspirar nuevos casos de prueba es una excelente manera de mejorar la cobertura y la calidad de las pruebas.
Visual Studio
Visual Studio le permite agregar fácilmente pruebas al conjunto de aplicaciones mientras realiza los cambios para corregir el error y ejecutar rápidamente las pruebas y la cobertura de código para asegurarse de que se tienen en cuenta todos los nuevos casos. Hacer referencia al identificador de error del sistema de seguimiento de problemas en el código donde escribe la prueba es una buena manera de conectar las pruebas de regresión a los problemas correspondientes. Prefiere usar Azure Boards y planes de prueba junto con Visual Studio:
- para asociar pruebas, casos de prueba y problemas; y
- para realizar un seguimiento de todos los aspectos de un problema y sus pruebas correspondientes.
Para más información, vea:
- Asociar pruebas automatizadas con casos de prueba: Azure Test Plans
- Vincular elementos de trabajo a otros objetos: Azure DevOps
Finalmente, la integración de estas pruebas en el área de pruebas unitarias que se supone que cubre la sección de código ayuda a mantener el conjunto de pruebas organizado y más fácil de administrar. Puede usar la agrupación de pruebas del Explorador de pruebas para realizar un seguimiento eficaz de las pruebas que pertenecen juntas. Para obtener más información, vea Ejecutar pruebas unitarias con el Explorador de pruebas: Visual Studio (Windows)
2.9 Fuzzing
Resumen Fuzzing (también conocido como pruebas de aproximadas) es una técnica automatizada de pruebas de software que implica proporcionar datos no válidos, inesperados o aleatorios como entrada a un programa. A continuación, el programa se supervisa para detectar excepciones como bloqueos, errores integrados o aserciones de código insertadas del compilador y posibles pérdidas de memoria.
Guía
Use la detección aproximada de todo el software que podría procesar entradas que un atacante podría controlar sin confianza. Si va a compilar una nueva aplicación y su conjunto de pruebas asociado, incluya el filtrado de datos de los módulos clave lo antes posible. La ejecución de errores por primera vez en un fragmento de software casi siempre descubre vulnerabilidades reales que anteriormente eran desconocidas. Una vez que comience a probarlo, no se detenga nunca.
Relación con otras secciones
Cuando la detección de errores notifica un error, siempre proporciona de forma natural un caso de prueba reproducible que muestra el error. Este caso de prueba se puede reproducir, resolver y, a continuación, agregarse a los casos de prueba históricos.
Al usar ambos saneadores, como y el desconsacertado de direcciones (ASan):
- En primer lugar, ejecute las pruebas normales con los saneadores habilitados para ver si hay problemas y, una vez que el código es un inicio de limpieza de sanear.
- Para C o C++, hay compiladores que automatizan la inserción de aserciones en tiempo de ejecución y metadatos que habilitan ASan. Cuando se compila para ASan, los archivos binarios resultantes se vinculan con una biblioteca en tiempo de ejecución que puede diagnosticar con precisión más de 15 categorías de errores de seguridad de memoria con cero falsos positivos. Para C o C++ cuando tenga origen, use LibFuzzer, que requiere que ASan esté habilitado primero.
- Para las bibliotecas escritas en Java, C#, Python, Rust, etc., usar el marco de AFL++.
Calidades clave
- La detección aproximada encuentra vulnerabilidades que a menudo faltan en el análisis estático de programas, pruebas exhaustivas de características y inspección manual de código.
- La detección aproximada es una manera eficaz de encontrar errores de seguridad y confiabilidad en el software, por lo que tanto que el Ciclo de vida de desarrollo de seguridad de Microsoft requiere una búsqueda aproximada en cada interfaz que no es de confianza de todos los productos (consulte también Modelado de amenazas).
- Use siempre la búsqueda aproximada para el software que podría procesar entradas que no son de confianza.
- La difusa es eficaz para las aplicaciones independientes con analizadores de datos grandes.
CI/CD de Azure y GitHub
Modifique las compilaciones para admitir la creación continua de ejecutables que usan LibFuzzer o AFL++. Puede agregar recursos informáticos adicionales necesarios para el desconcertamiento en servicios como OSS-Fuzz o OneFuzz.
Examen de aplicaciones web 2.10
Resumen
Dentro del ámbito de Microsoft Visual C++ en Windows, Microsoft recomienda:
- Prefiere TypeScript, JavaScript y ASP.NET para aplicaciones web.
- No escriba extensiones web en C++. Microsoft ha dejado de usar ActiveX.
- Cuando el código se compila en Emscripten/WASM, ya no se aplica C++ ni otras herramientas.
- Microsoft proporciona RESTler, un fuzzer de API de REST con estado.
Información general y calidades clave
Un analizador de aplicaciones web explora una aplicación web rastreando sus páginas web y la examina para detectar vulnerabilidades de seguridad. Este rastreo implica la generación automática de entradas malintencionadas y la evaluación de las respuestas de la aplicación. De forma crítica, el examen de aplicaciones web debe cubrir o admitir:
- Cataloga todas las aplicaciones web de la red, incluidas las nuevas y desconocidas, y se escala de una serie de aplicaciones a miles.
- Análisis profundo de versiones de software, servicios y API de REST y SOAP que usan los dispositivos móviles.
- Inserción de primitivos de seguridad en el desarrollo e implementación de aplicaciones en entornos de DevOps. Estos primitivos funcionan con el rastreador.
- Detección de malware.
2.11 Comprobación de componentes de software incluidos
Resumen
Controle el código de C++ igual que el código escrito en otros lenguajes de programación y aplique las herramientas Análisis de composición de software (SCA) y Análisis de origen (OA) adoptadas por su empresa al código de C++. Los flujos de trabajo y el examen de seguridad deben diseñarse como parte de los sistemas de CI/CD (integración continua y entrega continua).
Defensa ascendente
Para mitigar el riesgo de ataques en dependencias ascendentes, los orígenes o componentes de terceros deben almacenarse en un recurso controlado por la empresa, en el que se ejecutan las herramientas de SCA y OA.
- Las herramientas deben examinar y alertar cuando se identifican vulnerabilidades (incluidas las bases de datos públicas), como: Inicio | CVE
- Ejecute análisis estáticos en todos los componentes de software incluidos en la aplicación o repositorio para identificar patrones de código vulnerables.
Defensa de dependencias
Realice y mantenga una auditoría de las dependencias para validar que todas estas apariciones se tienen en cuenta y están cubiertas por las herramientas de SCA y OA.
- Los componentes se deben auditar y actualizar periódicamente a las versiones verificadas más recientes.
- Dependencias de fuente de paquetes.
- Las herramientas de SCA/OA cubren y auditan todas las dependencias de paquetes que proceden de una sola fuente.
SBOM
Genere un SBOM (lista de materiales de software) con el producto que muestre todas las dependencias, como:
- origen (por ejemplo, dirección URL (localizador uniforme de recursos))
- version
- coherencia (por ejemplo, hash de origen SHA-256) y otros medios para validar la coherencia, como compilaciones deterministas.
- Requerir y auditar archivos SBOM en dependencias de software o producidos como parte de una compilación, incluido OSS (software de código abierto).
- Microsoft está estándar y recomienda SPDX (Intercambio de datos de paquetes de software) versión 2.2 o posterior | Linux Foundation como formato de documento SBOM.
- El determinismo de compilación se puede usar para generar de forma independiente archivos binarios idénticos a bit y proporcionar comprobaciones independientes de integridad:
- Atestación de reproducibilidad de terceros o de terceros
- Otras técnicas, como la firma binaria a través de un origen de certificado de confianza, también pueden proporcionar algunas garantías de integridad binaria.
Recursos adicionales
Las soluciones de Microsoft incluyen las siguientes instrucciones y productos:
- Plataforma de cadena de suministro de Microsoft | Microsoft
- Proteger la cadena de suministro de software | Seguridad de GitHub
- vcpkg: los registros privados de vcpkg permiten el redireccionamiento de la adquisición de OSS a recursos controlados por la empresa para adquirir orígenes para una dependencia, para minimizar el riesgo de ataques ascendentes o por encima de la conexión.