Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A publicação do governo dos Estados Unidos NISTIR 8397: Guidelines on Minimum Standards for Developer Verification of Software contém excelentes orientações sobre como construir software confiável e seguro em qualquer linguagem de programação.
Este documento segue a mesma estrutura do NISTIR 8397. Cada secção:
- resume como usar produtos de desenvolvedor da Microsoft para C++ e outras linguagens para atender às necessidades de segurança dessa seção, e
- fornece orientação para obter o máximo valor em cada área.
2.1 Modelação de ameaças
Resumo
A modelagem de ameaças é um processo valioso, especialmente quando aplicado de uma forma que pode ser dimensionada para atender às suas necessidades de desenvolvimento e que reduz o ruído.
Recomendações
A modelagem de ameaças deve ser uma parte do seu ciclo de vida dinâmico de desenvolvimento de segurança (SDL). Sugerimos que, para o seu produto como um todo, para uma característica específica ou para uma grande alteração de design ou implementação:
- Tenha um SDL sólido e dinâmico que permita o envolvimento precoce com as equipes de desenvolvedores e a criação de direitos de abordagem.
- Aplique a modelagem de ameaças de forma direcionada. Aplique a modelagem de ameaças a todos os recursos, mas taticamente comece com recursos expostos, complexos ou críticos. Aplique-o regularmente, em vez disso, como parte de uma revisão de produto de cima para baixo.
- Aplique a modelagem de ameaças antecipadamente (como acontece com todos os requisitos de segurança), quando ainda houver oportunidade de alterar o design. Além disso, os modelos de ameaça servem como entrada para outros processos, como redução de superfície de ataque ou design para segurança. Os modelos de ameaça que são criados posteriormente são, na melhor das hipóteses, "pesquisas" para teste de caneta (teste de penetração) ou áreas que precisam de testes de segurança, como difusão. Depois de criar um modelo de ameaça de linha de base, planeje continuar iterando nele à medida que a superfície de ataque muda.
- Use o inventário de ativos e a conformidade para rastrear adequadamente o que compõe um produto e rastrear artefatos de segurança (incluindo modelos de ameaça) juntamente com os ativos aos quais eles se aplicam. Essa abordagem permite uma melhor avaliação automatizada de riscos e a concentração dos esforços de segurança nos componentes ou recursos específicos que mudam.
- No Azure, a Ferramenta de Modelagem de Ameaças da Microsoft foi atualizada em 2022 para o desenvolvimento do Azure. Para obter mais informações, consulte Visão geral da Microsoft Threat Modeling Tool - Azure
Fatores e práticas de apoio
Para aplicar adequadamente a modelagem de ameaças e evitar o subuso/uso excessivo, descobrimos que os seguintes conceitos centrais precisam ser abordados primeiro.
Abordagem de desenvolvimento
Primeiro, entenda a abordagem de desenvolvimento da equipe. Para equipes com fluxos de trabalho de desenvolvimento ágil que empurram dezenas de alterações para a produção diariamente, não é prático ou razoável exigir uma atualização do modelo de ameaça para cada alteração funcional. Em vez disso, desde o início ao escrever os requisitos funcionais de um recurso, considere incluir um questionário de requisitos de segurança. O questionário deve se concentrar em perguntas específicas sobre o recurso para determinar quais aspetos futuros do seu SDL se aplicam. Por exemplo:
- O recurso faz uma grande mudança no design de como fornecemos isolamento do cliente em um ambiente multilocatário? Em caso afirmativo, considere a execução de um modelo de ameaça completo.
- Um novo recurso permite o upload de arquivos? Se sim, talvez o mais apropriado seja uma avaliação de segurança de aplicativos Web.
- Essa alteração é principalmente apenas uma alteração funcional da interface do usuário? Se assim for, talvez nada seja necessário além de suas ferramentas automatizadas tradicionais.
Os resultados do questionário de segurança informam quais técnicas SDL vincular a qual unidade de desenvolvimento. Ele também informa os parceiros de desenvolvimento sobre os cronogramas SDL do recurso, para que eles possam colaborar nos momentos certos.
Inventário de produtos
Em segundo lugar, mantenha um forte inventário de ativos dos produtos que você está encarregado de avaliar. Os produtos estão a aumentar em complexidade. É comum escrever software para dispositivos conectados que tenham:
- sensores (tais como comboios de passageiros e veículos),
- redes baseadas em barramento que se comunicam com outros componentes do veículo (como o CANBUS ou o PROFIBUS),
- sem fio / celular / Bluetooth para comunicação com dispositivos do cliente e back-ends de nuvem,
- aprendizagem automática na nuvem que retroalimenta o dispositivo ou uma aplicação de gestão de frotas,
- e muito mais.
Em produtos tão complexos, a modelagem de ameaças é fundamental. Ter um inventário de ativos sólido permite visualizar toda a pilha de produtos para ver o quadro completo e ver os principais locais que precisam ser avaliados para saber como um recurso novo ou alterado afeta a segurança do produto.
Granularidade e integração
Estabeleça sistemas para medir a conformidade usando métricas claras.
- Meça regularmente a conformidade para o desenvolvimento ao nível das funcionalidades. A conformidade dos recursos geralmente deve ser medida com maior frequência e menor granularidade, às vezes até mesmo no sistema do desenvolvedor ou no momento da confirmação/mesclagem de código.
- Avalie periodicamente a segurança do produto mais amplo no qual um recurso ou componente está sendo consumido. Avaliações mais amplas normalmente são feitas com menor frequência e granularidade mais ampla, como no tempo de teste do módulo ou do sistema.
Escala
Mantenha um sistema de inventário de ativos adequado que capture e preserve artefatos de segurança e a saída de revisões de modelos de ameaças. Ter um inventário claro permite avaliar as saídas de revisão para padrões e tomar decisões inteligentes sobre como refinar o programa de segurança do produto regularmente.
Tente combinar questionários de segurança da fase de requisitos, resultados de modelagem de ameaças, resultados de avaliação de segurança e resultados de ferramentas automatizadas. Combiná-los permite automatizar um ponto de vista de risco relativo de um determinado produto, idealmente como um "painel", para informar suas equipes de segurança no que se concentrar para obter o melhor valor da modelagem de ameaças.
2.2 Testes automatizados
Resumo
Os testes automatizados são uma maneira importante de garantir a qualidade e a segurança do seu código. Eles são parte integrante do suporte a outras áreas mencionadas neste documento, como a Modelagem de Ameaças. Quando emparelhados com outras práticas de codificação seguras, eles ajudam a proteger contra bugs e vulnerabilidades que estão sendo introduzidos na base de código.
Atributos principais
Os testes devem ser confiáveis, consistentes e isolados. Esses testes devem abranger o máximo possível do código. Todos os novos recursos e correções de bugs devem ter testes correspondentes para garantir a segurança e a confiabilidade do código a longo prazo, quando possível. Execute testes automatizados regularmente e no maior número possível de ambientes, para garantir que eles sejam executados e abranjam todas as áreas:
- O primeiro lugar que eles devem executar é na máquina que está fazendo as alterações. A execução de testes é feita mais facilmente dentro do IDE que está sendo usado para edição, ou como um script na linha de comando à medida que o desenvolvedor faz as alterações.
- O próximo lugar em que eles devem ser executados é como parte do processo de confirmação/mesclagem de solicitação pull.
- O último lugar para executar testes é como parte de um pipeline de Integração Contínua e Implantação Contínua (CI/CD) ou em suas compilações candidatas à versão.
O escopo dos testes deve aumentar a cada etapa, com a última etapa fornecendo cobertura total para qualquer coisa que as outras etapas possam perder.
Utilização e manutenção contínuas
A confiabilidade do teste é uma parte importante da manutenção da eficácia do conjunto de testes. As falhas de teste devem ser atribuídas e investigadas, com possíveis problemas de segurança recebendo alta prioridade e sendo atualizados dentro de um prazo rápido e predeterminado. Ignorar falhas de teste não deve ser uma prática comum, mas deve exigir uma forte justificação e aprovação. As falhas de teste devidas a problemas no próprio conjunto de testes devem ser tratadas da mesma forma que outras falhas, para evitar um lapso na cobertura em que se possam deixar passar problemas do produto.
Tipos de testes, especialmente testes unitários
Existem vários tipos de testes automatizados e, embora nem todos sejam aplicáveis a todos os aplicativos, um bom conjunto de testes contém uma seleção de vários tipos diferentes. Casos de teste baseados em código, como testes de unidade, são os mais comuns e mais integrais, sendo aplicáveis a todos os aplicativos e cobrindo intencionalmente o maior número possível de caminhos de código para correção. Esses testes devem ser pequenos, rápidos e não afetar o estado da máquina, para que o conjunto completo de testes possa ser executado rapidamente e com frequência. Se possível, execute testes em muitas máquinas que têm configurações de hardware diferentes para detetar problemas que não são reproduzíveis em um único tipo de máquina.
Estúdio Visual
O Visual Studio Test Explorer oferece suporte nativo a muitas das estruturas de teste C++ mais populares e tem opções para instalar extensões para mais estruturas. Essa flexibilidade é útil para executar um subconjunto de testes que abrangem o código em que você está trabalhando e facilita a depuração de falhas de teste à medida que elas surgem. O Visual Studio também facilita a configuração de novos conjuntos de testes para projetos existentes e fornece ferramentas úteis, como o CodeLens, para facilitar o gerenciamento desses testes. Para obter mais informações sobre como escrever, executar e gerenciar testes C/C++ com o Visual Studio, consulte Escrever testes de unidade para C/C++ - Visual Studio (Windows).
No Azure e no GitHub CI/CD
Testes que fazem verificações mais profundas e levam mais tempo para serem executados, como análise estática, deteção de componentes e assim por diante, são bons candidatos para testes de solicitação pull ou testes de integração contínua. As Ações de DevOps e GitHub do Azure facilitam a execução automática de validações e bloqueiam check-ins de código se uma validação falhar. A imposição automatizada ajuda a garantir que todo o código que está sendo verificado seja seguro com base nessas verificações mais rigorosas que estão sendo executadas. Os Pipelines do Azure e a Validação de Compilação do Azure DevOps são descritos aqui:
- Políticas e configurações de ramificação do Git - Azure Repos
- Definindo a capacidade de mesclagem de solicitações pull | Documentos do GitHub
2.3 Análise baseada em código ou estática
Resumo A análise de código estático/binário deve ser habilitada por padrão, para ser segura por padrão. A análise estática analisa um programa para as políticas de segurança necessárias no momento em que está sendo construído, não no momento da execução, quando uma exploração pode ocorrer na máquina do cliente. A análise estática pode analisar o programa em forma de código fonte ou em forma executável compilada.
Recomendações A Microsoft recomenda que você:
- Habilite a análise estática para todos os programas C++, tanto para o código-fonte de entrada (antes da compilação) quanto para os binários executáveis (após a compilação). "Habilitar" pode significar executar a análise durante cada compilação na máquina do desenvolvedor, ou como uma compilação separada para inspecionar o código posteriormente ou como um requisito de check-in.
- Incorpore a análise estática em pipelines de CI como uma forma de teste.
- A análise estática por definição vem com falsos positivos e esteja preparado para incorporar esse fato em seu ciclo de feedback de qualidade. Esteja pronto para ativar todas as notificações de baixo falso-positivo logo no início. Em seguida, seja proativo para aumentar gradualmente o número de regras para as quais a sua base de código compila sem avisos, à medida que adiciona regularmente mais regras que sinalizam bugs importantes, embora isso leve a um número crescente de falsos positivos (inicialmente, antes que a base de código tenha sido limpa para essas regras também).
- Sempre use as versões com suporte mais recentes do Visual Studio e configure seu ambiente de engenharia para consumir rapidamente as versões de patch mais recentes assim que estiverem disponíveis, sem atrasar para o próximo estágio/ciclo de desenvolvimento.
Principais ferramentas Esteja ciente e use o seguinte:
- Documentação de análise de código - C++ e .NET
-
/analyze
- Compilador Visual C++ -
/W4
e/WX
- Compilador Visual C++ - Usar os verificadores de diretrizes principais do C++
- CodeQL | GitHub
- Binskim guia do usuário | GitHub
- Consulte também (somente Windows): anotações SAL
Observações:
-
/analyze
permite a análise estática do código C++ em tempo de compilação para identificar vulnerabilidades críticas de código de segurança e confiabilidade. Ele deve ser ativado em toda a linha do tempo de desenvolvimento de um programa C++. Comece ativando pelo menos o "Microsoft Native Recommended" por padrão como uma linha de base mínima. Em seguida, consulte a documentação para saber como especificar mais regras, especialmente as regras das Diretrizes Principais do C++, conforme exigido por suas políticas de engenharia. O recurso de análise estática do código-fonte está disponível no IDE do Visual C++ e nas ferramentas de compilação de linha de comando. -
/W4
e/WX
deve ser ativado sempre que possível, para garantir que você compile seu código corretamente em altos níveis de aviso (W4
) e trate os avisos como erros que devem ser corrigidos (WX
). Essas opções permitem encontrar erros de dados não inicializados que outras ferramentas de análise estática não podem verificar, porque os erros só se tornam visíveis depois que o back-end do compilador executa análise interprocessual e inlining. - A análise binária BinSkim garante que os projetos permitam uma ampla gama de recursos de segurança. BinSkim gera PDBs e outras saídas que facilitam a verificação da cadeia de custódia e a resposta eficiente a problemas de segurança. A Microsoft recomenda a execução da ferramenta BinSkim para analisar todos os binários executáveis (
.sys
ou.dll
.exe
) produzidos ou consumidos pelos seus programas. O Guia do Usuário BinSkim inclui uma lista de padrões de segurança suportados. A Microsoft recomenda que você corrija todos os problemas relatados como "erros" pela ferramenta BinSkim. Os problemas comunicados como "avisos" devem ser avaliados seletivamente, porque a sua resolução pode ter implicações no desempenho ou pode não ser necessária.
No Azure e no GitHub CI/CD A Microsoft recomenda sempre habilitar o código-fonte e a análise estática binária em cenários de CI/CD de versão. Execute a análise de origem imediatamente na máquina do desenvolvedor local, ou pelo menos para cada solicitação commit ou pull, para detetar bugs de origem o mais cedo possível e minimizar os custos gerais. Erros de nível binário tendem a ser introduzidos mais lentamente, por isso pode ser suficiente executar a análise binária em cenários de CI/CD menos frequentes antes do lançamento (como compilações noturnas ou semanais).
2.4 Revisão de segredos codificados
Resumo
Não codifice segredos dentro do software. Você pode encontrar e remover segredos do código-fonte de forma eficiente usando ferramentas confiáveis que podem verificar toda a sua base de código-fonte. Depois de encontrar segredos, mova-os para um local seguro seguindo as diretrizes para armazenamento seguro e uso de segredos.
Problema
"Segredos" refere-se a entidades que estabelecem a identidade e fornecem acesso a recursos, ou que são utilizadas para assinar ou encriptar dados sensíveis. Os exemplos incluem senhas, chaves de armazenamento, cadeias de conexão e chaves privadas. É tentador manter segredos no produto de software para que eles possam ser prontamente obtidos quando necessário pelo software. No entanto, esses segredos codificados podem levar a incidentes de segurança graves ou catastróficos, pois são facilmente descobertos e podem ser usados para comprometer seu serviço e dados.
Prevenção
Os segredos codificados no código-fonte (como texto simples ou blob encriptado) são uma vulnerabilidade de segurança. Aqui estão as diretrizes gerais sobre como evitar segredos no código-fonte:
- Use uma ferramenta de pré-checkin para verificar e capturar possíveis segredos codificados no código antes de enviar para o controle do código-fonte.
- Não coloque credenciais de texto não criptografado no código-fonte ou nos arquivos de configuração.
- Não armazene credenciais de texto não criptografado no SharePoint, OneNote, compartilhamentos de arquivos e assim por diante. Ou compartilhe-os por e-mail, mensagens instantâneas e assim por diante.
- Não encriptar um segredo com uma chave de desencriptação facilmente detetável. Por exemplo, não armazene um arquivo PFX junto com um arquivo que contenha sua senha.
- Não encripte um segredo com uma encriptação fraca. Por exemplo, não criptografe um arquivo PFX com uma senha fraca ou comum.
- Evite colocar credenciais criptografadas no código-fonte. Em vez disso, use espaços reservados na sua origem e permita que o seu sistema de implementação os substitua por informações confidenciais de repositórios aprovados.
- Aplique os mesmos princípios aos segredos em ambientes como teste, pré-produção e assim por diante, tal como faz em implantações de produção. Os adversários geralmente têm como alvo sistemas que não são de produção por serem menos bem geridos, e os usam para acessar a produção.
- Não compartilhe segredos entre implantações (por exemplo, teste, preparação, produção).
Embora não esteja diretamente relacionado a segredos codificados, lembre-se também de proteger segredos para seu teste, desenvolvimento e produção:
- Altere os segredos periodicamente e sempre que haja risco de exposição. Demonstrar a capacidade de alternar/reimplantar credenciais é prova de um sistema seguro. Mais notavelmente, a ausência dessa capacidade é uma evidência ainda mais forte de uma vulnerabilidade inevitável.
- Não ceda à lógica comum do desenvolvedor de que "minhas credenciais de teste não criam risco". Na prática, quase sempre o fazem.
- Considere eliminar completamente o uso de segredos (por exemplo, senhas, chaves de acesso) em favor de soluções orientadas por identidade/RBAC, como uma boa prática de engenharia que pode evitar completamente a má gestão de segredos.
Deteção
Os componentes herdados do seu produto podem conter segredos ocultos codificados em seu código-fonte. Às vezes, os segredos das máquinas de desktop dos desenvolvedores podem se infiltrar na ramificação remota e se fundir na ramificação de lançamento, vazando segredos sem querer. Para descobrir segredos que podem estar escondidos em seu código-fonte, você pode usar ferramentas que podem verificar seu código em busca de segredos codificados:
Reparação dos danos
Quando as credenciais são encontradas em seu código-fonte, a necessidade urgente imediata é invalidar a chave exposta e executar uma análise de risco com base na exposição. Mesmo que seu sistema precise permanecer em execução, você pode habilitar um gerenciador secreto para correção usando estas etapas:
- Se a correção permitir mudar para identidades geridas ou exigir a introdução de um gestor de segredos, como o Azure Key Vault (AKV), faça isso primeiro. Em seguida, implante novamente com a identidade ou chave atualizada.
- Invalide o segredo exposto.
- Realizar auditoria/avaliação de risco de danos potenciais devido a comprometimento.
Para proteger chaves criptográficas e outros segredos usados por aplicativos e serviços na nuvem, use o Cofre de Chaves do Azure com uma política de acesso apropriada.
Se uma exposição comprometer determinados dados/PII do cliente, poderá exigir outros requisitos de conformidade/relatórios.
Remova os segredos agora invalidados do seu código-fonte e substitua-os por métodos alternativos que não exponham os segredos diretamente no seu código-fonte. Procure oportunidades para eliminar segredos sempre que possível usando ferramentas como o Azure AD. Você pode atualizar seus métodos de autenticação para aproveitar as identidades gerenciadas por meio do Azure Ative Directory). Use apenas lojas aprovadas para armazenar e gerenciar segredos, como o Azure Key Vault (AKV). Para obter mais informações, consulte:
Azure DevOps (AzDO)
Os usuários do AzDO podem verificar seu código por meio do GitHub Advanced Security for Azure DevOps (GHAzDO). O GHAzDO também permite que os usuários evitem exposições secretas, ativando a Proteção Push em seus repositórios, capturando exposições potenciais antes que elas sejam vazadas. Para obter mais informações sobre como detetar segredos codificados no código no Azure DevOps, consulte Secret Scanning for GitHub Advanced Security for Azure DevOps em cada um dos seguintes links:
- Segurança avançada do GitHub para Azure DevOps
- Varrimento de Segredos da Segurança Avançada do GitHub no Azure DevOps
- Microsoft Defender para DevOps Preview
No GitHub
A verificação de segredos está disponível em GitHub.com em duas modalidades:
- Alertas de varredura de segredos para parceiros. É executado automaticamente em todos os repositórios públicos. Todas as cadeias de caracteres que correspondem a padrões fornecidos por parceiros de verificação secretos são relatadas diretamente ao parceiro relevante.
- Alertas de verificação de segredos para utilizadores. Você pode habilitar e configurar a verificação extra para repositórios pertencentes a organizações que usam o GitHub Enterprise Cloud e têm uma licença para o GitHub Advanced Security. Essas ferramentas também suportam repositórios privados e internos.
O GitHub fornece padrões conhecidos de segredos para parceiros e usuários que podem ser configurados para atender às suas necessidades. Para mais informações, consultar:
Observação
O GitHub Advanced Security para Azure DevOps traz as mesmas soluções de varredura de segredos, varredura de dependências e análise de código CodeQL já disponíveis para utilizadores do GitHub e integra-as nativamente ao Azure DevOps para proteger os seus repositórios e pipelines do Azure.
Recursos adicionais
- Análise de credenciais | Código Microsoft com Guia de Engenharia.
- detect-secrets: Ferramenta de verificação de credenciais | GitHub - um módulo apropriadamente nomeado para detetar segredos dentro de uma base de código.
- Executando detect-secrets nas Azure Pipelines.
- Git-segredos | awslabs do GitHub - impede que você confirme senhas e outras informações confidenciais em um repositório git.
- Gestão de Segredos | Microsoft Code with Engineering Playbook - fornece diretrizes gerais sobre como os segredos devem ser gerenciados.
2.5 Executar com verificações e proteção fornecidas pelo idioma e pelo sistema operativo
Resumo
O endurecimento binário é feito aplicando controles de segurança em tempo de compilação. Estas incluem atenuações que:
- prevenir vulnerabilidades exploráveis no código,
- habilitar deteções de tempo de execução que acionam defesas de segurança na exploração, e
- Habilite a produção e o arquivamento de dados para ajudar a limitar os danos causados por incidentes de segurança.
Os consumidores binários devem optar pelos recursos de segurança do Windows para obter todos os benefícios da proteção.
A Microsoft fornece um conjunto de recursos específicos para projetos C++ para ajudar os desenvolvedores a escrever e enviar código mais seguro. Os desenvolvedores de C++ também devem aderir aos padrões de segurança comuns às linguagens que geram código executável. A Microsoft mantém BinSkim, um verificador binário OSS público que ajuda a impor o uso de muitas proteções descritas nesta seção. Para obter mais informações sobre BinSkim, consulte Binskim guia do usuário | GitHub
Os controles de nível binário diferem de acordo com onde são aplicados no processo de engenharia. Você deve distinguir entre as opções de compilador e vinculador que: são estritamente tempo de compilação, alteram a geração de código com sobrecarga de tempo de execução e alteram a geração de código para obter compatibilidade com as proteções do sistema operacional.
As configurações do desenvolvedor devem preferir habilitar o máximo de análise estática possível, permitir a produção de dados privados para acelerar a depuração e assim por diante. As compilações de versão devem ser ajustadas a uma combinação apropriada de segurança, desempenho e outras preocupações de geração de código. Os processos de liberação devem ser configurados para gerar e gerenciar adequadamente dados de compilação públicos versus privados (por exemplo, símbolos públicos versus privados).
Mantenha-se atualizado: sempre use compiladores e ferramentas up-to-date
Compile todo o código-fonte com ferramentas atuais para tirar partido do suporte a linguagens da up-todata, análise estática, geração de código e controles de segurança. Como os compiladores afetam todos os componentes gerados, o potencial de regressão na atualização da ferramenta é relativamente alto. O uso de compiladores desatualizados cria um risco específico para ações corretivas ao responder a um incidente de segurança, porque as equipes podem não ter tempo suficiente para atualizar os compiladores. A Microsoft recomenda que as equipes desenvolvam o recurso para atualizar e testar regularmente as atualizações do compilador.
Use métodos de desenvolvimento seguros, versões de linguagem, frameworks/APIs
O código deve utilizar metodologias de desenvolvimento, versões de linguagem, framework, APIs e assim por diante, que minimizem o risco promovendo segurança e simplicidade em C++, incluindo:
- Consulte Biblioteca de Suporte de Diretriz (GSL) das Diretrizes Principais do C++ para obter orientação para escrever código C++ moderno, seguro e consistente que siga as práticas recomendadas e evite armadilhas comuns.
- Consulte Implementação do Microsoft GSL para funções e tipos que as Diretrizes Principais do C++ sugerem que você use.
- Contêineres C++ seguros para recursos, proteções contra estouro de memória da biblioteca de tempo de execução C (CRT): Prefira
std::vector
estd::string
, que são seguros para recursos. Se você precisar usar dados C, use as versões seguras das funções CRT, que são projetadas para ajudar a evitar corrupção de memória devido ao uso indevido do buffer e comportamentos de linguagem indefinidos. - A biblioteca SafeInt protege contra estouro de número inteiro em operações matemáticas e de comparação.
Consome dependências seguras
Os binários não devem ser vinculados a bibliotecas e dependências inseguras. As equipes de desenvolvimento devem rastrear todas as dependências externas e resolver CVEs/vulnerabilidades de segurança identificadas nesses componentes atualizando para versões mais seguras quando sujeitas a essas vulnerabilidades.
Maximizar as garantias de proveniência do código e a eficiência da resposta de segurança
A compilação deve permitir garantias fortes de proveniência do código para ajudar a detetar e prevenir a introdução de backdoors e outros códigos maliciosos. Os dados resultantes, também essenciais para depuração e investigação, devem ser arquivados para todas as versões de software para gerar uma resposta de segurança eficiente se forem comprometidos. As seguintes opções de compilador geram informações que são críticas para uma resposta de segurança:
-
/ZH:SHA_SHA256
in Visual C++ - Garante que um algoritmo criptograficamente seguro seja usado para gerar todos os hashes de arquivo de origem PDB. -
/Zi
,/ZI
(Debug Information Format) no Visual C++ - Além de publicar símbolos removidos para coletar dados de falha e outros cenários de uso público, certifique-se de que as compilações produzam e arquivem PDBs privados para todos os binários liberados. As ferramentas de análise binária exigem símbolos completos para verificar se muitas atenuações de segurança foram ativadas em tempo de compilação. Os símbolos privados são essenciais na resposta de segurança e reduzem os custos de depuração e investigação quando os engenheiros estão correndo para avaliar e limitar os danos quando uma exploração acontece. -
/SOURCELINK
in Visual C++ Linker - Include Sourcelink file in PDB: Source Link é um sistema agnóstico de linguagem e controlo de origem que fornece depuração do código-fonte para binários. A depuração de origem aumenta consideravelmente a eficiência, a gama de validações de segurança de pré-lançamento e a resposta a incidentes pós-lançamento.
Habilite erros do compilador para evitar problemas no momento da criação de código
A compilação deve permitir verificações do compilador relevantes para a segurança como erros de quebra, por exemplo:
-
/sdl
no Visual C++ - Habilitar verificações de segurança adicionais eleva muitos avisos relevantes à segurança em erros e habilita recursos avançados de geração de código seguro. - BinSkim BA2007. EnableCriticalCompilerWarnings | O GitHub mantém uma lista de avisos do compilador C/C++ recomendados pela Microsoft que devem ser sempre habilitados e elevados a erros.
Marcar binários como compatíveis com as mitigações de segurança do sistema operativo em tempo de execução
As configurações do compilador e do vinculador devem optar por recursos de geração de código que detetam e mitigam a execução de código mal-intencionado, incluindo:
- Prevenção de corrupção de pilha
-
/SAFESEH
- A imagem tem manipuladores de exceção seguros - Produz uma tabela dos manipuladores de exceção seguros da imagem para binários x86. -
/GS
- Verificação de segurança do buffer - Deteta algumas saturações de buffer que substituem endereços de retorno, endereços do manipulador de exceções ou certos tipos de parâmetros.
-
- Execução de código independente de posição
-
/DYNAMICBASE
- Usar Randomização do Layout do Espaço de Endereços - Gera imagens executáveis que podem ser rebaseadas aleatoriamente no momento de carregamento. -
/HIGHENTROPVA
e/LARGEADDRESSAWARE
- Suporte de ASLR de 64 bits e Gestão de endereços longos - Permite o uso de todo o espaço de endereçamento de 64 bits para o rebaseamento de imagens.
-
- Integridade do fluxo de código
-
/guard:cf
- Ativar Control Flow Guard - Insere verificações de tempo de execução para alvos de chamada indireta. -
/CETCOMPAT
- Compatível com Pilha Sombra CET - Marca uma imagem executável como compatível com a implementação da Microsoft da funcionalidade Pilha Sombra da Tecnologia de Reforço de Fluxo de Controle (CET) da Intel. -
/guard:ehcont
- Ativar metadados de continuação de EH - Gerar uma lista de endereços virtuais relativos seguros (RVA) para todos os alvos de continuação de tratamento de exceções.
-
- Prevenção de execução de dados
-
/NXCOMPAT
- Compatível com a Prevenção de Execução de Dados - Marca uma imagem executável de 32 bits como compatível com o recurso DEP (Prevenção de Execução de Dados) do Windows . As compilações de 64 bits são compatíveis com DEP por padrão.)
-
Impedir a divulgação de informações confidenciais
As configurações do compilador devem optar pela prevenção de descoberta de informações confidenciais. Nos últimos anos, os investigadores descobriram vazamentos não intencionais de informações que se originam de características de hardware, como a execução especulativa.
No nível do software, dados confidenciais podem ser transmitidos aos invasores se vazarem inesperadamente. A falha na inicialização a zero de buffers e o uso inadequado de buffers podem vazar dados confidenciais privados para invasores que chamam uma API confiável. Essa classe de problema é melhor tratada habilitando a análise estática extra e usando contêineres de recursos seguros, conforme descrito anteriormente.
-
/Qspectre
- Mitigar ataques de canal lateral de execução especulativa - Insere instruções de barreira que ajudam a evitar a divulgação de dados sensíveis produzidos pela execução especulativa. Essas atenuações devem ser habilitadas para código que armazena dados confidenciais na memória e opera através de um limite de confiança. A Microsoft sempre recomenda medir o impacto no desempenho em relação a benchmarks apropriados ao habilitar mitigações do Spectre, devido à possibilidade de introduzir verificações de tempo de execução em blocos ou loops críticos de desempenho. Esses caminhos de código podem desabilitar atenuações por meio dospectre(nomitigation)
declspec
modificador. Os projetos que habilitam/Qspectre
também devem ser vinculados a bibliotecas que também são compiladas com essas atenuações, incluindo as bibliotecas runtime da Microsoft.
2.6 Casos de teste de caixa preta
Resumo
Os testes de caixa preta não dependem do conhecimento do funcionamento interno do componente testado. Os testes de caixa preta são projetados para testar a funcionalidade de ponta a ponta dos recursos do produto em qualquer camada ou nível. Os testes de caixa preta podem ser testes funcionais, testes de interface do usuário, testes de desempenho e testes de integração. Os testes de caixa preta são valiosos para medir a confiabilidade geral e a correção funcional, além de garantir que o produto se comporte conforme o esperado.
Relação com outras secções
Esses tipos de testes baseados em requisitos são úteis para validar as suposições feitas no Modelo de Ameaça e cobrir ameaças potenciais, conforme mencionado nessa seção. Esses testes são úteis para testar a integração entre componentes separados do produto, especialmente aqueles que estão além dos limites de confiança, conforme descrito no modelo de ameaça. Os casos de teste de caixa preta também são úteis para testar todos os tipos de situações limite na validação da entrada do utilizador. Testar casos extremos conhecidos e casos de erro é útil. O fuzzing também é útil para testar casos menos óbvios.
Automação e regressão
Realize esses testes regularmente e compare os resultados com execuções anteriores para detetar alterações disruptivas ou regressões de desempenho. Além disso, a execução desses testes em muitas máquinas diferentes e configurações de instalação pode ajudar a cobrir quaisquer problemas que possam surgir de diferentes arquiteturas ou alterações de configuração.
Despejos de memória
Esses testes ajudam a identificar problemas relacionados à confiabilidade, permitindo testar muitos cenários diferentes que podem resultar em falhas, travamentos, bloqueios e assim por diante. Coletando despejos de memória como parte de falhas de teste, você pode importar os despejos diretamente para o Visual Studio para investigar melhor quais partes do código estão atingindo esses problemas. Se você executar testes funcionais de dentro do Visual Studio, você pode facilmente replicar e depurar falhas vendo exatamente onde dentro da caixa preta o teste falha, e você pode testar correções rapidamente.
Para começar a depurar testes, consulte Depurar testes de unidade com o Gerenciador de Testes - Visual Studio (Windows)
No Azure
O Azure DevOps também pode ajudar a gerenciar e validar esses testes com o uso de Planos de Teste. Esses testes podem ser usados para garantir a aprovação com validação manual e para executar testes automatizados associados aos requisitos do produto. Mais informações sobre os Planos de Teste do Azure e como usá-los para executar testes automatizados podem ser encontradas aqui:
- O que são os Planos de Teste do Azure? Ferramentas de teste manuais, exploratórias e automatizadas. - Planos de teste do Azure
- Executar testes automatizados a partir de planos de teste - Planos de Teste do Azure
2.7 Casos de teste baseados em código
Resumo
Os casos de teste baseados em código são parte integrante da manutenção da segurança e confiabilidade do seu produto. Estes testes devem ser pequenos e rápidos e não devem ter impacto uns nos outros para que possam ser executados em paralelo. Os testes baseados em código são fáceis para os desenvolvedores executarem localmente em sua máquina de desenvolvimento sempre que fizerem alterações no código sem se preocupar em desacelerar seu ciclo de desenvolvimento.
Tipos e relação com outras secções
Os tipos comuns de casos de teste baseados em código incluem:
- testes unitários,
- testes parametrizados para cobrir funções com vários tipos de entrada,
- testes de componentes para manter cada componente de teste separado, e
- teste simulado para validar partes do código que se comunicam com outros serviços, sem expandir o escopo do teste para incluir esses próprios serviços.
Esses testes são baseados no código interno que é escrito, enquanto os testes de caixa preta são baseados nos requisitos funcionais externos do produto.
Objetivo
Por meio desses testes, o objetivo é alcançar um alto nível de cobertura de teste sobre seu código. Você deve acompanhar ativamente essa cobertura e onde existem lacunas. À medida que você adiciona mais testes que exercem mais caminhos de código, a confiança geral na segurança e confiabilidade do seu código aumenta.
Estúdio Visual
As ferramentas do explorador de testes no Visual Studio facilitam a execução desses testes com freqüência e obtêm comentários sobre taxas de aprovação/reprovação e locais de falha rapidamente. Muitas das estruturas de teste também suportam recursos do CodeLens para ver o status do teste no local do próprio teste, facilitando a adição e manutenção do conjunto de testes. O Gerenciador de Testes também facilita o gerenciamento desses testes, permitindo grupos de teste, listas de reprodução de teste personalizadas, filtragem, classificação, pesquisa e muito mais.
Para obter mais informações, consulte:
- Fundamentos de teste de unidade - Visual Studio (Windows) - uma introdução e visão geral
- Executar testes de unidade com o Test Explorer - Visual Studio (Windows) - uma visão mais profunda do que está disponível para ajudar a gerenciar o conjunto potencialmente grande de testes de unidade com o Test Explorer
O Visual Studio também vem com ferramentas para controlar a cobertura do código. Essas ferramentas permitem garantir que as alterações de código feitas sejam cobertas por testes existentes ou adicionar novos testes para cobrir caminhos de código novos e não testados. As ferramentas também mostram a porcentagem de cobertura do código para garantir que ele seja mantido acima de um nível alvo para confiança na qualidade geral do código.
Para obter informações sobre essas ferramentas, consulte Teste de cobertura de código - Visual Studio (Windows)
No Azure
O Azure DevOps também pode ajudar no acompanhamento dos resultados da cobertura de código para todo o produto como parte do processo de pipeline de compilação. Para obter mais informações, consulte Rever a cobertura de código - Azure Pipelines.
2.8 Casos de teste históricos
Resumo
Os casos de teste históricos, também conhecidos como casos de teste de regressão, evitam que problemas antigos ressurjam novamente e aumentam a cobertura geral do teste do produto. Você deve garantir que, quando um bug é corrigido, o projeto também adiciona um caso de teste correspondente. Com o tempo, à medida que as correções são feitas, a robustez geral do conjunto de testes continuará melhorando, dando melhores garantias de confiabilidade e segurança.
Qualidades-chave e relação com outras secções
Como eles testam regressões de bugs, esses testes devem ser rápidos e fáceis de executar, para que possam ser executados junto com os casos de teste baseados em código e contribuir para a cobertura geral do código do produto. Junto com isso, usar exemplos reais de clientes para inspirar novos casos de teste é uma ótima maneira de melhorar a cobertura e a qualidade dos testes.
Estúdio Visual
O Visual Studio permite que você adicione facilmente testes ao pacote enquanto faz as alterações para corrigir o bug e execute rapidamente os testes e a cobertura de código para garantir que todos os novos casos sejam considerados. Referenciar o ID de bug do seu sistema de rastreamento de problemas em seu código onde você escreve o teste é uma boa maneira de conectar testes de regressão aos problemas correspondentes. Prefira usar os Quadros do Azure e os planos de teste junto com o Visual Studio:
- associar testes, casos de teste e problemas; e ainda
- para acompanhar todos os aspetos de um problema e seus testes correspondentes.
Para obter mais informações, consulte:
- Associar testes automatizados a casos de teste - Planos de Teste do Azure
- Vincular itens de trabalho a outros objetos - Azure DevOps
Eventualmente, a integração desses testes na área de teste de unidade que deve cobrir a seção de código ajuda a manter o conjunto de testes organizado e mais fácil de gerenciar. Pode utilizar a funcionalidade de agrupamento de testes do Test Explorer para monitorizar eficazmente os testes que pertencem juntos. Para obter mais informações, consulte Executar testes de unidade com o Test Explorer - Visual Studio (Windows)
2.9 Fuzzing (teste de aleatoriedade de entradas em software)
Resumo Fuzzing (também conhecido como teste difuso) é uma técnica de teste de software automatizado que envolve o fornecimento de dados inválidos, inesperados ou aleatórios como entrada para um programa. O programa é então monitorado para exceções, tais como falhas; asserções de código interno falhadas ou injetadas pelo compilador, e possíveis fugas de memória.
Orientação
Utilize o fuzzing em todo o software que possa processar entradas não confiáveis que um atacante possa controlar. Se estiveres a criar uma nova aplicação e o seu conjunto de testes associado, inclui o fuzzing para os módulos principais assim que possível. Executar fuzzing pela primeira vez em um software quase sempre revela vulnerabilidades reais que antes eram desconhecidas. Uma vez que comece a fazer fuzzing, nunca pare.
Relação com outras secções
Quando o fuzzing relata uma falha, ele naturalmente fornece um caso de teste reproduzível que demonstra o erro (bug). Este caso de teste pode ser reproduzido, resolvido e, em seguida, adicionado aos casos de teste históricos.
Ao usar ambas as ferramentas de sanitização, como Address Sanitizer (ASan) e fuzzing:
- Primeiro, executa os teus testes normais com sanitizadores habilitados para ver se há problemas, em seguida, uma vez que o código esteja limpo de erros de sanitização, começa a executar o fuzzing.
- Para C ou C++, existem compiladores que automatizam a injeção de asserções de tempo de execução e metadados que habilitam o ASan. Quando compilados para ASan, os binários resultantes ligam-se a uma biblioteca de tempo de execução que pode diagnosticar com precisão mais de 15 categorias de erros de segurança de memória com zero falsos positivos. Para C ou C++ quando tiveres o código fonte, usa LibFuzzer, que requer que o ASan seja ativado primeiro.
- Para bibliotecas escritas em Java, C#, Python, Rust e assim por diante, use a estrutura AFL++.
Qualidades-chave
- O fuzzing encontra vulnerabilidades frequentemente omitidas pela análise estática de programas, testes exaustivos de funcionalidades e inspeção manual de código.
- O fuzzing é uma maneira eficaz de encontrar bugs de segurança e confiabilidade no software, tanto que o Ciclo de Vida do Desenvolvimento de Segurança da Microsoft requer fuzzing em todas as interfaces não confiáveis de cada produto (consulte também Modelação de Ameaças).
- Use sempre fuzzing para software que possa processar entradas não confiáveis.
- O Fuzzing é eficaz para aplicações autónomas com grandes parsers de dados.
Azure e GitHub CI/CD
Modifique a(s) sua(s) compilação(ões) para suportar a criação contínua de executáveis que usam LibFuzzer ou AFL++. Você pode adicionar recursos de computação extra necessários para fuzzing em serviços como o OSS-Fuzz.
2.10 Varredura de aplicativos Web
Resumo
No âmbito do Microsoft Visual C++ no Windows, a Microsoft recomenda:
- Prefira TypeScript, JavaScript e ASP.NET para aplicativos Web.
- Não escreva extensões da Web em C++. A Microsoft descontinuou o ActiveX.
- Quando o código é compilado para Emscripten/WASM, ele não é mais C++ e outras ferramentas se aplicam.
- A Microsoft fornece o RESTler, um difusor de API REST com monitoração de estado.
Visão geral e principais qualidades
Um verificador de aplicativo da Web explora um aplicativo da Web rastreando suas páginas da Web e examina-a em busca de vulnerabilidades de segurança. Esse rastreamento envolve a geração automática de entradas maliciosas e a avaliação das respostas do aplicativo. Essencialmente, a verificação de aplicativos da Web deve cobrir/suportar:
- Cataloga todas as aplicações Web na sua rede, incluindo as novas e desconhecidas, e pode variar de um punhado de aplicações para milhares.
- Verificação profunda de versões de software, serviços de API SOAP e REST e APIs usadas por dispositivos móveis.
- Inserção de primitivas de segurança no desenvolvimento e implantação de aplicativos em ambientes DevOps. Esses primitivos trabalham com o rastreador.
- Deteção de malware.
2.11 Verifique os componentes de software incluídos
Resumo
Manipule seu código C++ da mesma forma que o código escrito em outras linguagens de programação e aplique qualquer ferramenta de Análise de Composição de Software (SCA) e Análise de Origem (OA) adotada pela sua empresa ao seu código C++. Os fluxos de trabalho e a verificação de segurança devem ser projetados como parte de sistemas de CI/CD (integração contínua e entrega contínua).
Defesa a montante
Para reduzir o risco de ataques a dependências upstream, fontes/componentes de terceiros devem ser armazenados em um ativo controlado pela empresa, contra o qual as ferramentas SCA e OA são executadas.
- As ferramentas devem analisar e alertar quando são identificadas vulnerabilidades (incluindo bases de dados públicas), tais como: Início | CVE
- Execute a análise estática em todos os componentes de software incluídos em seu aplicativo/repositório para identificar padrões de código vulneráveis.
Defesa de dependência
Execute e mantenha uma auditoria de dependências para validar se todas essas ocorrências são contabilizadas e cobertas por suas ferramentas SCA e OA.
- Os componentes devem ser regularmente auditados e atualizados para as versões verificadas mais recentes.
- Dependências de alimentação de pacotes.
- As ferramentas SCA/OA cobrem e auditam todas as dependências de pacotes provenientes de uma fonte única.
SBOM
Produza uma SBOM (lista de materiais de software) com o seu produto listando todas as dependências, tais como:
- origem (por exemplo, URL (Uniform Resource Locator))
- versão
- consistência (por exemplo, hash de origem SHA-256) e outros meios para validar a consistência, como compilações determinísticas.
- Exigir e auditar arquivos SBOM em dependências de software ou produzidos como parte de uma compilação, incluindo OSS (software de código aberto).
- A Microsoft está padronizando e recomenda SPDX (Software Package Data Exchange) versão 2.2 ou posterior | Linux Foundation como o formato de documento SBOM.
- O determinismo de construção pode ser usado para produzir de forma independente binários idênticos a nível de bits e fornecer verificações independentes de integridade.
- Atestado de reprodutibilidade próprio ou de terceiros
- Outras técnicas, como assinatura binária por meio de uma fonte de certificado confiável, também podem fornecer algumas garantias de integridade binária.
Recursos adicionais
As soluções Microsoft incluem as seguintes orientações e produtos:
- Plataforma da Cadeia de Abastecimento da Microsoft | Microsoft
- Proteja a sua cadeia de valor de software | Segurança do GitHub
- vcpkg - Os registros privados de vcpkg permitem o redirecionamento da aquisição de OSS para recursos controlados pela Empresa para adquirir fontes para uma dependência, para minimizar o risco de ataques upstream ou over-the-wire.