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
- OWASP Secure Coding Practices — Guia de referência rápida com práticas essenciais de codificação segura aplicáveis a qualquer linguagem
- Python Security Best Practices — Documentação oficial do Python sobre considerações de segurança, incluindo subprocess e serialização
- Node.js Security Checklist — Guia oficial do Node.js com práticas recomendadas para segurança de aplicações
- Java Secure Coding Guidelines — Diretrizes oficiais da Oracle para codificação segura em Java
- SEI CERT C Coding Standard — Padrão de codificação segura para C/C++ do SEI CERT, incluindo prevenção de buffer overflow
- Go Security Policy — Política de segurança oficial da linguagem Go, com diretrizes para evitar race conditions e vulnerabilidades
- Ruby on Rails Security Guide — Guia completo de segurança para Ruby on Rails, cobrindo SQL injection, mass assignment e XSS
- OWASP Dependency-Check — Ferramenta para identificar vulnerabilidades conhecidas em dependências de projetos Java, .NET, Python e Ruby