Skip to main content

5 dicas essenciais de segurança para C++

Escrito por
Headshot of DeveloperSteve Coochin

DeveloperSteve Coochin

feature plus plus

19 de julho de 2022

0 minutos de leitura

C++ se tornou uma parte fundamental do setor de tecnologia atual. A linguagem é usada para diversas finalidades, como aplicações para desktop e servidores, jogos, realidade virtual, firmware para a Internet das Coisas e até como base para muitas linguagens de programação modernas.

Desde seu lançamento inicial, em 1985, como uma extensão da linguagem de programação C, o C++ foi projetado com foco na programação de sistemas e no desenvolvimento de recursos embarcados.

Como acontece com qualquer tecnologia de desenvolvimento amplamente adotada ou voltada para usuários, é importante incorporar medidas de segurança em toda a aplicação. Confira algumas dicas de segurança para ter em mente ao desenvolver com C++.

1. Valide e higienize as entradas nas aplicações C++

Lidar com entradas fornecidas por usuários é fundamental para proteger uma aplicação contra acessos indevidos. O C++ tem sido muito usado ao longo dos anos por permitir a manipulação de memória orientada a objetos — recurso bastante útil no desenvolvimento de aplicações, mas que também pode ser explorado como vetor de ataque por agentes maliciosos.

Uma opção é usar a função std:cin para lidar com variáveis. Ela pode ser usada de algumas maneiras, por exemplo:

int main()
{
    int varnum;

    while (true) {
        cout << "Enter a number: ";
        if (cin >> varnum) {
            break;
        } else {
            cout << "Enter a valid number!\n";
            cin.clear();
            cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
        }
    }

    return EXIT_SUCCESS;
}

Nesta função, cin.clear() limpa os sinalizadores de status de erro, e cin.ignore() remove todos os caracteres restantes no fluxo até o caractere de nova linha. É importante tratar essas mensagens de erro para que elas não apareçam na saída exibida ao usuário final. Mensagens de erro e alerta podem revelar vetores de ataque indesejados.

2. Estouro ou subfluxo de buffer

Ao lidar com entradas de usuários em C++, é importante verificar o comprimento, o intervalo, o formato e o tipo dos dados. A interação mais granular com o sistema operacional significa que, quando funções recebem um buffer de entrada muito grande, pode ocorrer um ataque de estouro de buffer.

O estouro aritmético ocorre quando o resultado de uma operação excede os valores que o tipo da expressão pode representar. Por exemplo:

uint16_t x = 65535;
x++;

std::cout << x;

A função acima retornará 0, pois x é um inteiro sem sinal que pode representar valores entre 0 e 65535. Quando esse intervalo é excedido, o valor não pode ser representado por unit16_t, o que causa um estouro.

Uma forma de lidar com estouros e subfluxos é definir uma captura ao processar um tipo de variável, verificando se o valor está entre os limites mínimo e máximo — e detectando, assim, qualquer valor que ultrapasse esses limites. Com templates, você define esse intervalo e cria uma captura genérica reutilizável para essas ocorrências.

#include <limits>

template <typename V>
constexpr bool AdditionOverflows(const V& a, const V& b) {
    return (b >= 0) && (a > std::numeric_limits<V>::max() - b);
}

template <typename V>
constexpr bool AdditionUnderflows(const V& a, const V& b) {
    return (b < 0) && (a < std::numeric_limits<V>::min() - b);
}

É importante evitar comportamentos indefinidos ao usar C++. A maioria dos compiladores avisa quando há comportamentos indefinidos durante a compilação da aplicação. O GCC e o Clang, por exemplo, oferecem a -fsanitize=undefined flag.

Além disso, tenha cuidado ao usar funções como strcpy() e strcat() na biblioteca do C++. Embora sejam úteis para lidar com variáveis, elas podem causar estouros e subfluxos se o valor armazenado exceder a capacidade do tipo de armazenamento.

3. Operações incorretas no sistema de arquivos podem gerar vulnerabilidades de travessia de diretórios

O tratamento inadequado de arquivos pode levar a vulnerabilidades de travessia de diretórios. Isso é perigoso em qualquer framework, mas especialmente em um sistema operacional com uma linguagem granular como C++. Em C++, os arquivos são tratados como fluxos e como abstrações que representam a entrada e a saída de dados. Um fluxo pode ser representado por uma sequência de caracteres de tamanho indefinido, dependendo da estrutura do código.

O C++ oferece várias técnicas para manipular arquivos, incluindo ifstream(), ofstream() , e fstream(). Essas funções derivam dos grupos de classes fstreambase e iostream e foram projetadas para gerenciar os arquivos fisk declarados em fstream.

As classes de manipulação de arquivos se diferenciam da seguinte forma:

  • ofstream: essa classe representa o fluxo de saída e é usada para criar arquivos e gravar informações neles.

  • ifstream: essa classe representa o fluxo de entrada de arquivos e é usada para ler informações de arquivos.

  • fstream: essa classe pode ser usada para ler e gravar arquivos.

É importante garantir que os fluxos de arquivos sejam tratados com segurança. Comece usando new_file.open() para iniciar um novo arquivo e, em seguida, grave o fluxo no arquivo. Por fim, feche o arquivo quando terminar e inclua uma instrução catch para tratar erros que possam ocorrer durante a operação.

Por exemplo:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    fstream newFile; 
    newFile.open("file_write.txt",ios::out);  

    if(!newFile) 
    {
        cout<<"File failed";
    }
    else
    {
        cout<<"File created";
        newFile<<"File write";    //File write
        newFile.close(); // Close file 
    }   
    return 0;
}

4. Limite o uso de system() e de chamadas de execução de processos para evitar injeção de comandos

Assim como em muitas outras linguagens, é preciso ter muito cuidado ao interagir com o sistema por meio de funções no código. Em C++, funções como system() devem ser evitadas ou usadas com cautela. Essas funções de sistema variam de acordo com o chipset e o hardware em que a aplicação é executada.

Também é importante garantir que a aplicação seja instalada e executada de modo a permitir funções executáveis adicionais, como exec(), execp() , e CreateProcess(). No entanto, elas também devem ser usadas com cautela, especialmente porque podem dar acesso a partes essenciais do sistema operacional.

Lembre-se de que aplicações em C++ têm acesso muito mais granular a informações críticas do sistema, o que pode expor mais atividades maliciosas e caminhos vulneráveis.

5. Analise as bibliotecas do projeto com uma ferramenta abrangente de análise estática de código

Acompanhar os caminhos potencialmente vulneráveis nas bibliotecas e dependências do projeto é essencial para manter a segurança. Esses caminhos podem permitir ações indevidas por agentes maliciosos, especialmente em projetos C++ instalados em diferentes sistemas e níveis de arquitetura.

A análise estática de código, ou SCA, é útil porque pode alertar você sobre possíveis vulnerabilidades durante o desenvolvimento de uma aplicação e avisar quando uma vulnerabilidade é identificada no projeto implantado.

Como começar a analisar código C++ com SCA no Snyk

Para começar a usar o Snyk, instale o Snyk CLI no seu ambiente de desenvolvimento. Há várias maneiras de instalar a CLI, incluindo npm, Yarn e executáveis independentes.

Em seguida, conecte a CLI à sua conta do Snyk. Se ainda não tiver uma conta, é rápido e fácil se cadastrar. Acesse a página de cadastro e siga as etapas para começar.

Depois de instalar a CLI e entrar na sua conta do Snyk, execute snyk auth na linha de comando para concluir a conexão da conta. Isso vai gerar um link de autenticação e abrir automaticamente a página de autenticação no navegador. Clique em Authenticate e pronto: você já pode usar o Snyk CLI.

Página de autenticação da CLI do Snyk com o título “Autenticar na CLI”, instruções e um botão Autenticar.

Agora, para concluir, acesse o diretório raiz do projeto que você quer analisar e execute snyk test –unmanaged. O processo de análise localizará a pasta na raiz do projeto que contém as bibliotecas de código aberto e fará a verificação.

Saída do terminal listando vulnerabilidades altas e críticas no FFmpeg, incluindo gravações fora dos limites, estouro de inteiro e referência a ponteiro nulo.

Ao concluir, você verá todos os resultados encontrados e poderá investigá-los em mais detalhes. Um recurso de que gosto bastante é poder explorar os problemas identificados pelos links da análise e entender melhor cada vulnerabilidade.

Página da Snyk Vulnerability Database sobre uma vulnerabilidade de valor de retorno não verificado no ffmpeg, classificada como Crítica, com nota 9,8.

Cadastre-se agora e comece a analisar

Para quem desenvolve, trabalha com DevOps ou atua em tecnologia, priorizar a segurança e fazer tudo ao nosso alcance para proteger as aplicações é fundamental. Incorporar a segurança ao desenvolvimento de aplicações ajuda a proteger os usuários finais e manter seguros tanto eles quanto seus dados.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.

Leia mais

Blog

Modelos de ponta encontraram as vulnerabilidades. Só o atacante encontrou as cadeias.

A análise estática encontrou as falhas, mas só os testes de ataque em aplicações ativas provaram como elas poderiam ser encadeadas para causar invasões. Uma comparação entre Evo COS, Claude Security e Claude Code Security.

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

Blog

Por que agentes de programação com IA continuam criando falhas de controle de acesso

Agentes de programação com IA podem gerar uma lógica de autorização que compila e passa pela revisão, mas permite que um tenant acesse os dados de outro. Saiba por que é difícil detectar falhas de controle de acesso e como evitá-las.