Secure coding guidelines por linguagem

1. Princípios Universais de Codificação Segura

Antes de mergulharmos nas especificidades de cada linguagem, é fundamental estabelecer princípios que transcendem qualquer tecnologia. A segurança começa com a mentalidade correta.

Validação e sanitização de entradas são a primeira linha de defesa. Toda entrada externa deve ser considerada hostil até prova em contrário. O princípio é simples: nunca confie, sempre verifique.

Princípio do menor privilégio: cada componente do sistema deve ter apenas as permissões necessárias para executar sua função. Isso limita o dano em caso de comprometimento.

Tratamento seguro de erros: mensagens de erro não devem vazar informações sensíveis como caminhos de arquivos, versões de software ou detalhes de infraestrutura. Logs devem ser protegidos contra acesso não autorizado.

2. Segurança em Python

Python é amplamente utilizado, mas seu dinamismo traz riscos específicos.

# INSEGURO - Nunca use eval ou exec com entrada do usuário
user_input = "__import__('os').system('rm -rf /')"
eval(user_input)  # Catástrofe

# SEGURO - Valide e sanitize antes de usar subprocess
import subprocess
import shlex

def executar_comando_seguro(comando):
    comando_seguro = shlex.quote(comando)  # Escapa caracteres especiais
    subprocess.run(['ls', '-l', comando_seguro], check=True)

Gerenciamento de dependências: sempre use hashes para verificar integridade dos pacotes.

# requirements.txt com hashes
requests==2.31.0 --hash=sha256:58cd2187c01e70e6e26505bca751777f9b2a0...

Serialização segura: evite pickle com dados não confiáveis. Prefira JSON.

# INSEGURO - Pickle executa código arbitrário
import pickle
dados = pickle.loads(entrada_usuario)

# SEGURO - JSON é seguro para dados não confiáveis
import json
dados = json.loads(entrada_usuario)

3. Segurança em JavaScript/Node.js

O ecossistema JavaScript enfrenta desafios únicos com XSS e dependências.

// Prevenção de XSS com sanitização de saída
const DOMPurify = require('dompurify');
const saida_segura = DOMPurify.sanitize(entrada_usuario);

// Helmet protege headers HTTP
const helmet = require('helmet');
app.use(helmet());

Segurança em dependências: npm audit é seu aliado.

# Verificar vulnerabilidades
npm audit

# Gerar lockfile para garantir integridade
npm install --package-lock-only

APIs REST seguras:

// Rate limiting com express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutos
    max: 100 // limite de 100 requisições
});
app.use('/api/', limiter);

// CORS configurado explicitamente
app.use(cors({
    origin: 'https://meudominio.com',
    methods: ['GET', 'POST']
}));

4. Segurança em Java

Java oferece proteções robustas, mas exige atenção em pontos específicos.

// INSEGURO - Concatenação de strings em SQL
String query = "SELECT * FROM usuarios WHERE nome = '" + nome + "'";

// SEGURO - PreparedStatement previne SQL injection
PreparedStatement stmt = conexao.prepareStatement(
    "SELECT * FROM usuarios WHERE nome = ?"
);
stmt.setString(1, nome);

Serialização segura:

// Validar antes de desserializar
ObjectInputStream ois = new ObjectInputStream(inputStream);
// Verificar classe permitida antes de ler
if (!classePermitida(ois.readClassDescriptor())) {
    throw new SecurityException("Classe não permitida");
}

OWASP Dependency-Check para gerenciar vulnerabilidades em bibliotecas:

# Maven plugin
<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>8.4.0</version>
</plugin>

5. Segurança em C/C++

Aqui a memória é o campo de batalha principal.

// INSEGURO - Buffer overflow potencial
char buffer[10];
strcpy(buffer, entrada_usuario);  // Se entrada > 10, overflow!

// SEGURO - strncpy com limite explícito
strncpy(buffer, entrada_usuario, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';

// Alternativa moderna: snprintf
snprintf(buffer, sizeof(buffer), "%s", entrada_usuario);

Gerenciamento seguro de memória com RAII:

// Smart pointers previnem vazamentos e duplas liberações
std::unique_ptr<Recurso> recurso = std::make_unique<Recurso>();
// Quando sair do escopo, recurso é automaticamente liberado

6. Segurança em Go

Go é moderna e segura por design, mas ainda exige cuidados.

// Race condition - INSEGURO
var contador int
go func() {
    contador++  // Data race!
}()

// SEGURO - Usando mutex
var mu sync.Mutex
go func() {
    mu.Lock()
    contador++
    mu.Unlock()
}()

// Alternativa com channels
ch := make(chan int)
go func() {
    ch <- 1
}()
resultado := <-ch

Validação de pacotes externos:

# Verificar integridade do módulo
go mod verify
# O comando verifica se os checksums dos módulos baixados
# correspondem aos registrados no go.sum

7. Segurança em Ruby

Ruby on Rails oferece proteções, mas o desenvolvedor precisa ativá-las.

# INSEGURO - SQL injection via concatenação
User.where("name = '#{params[:name]}'")

# SEGURO - ActiveRecord com placeholders
User.where("name = ?", params[:name])

# Parâmetros fortes previnem mass assignment
class UsersController < ApplicationController
  def create
    User.create(user_params)
  end

  private

  def user_params
    params.require(:user).permit(:name, :email)
  end
end

Proteção contra injeção de comandos:

# INSEGURO
system("ls #{params[:diretorio]}")

# SEGURO - Usando shellwords
require 'shellwords'
system("ls", Shellwords.escape(params[:diretorio]))

8. Ferramentas e Práticas de Verificação por Linguagem

Linters e analisadores estáticos:

# Python - Bandit
bandit -r meu_projeto/

# JavaScript - ESLint com regras de segurança
npx eslint --rules security meu_arquivo.js

# Java - FindBugs/SpotBugs
mvn spotbugs:spotbugs

Integração SAST/DAST no CI/CD:

# Exemplo GitHub Actions
- name: Análise SAST
  run: |
    bandit -r . -f json -o resultados.json
    npm audit --json > audit.json

Revisão de código focada em segurança: utilize checklists da OWASP para garantir que aspectos críticos sejam cobertos em cada code review.

Conclusão

A segurança em desenvolvimento não é um destino, mas uma jornada contínua. Cada linguagem tem suas peculiaridades, mas os princípios fundamentais permanecem: valide entradas, aplique o menor privilégio, gerencie dependências com cuidado e nunca confie em dados externos. Incorpore ferramentas de análise no pipeline e promova uma cultura de segurança desde o início do desenvolvimento.

Referências