O que é codificação segura? Visão geral e práticas recomendadas

12 Minuto de leitura
Main Takeaways from Secure Coding:
  • Secure coding tackles vulnerabilities like XSS and memory leaks early, boosting software resilience and reducing risks.

  • Proactive practices save time and money by preventing costly post-release fixes and fostering user trust.

  • Best practices include validating inputs, securing third-party code, and leveraging tools like SAST for continuous checks.

  • Standards from OWASP, CERT, and NIST help developers build secure, reliable applications.

  • Wiz Code supports secure coding with real-time scans, actionable feedback, and guidance to safeguard your SDLC.

  • A codificação segura lida com vulnerabilidades como XSS e vazamentos de memória antecipadamente, aumentando a resiliência do software e reduzindo os riscos.

  • As práticas proativas economizam tempo e dinheiro, evitando correções dispendiosas após o lançamento e promovendo a confiança do usuário.

  • As práticas recomendadas incluem validar entradas, proteger código de terceiros e aproveitar ferramentas como SAST para verificações contínuas.

  • Os padrões do OWASP, CERT e NIST ajudam os desenvolvedores a criar aplicativos seguros e confiáveis.

  • O Wiz Code oferece suporte a codificação segura com varreduras em tempo real, feedback acionável e orientação para proteger seu SDLC.

O que é codificação segura?

A codificação segura é a prática de desenvolver software resistente a vulnerabilidades de segurança, aplicando as melhores práticas, técnicas e ferramentas de segurança no início do desenvolvimento. Em vez de pensar apenas na experiência do usuário, a codificação segura alinha todos os recursos com medidas de segurança, desde o início do ciclo de vida de desenvolvimento de software.

Por exemplo, um aplicativo que aceita todos os dados de um cliente sem higienizá-lo pode ser mais fácil de implementar, usar e manter. No entanto, ele abre um ponto de entrada para invasores injetarem código mal-intencionado.

Why is secure coding important?

A codificação segura incorpora segurança ao DNA do seu software para evitar vulnerabilidades como injeção de SQL, estouros de buffer, scripts entre sites e muito mais. Além de apenas evitar violações, é uma maneira de proteger a confiança do usuário, mudar a segurança para a esquerda e atender aos rígidos padrões das leis de proteção de dados.

A recompensa? Menos surpresas desagradáveis após o lançamento, aplicativos mais fortes e melhor proteção para os usuários e sua organização.

Sete técnicas de codificação segura para criar software seguro

Um processo de desenvolvimento de software seguro começa com o cumprimento das práticas de codificação corretas que ajudam a evitar vulnerabilidades e manter seus aplicativos seguros. Se você're procurando um recurso mais aprofundado, certifique-se de verificar os requisitos de codificação segura OWASP em seu Guia do desenvolvedor.

Enquanto isso, aqui estão algumas técnicas importantes que você pode começar a usar imediatamente para criar sistemas de software mais seguros:

1. Use linguagens e ferramentas modernas

Muitas vulnerabilidades de segurança relacionadas à memória afetam linguagens de programação com gerenciamento manual de memória e sem verificações de memória internas. Ao iniciar um novo projeto, certifique-se de que você realmente precisa de C/C++ para ele e, se precisar, use Ponteiros inteligentes e Analisadores de código estático para minimizar o impacto de falhas de linguagem.

Se você precisa de recursos de programação do sistema, uma linguagem mais moderna como Ferrugem pode ser uma boa escolha porque seu tipo de sistema verifica o uso da memória em tempo de compilação. Zig também pode ser uma boa alternativa, pois não tem fluxo de controle oculto ou alocações de memória.

Se você não precisa de recursos de programação do sistema, usar uma linguagem coletada de lixo como Java ou C# pode protegê-lo de muitos problemas de memória.

2. Validar e higienizar dados de entrada e saída

Os dados do usuário não validados são a principal razão para falhas de injeção. É por isso que é extremamente importante validar todos os dados que entram no seu sistema. O saneamento é outro passo que pode manter a segurança sob controle sem sacrificar a usabilidade. Em vez de rejeitar uma entrada do usuário se ela for inválida, o saneamento cortará partes de entrada problemáticas (ou seja, JavaScript dentro do HTML) e usará os dados restantes. Ao executar em um ambiente cliente-servidor, certifique-se de que essa validação e higienização aconteçam no servidor. Isso significa adicionar Validadores e Sanitizantes para todos os pontos de extremidade da API que aceitam dados do usuário. Isso também pode significar a escolha de formatos de dados fáceis de validar, por exemplo, aceitar Markdown simples em vez de HTML completo.

Manter os dados de entrada limpos nem sempre é possível; As bibliotecas de validação também têm bugs. Para garantir que nada vaze para seus usuários, exiba apenas saídas com base nas entradas do usuário de forma segura (ou seja, não renderize HTML).

3. Verificar a integridade do código de terceiros

Bibliotecas e estruturas de terceiros são salva-vidas para acelerar o desenvolvimento, mas vêm com restrições - elas não foram construídas em sua casa. Trate-os como qualquer entrada para o seu processo de construção: cuidadosamente examinados e sob controle.

Quer evitar surpresas desagradáveis? Fixar dependências em versões específicas ou hashes para impedir que atualizações não testadas entrem em produção. Auditar e atualizar regularmente essas bibliotecas não é glamoroso, mas é a única maneira de evitar que o código desatualizado se torne seu calcanhar de Aquiles.

4. Aplique um controle de acesso rigoroso

O controle de acesso limita quem pode visualizar ou modificar código e recursos, protegendo funções e dados confidenciais de usuários não autorizados. Atenha-se ao Princípio do privilégio mínimo: dê aos usuários apenas o que eles precisam para fazer seu trabalho - nada mais, nada menos.

Para maior segurança, considere a implementação de controles de acesso baseados em função (RBAC) e autenticação multifator (MFA). Essas medidas reduzem ainda mais sua superfície de ataque e garantem que indivíduos não autorizados não possam acessar sistemas ou dados críticos.

5. Implementar o tratamento e o registro adequados de erros

Ninguém quer entregar aos invasores um roteiro, mas é exatamente isso que mensagens de erro excessivamente detalhadas podem fazer. Mantenha os detalhes internos (rastreamentos de pilha e erros de banco de dados, por exemplo) fora do alcance dos usuários. Em vez disso, registre-os, com segurança e atenção, apenas para os olhos de sua equipe.

Bons registros contam a história: o que aconteceu, quando e por quê. Monitore-os em busca de qualquer coisa suspeita, mas não exagere registrando dados confidenciais. O equilíbrio é fundamental aqui - você está solucionando problemas, não expondo.

6. Automatize revisões de código

As revisões manuais são importantes, mas a automação é necessária. Ferramentas automatizadas como teste de segurança de aplicativo estático (SAST) e os linters sinalizam vulnerabilidades e erros de codificação mais rápido do que os humanos jamais poderiam.

Conecte essas ferramentas ao pipeline de CI/CD e cada alteração de código será revisada antes de ser mesclada. O feedback imediato mantém os desenvolvedores informados e garante que as práticas recomendadas de segurança permaneçam na frente e no centro.

7. Aplicar técnicas de ofuscação de código

A ofuscação de código não torna seu aplicativo à prova de balas, mas retarda os invasores. Renomear variáveis para jargões, codificar strings e reestruturar o código dificultam a engenharia reversa ou o roubo de propriedade intelectual.

Pense nisso como adicionar camuflagem: o aplicativo ainda funciona sem problemas para os usuários, mas os malfeitores acharão muito mais difícil invadir ou entender o que veem. Cada obstáculo ajuda.

Vulnerabilidades comuns de software de código

Vejamos as vulnerabilidades de segurança comuns que os desenvolvedores de software e pesquisadores de segurança identificaram. Vamos de problemas de baixo nível, como vulnerabilidades de memória, para problemas de nível superior, como ataques de injeção.

Estouro de buffer

Os estouros de buffer podem travar seu aplicativo ou permitir que invasores gravem dados em outros buffers. 

Linguagens de programação de sistema como C/C++ são propensas a essa vulnerabilidade. Eles permitem e até exigem gerenciamento de memória explicitamente, mas não verificam o acesso à memória até que seja tarde demais. Se você gravar mais dados em um buffer do que o atribuído no momento da definição, C substituirá todos os dados de memória que seguem no final do buffer.

Exemplo de estouro de buffer em C:

Int B[5];
  b[5] = 999; buffer só vai de 0 a 4

Use depois de gratuito

O uso após a liberação acontece quando você libera memória na pilha, mas continua usando o ponteiro antigo.

Novamente, essa vulnerabilidade é proeminente em linguagens sem coleta de lixo, como C/C++, onde você deve gerenciar manualmente a memória. Existem dois tipos de memória: a pilha e o heap. A linguagem gerencia automaticamente a pilha, que não pode armazenar dados com tamanhos dinâmicos que não são conhecidos em tempo de compilação. O heap é para dados dinâmicos, mas você deve alocar manualmente e liberar espaço nele. Liberar significa que você diz ao sistema operacional que não precisa mais da memória, portanto, se você usá-la depois com um ponteiro, o acesso ilegal irá para um local de memória não alocado.

Exemplo de uso após livre em C:

char* p = (char*)malloc (16);
      p = strdup("Algum texto!");
      livre (p);
      printf("%s", p); imprime o que está agora na memória liberada

Duplo gratuito

No caso do double free, você está liberando memória de pilha depois de já tê-la liberado. 

A liberação dupla é um problema em idiomas com gerenciamento de memória manual, onde você deve dizer explicitamente ao sistema operacional que não precisa mais de um intervalo de memória específico. Fazer isso duas vezes resultará em uma falha semelhante ao uso após a emissão gratuita. Isso geralmente acontece quando você tem vários objetos com ponteiros uns para os outros que são liberados em algum momento. Double free pode corromper a memória de um ponteiro referenciado antes do primeiro free.

Exemplo de duplo livre em C:

char* p = (char*)malloc (16);
      p = strdup("Algum texto!");
      livre (p);
      livre (p); corromperá o que está na memória liberada

Desserialização insegura

A desserialização insegura envolve a transformação direta de uma estrutura de dados externa (por exemplo, JSON, XML, etc.) em uma estrutura interna (por exemplo, objetos, matrizes, etc.) sem verificações suficientes.

A desserialização insegura é uma vulnerabilidade comum em todos os tipos de aplicativos. Pode ser bom aceitar dados não higienizados durante o desenvolvimento, mas os usuários podem se esgueirar em dados maliciosos sem aviso prévio se isso for feito em produção. 

Exemplo de desserialização insegura em JSON:

{
  "nome": "exemplo",
  "Email": "email@example.com",
  "isAdmin": true // deve ser excluído no servidor
}

Vazamentos de memória

Vazamentos de memória permitem que seu aplicativo consuma memória sem limites. Se você esgotar a memória disponível e solicitar mais, seu aplicativo falhará. 

Todo aplicativo suficientemente complexo é suscetível a essa vulnerabilidade. Mesmo os idiomas coletados de lixo não estão a salvo de vazamentos de memória. As linguagens coletadas por lixo ainda permitem que você crie estruturas de dados que um coletor de lixo não pode gerenciar. 

Falhas de injeção

Executar a entrada do usuário como código sem validá-lo é conhecido como uma falha de injeção.

Esse problema pode afetar todos os aplicativos, independentemente da linguagem de programação usada. Uma maneira de tornar seu aplicativo vulnerável a falhas de injeção é permitir que os usuários adicionem código personalizado como um recurso e não protejam a execução corretamente. Os estouros de buffer que permitem que invasores gravem código em locais de memória executável são outra maneira de seu aplicativo se tornar vulnerável a falhas de injeção.

Script entre sites (XSS)

O script entre sites é uma versão específica da Web de uma falha de injeção. Aqui, um invasor insere JavaScript personalizado oculto dentro da marcação HTML.

XSS pode acontecer em todos os sites. Como a marcação e o código executável estão totalmente integrados na Web, é fácil inserir JavaScript em HTML, que vaza dados confidenciais.

Exemplo de XSS em HTML e JavaScript:

<-- isso enviará uma solicitação de busca 
quando o mouse está sobre o <p> elemento-->
<p onmouseover="fetch('//example.com')">Olá, mundo!</p>

Entidades externas XML (XXE)

As entidades externas XML são outra instância de uma falha de injeção. Todos os aplicativos que usam XML são suscetíveis a esse ataque. A ideia por trás de entidades externas em XML é permitir a reutilização de arquivos XML existentes. No entanto, um invasor pode usar esse recurso para incluir links para arquivos XML privados, permitindo que eles leiam dados privados indiretamente por meio de seu arquivo XML carregado.

Exemplo de injeção de entidade XML externa:

<?versão xml="1.0" codificação="ISO-8859-1"?>
<! DOCTYPE a [  
  <! ELEMENTO A QUALQUER >
  <-- isso define uma nova entidade chamada xxe
  de um arquivo privado -->
  <! ENTIDADE xxe SISTEMA "file:///etc/passwd" >
]>
<-- aqui, a entidade é renderizada para exibição 
o conteúdo do arquivo -->
<a>&xxe;</a>

Referência de objeto direto (IDOR) inseguro

Quando você permite que APIs públicas façam referência a objetos com IDs sequenciais diretamente, os IDORs podem permitir que invasores adivinhem a ID de todos os objetos no servidor. 

Esse problema pode acontecer em qualquer lugar em que IDs sequenciais são usados para fazer referência a objetos e é especialmente grave ao usar os IDs para fazer referência a objetos públicos e privados sem exigir autorização.

URLs de exemplo:

https://example.com/users/4539

https://example.com/users/4540

https://example.com/users/4541

Travessia de diretório (também conhecida como travessia de caminho)

Outra falha de injeção é onde os invasores podem atravessar caminhos ou estruturas de diretório por meio de entradas de nome de arquivo.

Todos os aplicativos que permitem entradas de nome de arquivo podem ser vítimas dessa vulnerabilidade. A travessia de diretório pode acontecer quando os usuários carregam vários arquivos fazendo referência uns aos outros por meio de caminhos relativos. Os invasores podem usar caminhos de travessia de arquivos como ".." para navegar a partir do diretório de upload no servidor e em diretórios com arquivos de administradores ou outros usuários.

Exemplo de travessia de diretório em JavaScript no Node.js:

Isso carrega um arquivo javascript privado
modelo const = require(".. /.. /.. /servidor/config/banco de dados")
  render(modelo)

Padrões de segurança de código

Padrões de codificação segura são conjuntos de diretrizes e práticas recomendadas que os desenvolvedores seguem para criar software seguro e minimizar vulnerabilidades. Eles abordam erros de codificação comuns e fraquezas que podem ser exploradas por invasores, com o objetivo de criar código mais resiliente e resistente.

Abaixo estão os padrões comuns de código seguro a serem seguidos:

1. Práticas de codificação segura do OWASP:

As Práticas de Codificação Segura (SCP) do OWASP são diretrizes do Open Web Application Security Project que se concentram em áreas-chave para melhorar a segurança do software, como validação de entrada, autenticação, gerenciamento de sessão, criptografia e tratamento de erros. É um roteiro para um código mais seguro, a partir do seu primeiro confirmar a implantação final.

2. Padrões de codificação segura CERT:

CERT Secure Coding Standards (SCS) é um conjunto de diretrizes e recomendações desenvolvidas pelo Software Engineering Institute (SEI) da Carnegie Mellon University para ajudar os desenvolvedores a escrever código seguro e prevenir vulnerabilidades. Principais áreas de enfoque:

  • Diretrizes específicas do idioma:Oferecendo recomendações para C, C++, Java, Android e Perl para resolver vulnerabilidades comuns nessas linguagens.

  • Programação defensiva:Enfatizando a antecipação e o tratamento de erros graciosamente para evitar a exploração.

  • Gerenciamento de memória:Concentre-se em evitar estouros de buffer e vazamentos de memória, especialmente em linguagens como C e C++.

3. Diretrizes de codificação segura do NIST:

As Diretrizes de Codificação Segura do NIST (também conhecidas como Publicação Especial 800-218 do NIST) concentram-se em áreas críticas, como validação de entrada, autenticação, criptografia e tratamento de erros, oferecendo conselhos claros para manter ataques de injeção, sequestro de sessão e problemas de memória fora do seu software. Se você deseja um selo de aprovação apoiado pelo governo em seu Práticas de segurança de código, este é o seu destino.

4. ISO/IEC 27001:

A ISO/IEC 27001 é uma norma internacional de segurança da informação. Enquanto ele's não especificamente um padrão de codificação seguro, ele inclui requisitos para práticas de codificação segura como parte de uma abordagem abrangente de gerenciamento de segurança. O Anexo A, Controle 8.28: Práticas de Codificação Segura, concentra-se especificamente na codificação segura e enfatiza como as organizações devem:

  • Desenvolva processos de codificação seguros para desenvolvimento interno e código de terceiros.

  • Mantenha-se informado sobre a evolução das ameaças e vulnerabilidades.

  • Implemente princípios de codificação robustos e seguros para resolvê-los.

Garanta um ciclo de vida de desenvolvimento de software seguro com a Wiz

A codificação segura é uma prática que abrange todos os aspectos do desenvolvimento de software, desde a escolha de formatos de dados e linguagens de programação até o planejamento de entradas e saídas e implementação. 

Nós'Estou animado para apresentar Código Wiz, nossa mais recente inovação projetada para capacitar desenvolvedores e equipes de segurança a implementar e manter práticas robustas de codificação segura durante todo o ciclo de vida de desenvolvimento de software!

A Wiz Code estende nossa plataforma de segurança em nuvem para cobrir todas as etapas de desenvolvimento, oferecendo recursos poderosos para apoiar suas iniciativas de codificação segura:

  • Varredura de código integrada: Detecte vulnerabilidades, configurações incorretas e problemas de conformidade diretamente em seu IDE e repositórios de código, detectando possíveis problemas antes que eles cheguem à produção.

  • Feedback de segurança em tempo real: Obtenha insights de segurança instantâneos enquanto codifica, permitindo que os desenvolvedores resolvam problemas imediatamente e aprendam práticas de codificação seguras em qualquer lugar.

  • Rastreabilidade da nuvem para o código: rastreie os riscos descobertos em ambientes de produção de volta ao código específico e às equipes que os introduziram, facilitando a análise e a correção rápidas da causa raiz.

  • Diretrizes de correção no código: Receba recomendações acionáveis e sensíveis ao contexto para corrigir problemas de segurança diretamente em seu ambiente de desenvolvimento.

  • Suporte abrangente a idiomas: Beneficie-se das melhores práticas de codificação seguras em uma ampla variedade de linguagens de programação e estruturas.

Secure your SDLC from start to finish

See why Wiz is one of the few cloud security platforms that security and devops teams both love to use.

Ver demonstração