🔑 OAuth 2.0

O OAuth 2.0 é a estrutura de autorização mais amplamente usada para acesso à API. A seleção do tipo de concessão adequado é fundamental.

Escolha de um tipo de subsídio

Tipo de subsídio Caso de uso Nível de segurança
Authorization Code + PKCE SPAs e aplicativos móveis (recomendado) Mais alto
Authorization Code Aplicativos da Web no lado do servidor Alta
Client Credentials Comunicação de servidor para servidor (M2M) Médio
Implícito (obsoleto) SPAs legados Baixa
Senha do proprietário do recurso (obsoleto) Somente de terceiros altamente confiáveis Baixa
Não usar concessão implícita

A concessão implícita agora está obsoleta porque o token é exposto no fragmento de URL. Use Código de autorização + PKCE para SPAs.

Código de autorização + fluxo de PKCE

JavaScript Geração de desafios PKCE
// 1. gerar aleatoriamente o code_verifier
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return btoa(String.fromCharCode(...array))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// 2. gerar code_challenge com SHA-256
async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// Solicitação de autorização
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);

const authUrl = `https://auth.example.com/authorize?` +
  `response_type=code&` +
  `client_id=${clientId}&` +
  `redirect_uri=${redirectUri}&` +
  `code_challenge=${challenge}&` +
  `code_challenge_method=S256&` +
  `scope=read write`;

🎫 JWT (JSON Web Token)

O JWT é amplamente usado como um token de autenticação sem estado, mas erros de implementação levam diretamente a vulnerabilidades de segurança.

Considerações sobre a segurança do JWT

✅ Fazer

  • Especificar explicitamente o algoritmo (HS256 / RS256)
  • Defina um tempo de expiração (exp) curto (15 minutos ou menos)
  • Validar o emissor (iss) e o público (aud)
  • Gerar chaves secretas com comprimento suficiente (256 bits ou mais)
  • Implementar rotação para tokens de atualização

Não

  • Armazenar informações confidenciais na carga útil do JWT (Base64 não é criptografia)
  • Permitir alg: "none"
  • Armazenar tokens no localStorage (vulnerável a XSS)
  • Negligenciar o gerenciamento de revogação de tokens
  • Usar uma chave pública RS256 como uma chave secreta HS256

Implementação segura de JWT

JavaScript (Node.js) Emissão e verificação de tokens
const jwt = require('jsonwebtoken');

// --- Emissão de token ---
function issueTokens(user) {
  const accessToken = jwt.sign(
    { sub: user.id, role: user.role },
    process.env.JWT_SECRET,
    {
      algorithm: 'HS256',
      expiresIn: '15m',
      issuer: 'api.example.com',
      audience: 'app.example.com',
    }
  );

  const refreshToken = jwt.sign(
    { sub: user.id, type: 'refresh' },
    process.env.REFRESH_SECRET,
    { algorithm: 'HS256', expiresIn: '7d' }
  );

  return { accessToken, refreshToken };
}

// --- Verificação de token ---
function verifyAccessToken(token) {
  return jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ['HS256'],   // ← Sempre especifique!
    issuer: 'api.example.com',
    audience: 'app.example.com',
  });
}

Comparação de locais de armazenamento de tokens

Local de armazenamento Resistência a XSS Resistência a CSRF Recomendação
HttpOnly Cookie △ (requer a configuração SameSite) Recomendado
Memória (variável) Perda de navegação na página
localStorage ✕ (pode ser roubado via XSS) Não recomendado
sessionStorage ✕ (pode ser roubado via XSS) Uso limitado
Recomendado: HttpOnly + Seguro + Cookie SameSite

Armazenar o token de acesso em um cookie com atributos HttpOnly, Secure e SameSite=Strict é a abordagem mais segura. Isso evita o roubo de tokens por meio de ataques XSS.

🗝 Gerenciamento de chaves de API

As chaves de API são um mecanismo de autenticação simples, mas o gerenciamento inadequado pode levar a sérios riscos.

Práticas recomendadas de chave de API

Geração e armazenamento

Gerar com entropia suficiente (256 bits ou mais). Armazene com hash; nunca mantenha texto simples no banco de dados.

Restrição de escopo

Atribua escopos mínimos por chave (somente leitura, somente recursos específicos, etc.). Evite permissões curinga.

Rotação

Faça a rotação das chaves regularmente (dentro de 90 dias). Defina um período de carência para chaves antigas. Revogue imediatamente em caso de vazamento.

Método de transmissão

Envie via cabeçalho (X-API-Key ou Authorization). Não inclua nos parâmetros de consulta de URL (eles aparecem nos registros).

Gerenciamento de variáveis de ambiente

Não codifique as chaves no código-fonte. Gerencie-as com variáveis de ambiente ou com um gerenciador de segredos.

Monitoramento e registro

Monitorar o uso da chave da API. Detecte padrões anômalos (solicitações em massa, acesso de IPs desconhecidos).

Exemplo de implementação de validação de chave de API

JavaScript (Express) Validação de chave de API com hash
const crypto = require('crypto');

// Faça o hash da chave da API com SHA-256 e compare
async function validateApiKey(req, res, next) {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) {
    return res.status(401).json({ error: 'API key required' });
  }

  // Hash e consulta no banco de dados (timingSafeEqual não é necessário, pois comparamos hashes)
  const hashedKey = crypto
    .createHash('sha256')
    .update(apiKey)
    .digest('hex');

  const keyRecord = await db.findApiKey(hashedKey);

  if (!keyRecord || keyRecord.revokedAt) {
    return res.status(403).json({ error: 'Invalid API key' });
  }

  // Verificar expiração
  if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt) {
    return res.status(403).json({ error: 'API key expired' });
  }

  req.apiClient = keyRecord;
  next();
}

🤖 Securing LLM & AI Agent API Access

AI agents and LLM-powered applications require additional authentication and authorization patterns beyond traditional API security.

Model-Specific Scopes

Assign different access levels per model tier (e.g., GPT-4 requires elevated scope). Prevent unauthorized use of expensive or sensitive models.

Token Budget Enforcement

Embed token budgets in access tokens or API key metadata. Enforce per-request and per-day limits to prevent denial-of-wallet attacks.

Prompt-Level Authorization

Check user permissions before executing certain prompt types (e.g., code generation, data analysis). Map prompt categories to role-based permissions.

Multi-Tenant Isolation

Ensure conversation history, fine-tuned models, and RAG data are isolated per tenant. Use tenant-scoped API keys with namespace enforcement.

Agent Identity & Attestation

Assign cryptographic identities to AI agents. Use signed JWTs for inter-agent communication. Verify agent identity before granting tool access.

Credential Rotation for AI Services

Automate API key rotation for LLM providers. Use short-lived tokens for agent sessions. Revoke credentials immediately when agents are decommissioned.

Agent Tool Access Authorization

Scenario Auth Method Required Scope Approval
Agent reads public data API Key data:read Automatic
Agent modifies user data OAuth 2.0 (delegated) data:write User consent required
Agent calls external API OAuth 2.0 + mTLS external:invoke Human-in-the-loop
Agent executes code / shell Scoped JWT + Sandbox exec:sandbox Admin approval + audit log

Scoped OAuth Token for AI Agents (Python)

Python Agent Token Issuance with Scope Enforcement
import jwt
import time
from datetime import datetime, timedelta

class AgentTokenIssuer:
    """Issues scoped, short-lived tokens for AI agents."""

    ALLOWED_SCOPES = {
        "reader": ["data:read"],
        "writer": ["data:read", "data:write"],
        "executor": ["data:read", "data:write", "exec:sandbox"],
    }

    def __init__(self, secret_key: str):
        self.secret_key = secret_key

    def issue_agent_token(
        self,
        agent_id: str,
        role: str,
        tenant_id: str,
        token_budget: int = 10000,
        ttl_minutes: int = 15,
    ) -> str:
        # Validate role and resolve scopes
        if role not in self.ALLOWED_SCOPES:
            raise ValueError(f"Invalid role: {role}")

        payload = {
            "sub": agent_id,
            "tenant": tenant_id,
            "scopes": self.ALLOWED_SCOPES[role],
            "token_budget": token_budget,
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + timedelta(minutes=ttl_minutes),
            "type": "agent",
        }
        return jwt.encode(payload, self.secret_key, algorithm="HS256")

    def verify_tool_access(self, token: str, required_scope: str) -> dict:
        # Decode and verify agent token
        payload = jwt.decode(
            token, self.secret_key, algorithms=["HS256"]
        )
        if required_scope not in payload["scopes"]:
            raise PermissionError(
                f"Agent {payload['sub']} lacks scope: {required_scope}"
            )
        return payload
OWASP References

Related: LLM06: Excessive Agency, ASI01: Excessive Agency, ASI03: Insecure Tool/Function Calling

Comparação de métodos de autenticação

Método Sem estado Caso de uso Considerações sobre segurança
OAuth 2.0 + PKCE APIs que exigem autorização do usuário estado/PKCE necessário, gerenciamento da expiração do token
JWT Bearer Comunicação inter-microsserviços É necessária a especificação do algoritmo, sem dados confidenciais na carga útil
Chave da API Servidor para servidor, integrações externas Rotação e restrição de escopo necessárias
mTLS Comunicação M2M de alta segurança Alto custo operacional para o gerenciamento de certificados
Cookie de sessão Aplicativos da Web tradicionais Necessidade de proteção CSRF, desafios de dimensionamento