Seguranca em Aplicacoes Python — 2025 | Python Brasil

Boas praticas de seguranca para aplicacoes Python. Previna SQL injection, XSS, CSRF e gerencie segredos de forma segura nos seus projetos.

5 min de leitura Equipe Python Brasil

Seguranca e um aspecto critico no desenvolvimento de software, e Python nao e excecao. Aplicacoes mal protegidas podem expor dados sensiveis, permitir acesso nao autorizado e comprometer servidores inteiros. Neste guia, a gente vai explorar as vulnerabilidades mais comuns e como preveni-las em projetos Python.

SQL Injection

SQL injection acontece quando dados do usuario sao inseridos diretamente em queries SQL. E uma das vulnerabilidades mais antigas e perigosas.

# ERRADO: vulneravel a SQL injection
def buscar_usuario_inseguro(cursor, nome_usuario):
    query = f"SELECT * FROM usuarios WHERE nome = '{nome_usuario}'"
    cursor.execute(query)
    return cursor.fetchone()
    # Se o usuario digitar: ' OR '1'='1
    # A query vira: SELECT * FROM usuarios WHERE nome = '' OR '1'='1'

# CORRETO: usando parametros
def buscar_usuario_seguro(cursor, nome_usuario):
    query = "SELECT * FROM usuarios WHERE nome = %s"
    cursor.execute(query, (nome_usuario,))
    return cursor.fetchone()

Com ORMs como SQLAlchemy, a protecao ja e automatica:

from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session

engine = create_engine("sqlite:///app.db")

# Seguro: SQLAlchemy parametriza automaticamente
with Session(engine) as session:
    resultado = session.query(Usuario).filter(
        Usuario.nome == nome_usuario
    ).first()

    # Ou com text() e bind parameters
    resultado = session.execute(
        text("SELECT * FROM usuarios WHERE nome = :nome"),
        {"nome": nome_usuario}
    ).fetchone()

Cross-Site Scripting (XSS)

XSS permite que atacantes injetem scripts maliciosos em paginas web. Sempre escape dados do usuario antes de renderizar no HTML:

# ERRADO: renderiza HTML nao escapado
from flask import Flask, request

@app.route("/saudacao")
def saudacao_insegura():
    nome = request.args.get("nome", "")
    return f"<h1>Ola, {nome}!</h1>"
    # URL maliciosa: /saudacao?nome=<script>alert('XSS')</script>

# CORRETO: usar template engine com escape automatico
from flask import render_template_string
from markupsafe import escape

@app.route("/saudacao")
def saudacao_segura():
    nome = request.args.get("nome", "")
    return f"<h1>Ola, {escape(nome)}!</h1>"

Frameworks como Django e Flask com Jinja2 escapam dados automaticamente nos templates, mas cuidado com o filtro |safe e Markup():

# Em templates Jinja2, dados ja sao escapados por padrao
# {{ nome }}  -> escapado automaticamente
# {{ nome|safe }}  -> PERIGOSO: desativa escaping

Gerenciamento de Segredos

Nunca coloque senhas, chaves de API ou tokens no codigo fonte:

# ERRADO: segredos hardcoded
DATABASE_URL = "postgresql://admin:senha123@localhost/mydb"
API_KEY = "sk-1234567890abcdef"

# CORRETO: usar variaveis de ambiente
import os

DATABASE_URL = os.environ["DATABASE_URL"]
API_KEY = os.environ["API_KEY"]

# Melhor ainda: usar python-dotenv para desenvolvimento
from dotenv import load_dotenv

load_dotenv()  # Carrega .env automaticamente

DATABASE_URL = os.getenv("DATABASE_URL")
API_KEY = os.getenv("API_KEY")

Crie um arquivo .env para desenvolvimento e adicione-o ao .gitignore:

# .env (NUNCA comite este arquivo)
DATABASE_URL=postgresql://admin:senha123@localhost/mydb
API_KEY=sk-1234567890abcdef
SECRET_KEY=minha-chave-secreta-muito-longa

Hashing de Senhas

Nunca armazene senhas em texto plano. Use algoritmos de hash seguros:

# ERRADO: armazenar senha em texto plano
def salvar_usuario_inseguro(email, senha):
    db.execute("INSERT INTO usuarios (email, senha) VALUES (%s, %s)",
               (email, senha))  # Senha visivel no banco

# CORRETO: usar bcrypt
import bcrypt

def hash_senha(senha: str) -> str:
    """Gera hash seguro da senha."""
    salt = bcrypt.gensalt(rounds=12)
    return bcrypt.hashpw(senha.encode("utf-8"), salt).decode("utf-8")

def verificar_senha(senha: str, hash_armazenado: str) -> bool:
    """Verifica se a senha corresponde ao hash."""
    return bcrypt.checkpw(
        senha.encode("utf-8"),
        hash_armazenado.encode("utf-8")
    )

# Uso
hash_salvo = hash_senha("minha_senha_forte")
print(hash_salvo)
# $2b$12$LJ3m4ys3Lg...

assert verificar_senha("minha_senha_forte", hash_salvo) == True
assert verificar_senha("senha_errada", hash_salvo) == False

Validacao de Entrada

Sempre valide e sanitize dados recebidos do usuario:

from pydantic import BaseModel, EmailStr, validator, Field

class CadastroUsuario(BaseModel):
    """Schema de validacao para cadastro."""
    nome: str = Field(min_length=2, max_length=100)
    email: EmailStr
    idade: int = Field(ge=18, le=120)
    senha: str = Field(min_length=8, max_length=128)

    @validator("nome")
    def nome_sem_caracteres_especiais(cls, v):
        if not v.replace(" ", "").isalpha():
            raise ValueError("Nome deve conter apenas letras")
        return v.strip()

    @validator("senha")
    def senha_forte(cls, v):
        if not any(c.isupper() for c in v):
            raise ValueError("Senha deve ter pelo menos uma maiuscula")
        if not any(c.isdigit() for c in v):
            raise ValueError("Senha deve ter pelo menos um numero")
        return v

# Uso com FastAPI ou Flask
try:
    usuario = CadastroUsuario(
        nome="Ana Silva",
        email="ana@email.com",
        idade=28,
        senha="Senha123Forte"
    )
    print(f"Cadastro valido: {usuario.nome}")
except Exception as e:
    print(f"Erro de validacao: {e}")

CSRF Protection

Cross-Site Request Forgery pode ser prevenido com tokens CSRF:

# Flask com Flask-WTF
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]
csrf = CSRFProtect(app)

# No template HTML
# <form method="POST">
#     {{ csrf_token() }}
#     <input type="text" name="nome">
#     <button type="submit">Enviar</button>
# </form>

# Para APIs, use tokens no header
@app.route("/api/dados", methods=["POST"])
@csrf.exempt  # Apenas se usar autenticacao por token JWT
def api_dados():
    pass

Headers de Seguranca

Configure headers HTTP para proteger a aplicacao:

from flask import Flask

app = Flask(__name__)

@app.after_request
def adicionar_headers_seguranca(response):
    """Adiciona headers de seguranca a todas as respostas."""
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "DENY"
    response.headers["X-XSS-Protection"] = "1; mode=block"
    response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    response.headers["Content-Security-Policy"] = "default-src 'self'"
    response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
    return response

Dependencias Seguras

Mantenha suas dependencias atualizadas e verifique vulnerabilidades:

# Verificar vulnerabilidades conhecidas
pip install safety
safety check

# Ou com pip-audit
pip install pip-audit
pip-audit

# Fixar versoes no requirements.txt
pip freeze > requirements.txt

Automatize verificacoes com pre-commit hooks:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Lucas-C/pre-commit-hooks-safety
    rev: v1.3.1
    hooks:
      - id: python-safety-dependencies-check
        files: requirements.txt

  - repo: https://github.com/PyCQA/bandit
    rev: 1.7.5
    hooks:
      - id: bandit
        args: ["-r", "app/"]

Rate Limiting

Proteja suas APIs contra abuso com rate limiting:

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    get_remote_address,
    app=app,
    default_limits=["200 per day", "50 per hour"]
)

@app.route("/api/login", methods=["POST"])
@limiter.limit("5 per minute")
def login():
    """Endpoint de login com limite de tentativas."""
    pass

@app.route("/api/dados")
@limiter.limit("100 per hour")
def dados():
    """Endpoint de dados com limite por hora."""
    pass

Boas Praticas Gerais

Adote estas praticas em todos os seus projetos Python:

  • Use HTTPS em producao para criptografar dados em transito
  • Mantenha Python e todas as dependencias atualizados
  • Execute analise estatica de seguranca com Bandit regularmente
  • Implemente logging adequado para auditoria sem expor dados sensiveis
  • Use o principio do menor privilegio em acessos a banco de dados
  • Faca revisoes de codigo focadas em seguranca
  • Configure timeouts em requisicoes HTTP e conexoes de banco

Conclusao

Seguranca nao e algo que se adiciona ao final do projeto. Ela deve ser parte do processo de desenvolvimento desde o inicio. Com as praticas apresentadas neste guia, voce pode prevenir as vulnerabilidades mais comuns e proteger seus usuarios e dados. Comece pelas praticas mais simples como gerenciamento de segredos e validacao de entrada, e gradualmente adicione camadas de protecao ao seu projeto.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português