🔒 Segurança
Este guia cobre exclusivamente aspectos de segurança na integração com APIs da Credsystem. Para implementação técnica, veja o Passo 4: Usando o Token.
NUNCA exponha suas credenciais (client_id e client_secret) em:
- ❌ Código client-side (frontend, aplicativos móveis)
- ❌ Repositórios públicos (GitHub, GitLab, etc.)
- ❌ Logs ou mensagens de erro
- ❌ URLs ou query parameters
- ❌ Código JavaScript no navegador
- ❌ Apps móveis (podem ser decompilados)
- ❌ Emails, mensagens ou documentação pública
✅ Armazene credenciais APENAS em servidores backend seguros com acesso restrito
🔐 1. Armazenamento Seguro de Credenciais
Variáveis de Ambiente
✅ Configuração Recomendada:
# .env (NUNCA commitar este arquivo)
CREDSYSTEM_CLIENT_ID=seu_client_id_aqui
CREDSYSTEM_CLIENT_SECRET=seu_client_secret_aqui
CREDSYSTEM_AUTH_URL=https://ssoprd.credsystem.com.br/auth/realms/seu-realm/protocol/openid-connect/token
CREDSYSTEM_API_URL=https://api.credsystem.com.br
Adicione ao .gitignore:
.env
.env.local
.env.*.local
*.secret
*.key
Uso no código:
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// Node.js
require('dotenv').config();
const clientId = process.env.CREDSYSTEM_CLIENT_ID;
const clientSecret = process.env.CREDSYSTEM_CLIENT_SECRET;
const authUrl = process.env.CREDSYSTEM_AUTH_URL;
# Python
import os
from dotenv import load_dotenv
load_dotenv()
client_id = os.getenv('CREDSYSTEM_CLIENT_ID')
client_secret = os.getenv('CREDSYSTEM_CLIENT_SECRET')
auth_url = os.getenv('CREDSYSTEM_AUTH_URL')
// Java
public class Config {
private static final String CLIENT_ID = System.getenv("CREDSYSTEM_CLIENT_ID");
private static final String CLIENT_SECRET = System.getenv("CREDSYSTEM_CLIENT_SECRET");
private static final String AUTH_URL = System.getenv("CREDSYSTEM_AUTH_URL");
}
// C#
using System;
public class Config
{
public static string ClientId = Environment.GetEnvironmentVariable("CREDSYSTEM_CLIENT_ID");
public static string ClientSecret = Environment.GetEnvironmentVariable("CREDSYSTEM_CLIENT_SECRET");
public static string AuthUrl = Environment.GetEnvironmentVariable("CREDSYSTEM_AUTH_URL");
}
<?php
// PHP
$clientId = getenv('CREDSYSTEM_CLIENT_ID');
$clientSecret = getenv('CREDSYSTEM_CLIENT_SECRET');
$authUrl = getenv('CREDSYSTEM_AUTH_URL');
// Ou usando $_ENV (se variables_order configurado)
$clientId = $_ENV['CREDSYSTEM_CLIENT_ID'];
?>
package main
import (
"os"
"github.com/joho/godotenv"
)
func init() {
godotenv.Load()
}
var (
ClientID = os.Getenv("CREDSYSTEM_CLIENT_ID")
ClientSecret = os.Getenv("CREDSYSTEM_CLIENT_SECRET")
AuthURL = os.Getenv("CREDSYSTEM_AUTH_URL")
)
Gerenciadores de Secrets (Produção)
Para ambientes de produção, use serviços especializados:
- AWS Secrets Manager - Para infraestrutura AWS
- Azure Key Vault - Para infraestrutura Azure
- Google Secret Manager - Para infraestrutura GCP
- HashiCorp Vault - Multi-cloud e on-premise
- Kubernetes Secrets - Para ambientes containerizados
Benefícios:
- 🔄 Rotação automática de credenciais
- 📊 Auditoria completa de acessos
- 🔐 Criptografia em repouso e em trânsito
- 🎯 Controle de acesso granular (IAM)
- 📜 Versionamento de secrets
🎯 2. Gerenciamento de Tokens
-
Cache de token válido (não gerar novo a cada requisição)
# ✅ Reutilizar token enquanto válidoif token_expiry > datetime.now():return cached_token -
Renovação automática antes de expirar
// ✅ Renovar 30s antes de expirarif (Date.now() > tokenExpiry - 30000) {await refreshToken();} -
NUNCA armazenar token no frontend
// ❌ ERRADOlocalStorage.setItem('token', accessToken);sessionStorage.setItem('token', accessToken);// ✅ CORRETO - Token fica apenas no backend// Frontend recebe apenas dados autorizados -
Sempre usar campo
expires_inda resposta// ✅ CORRETO - Usar expires_in dinâmicoconst tokenExpiry = Date.now() + (data.expires_in * 1000);// ❌ ERRADO - Assumir valor fixoconst tokenExpiry = Date.now() + (300 * 1000); // Pode variar!
🛡️ 3. Proteção de Tokens em Uso
❌ Vulnerabilidades Comuns
1. Token em LocalStorage/SessionStorage
// ❌ NUNCA FAÇA ISSO - Vulnerável a XSS
localStorage.setItem('access_token', token);
sessionStorage.setItem('access_token', token);
// ✅ Token deve ficar apenas no backend
// Frontend nunca deve ter acesso ao token
2. Token em Cookies sem Segurança
// ❌ Cookie sem flags de segurança
document.cookie = 'token=' + token;
// ✅ Se usar cookies (HttpOnly + Secure + SameSite)
res.cookie('session', sessionId, {
httpOnly: true, // Não acessível via JavaScript
secure: true, // Apenas HTTPS
sameSite: 'strict' // Proteção CSRF
});
3. Token em URLs
// ❌ Token exposto em URLs
fetch(`/api/data?token=${token}`);
// ✅ Token em header Authorization
fetch('/api/data', {
headers: { 'Authorization': `Bearer ${token}` }
});
🔒 Proteções Essenciais
- ✅ HTTPS obrigatório em todas as conexões
- ✅ Token apenas no backend server-side
- ✅ Headers HTTP corretos (Authorization: Bearer)
- ✅ Timeout de requisições (evita tokens pendentes)
- ✅ Validação de domínio nas chamadas de API
🌐 4. HTTPS/TLS Obrigatório
NUNCA faça requisições de autenticação ou API por HTTP não criptografado.
Configuração Mínima
✅ TLS 1.2 ou superior
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// Node.js - Forçar TLS 1.2+
const https = require('https');
const agent = new https.Agent({
minVersion: 'TLSv1.2'
});
// Usar em requisições
fetch(url, { agent });
# Python - Forçar TLS 1.2+
import ssl
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
class TLSAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
ctx = create_urllib3_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
kwargs['ssl_context'] = ctx
return super().init_poolmanager(*args, **kwargs)
session = requests.Session()
session.mount('https://', TLSAdapter())
// Java - Forçar TLS 1.2+
import javax.net.ssl.SSLContext;
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
// C# - Forçar TLS 1.2+
using System.Net;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
<?php
// PHP - Forçar TLS 1.2+
$context = stream_context_create([
'ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
]
]);
// Usar com file_get_contents
$result = file_get_contents($url, false, $context);
// Ou com cURL
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
curl_exec($ch);
?>
package main
import (
"crypto/tls"
"net/http"
)
// Go - Forçar TLS 1.2+
func main() {
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
}
transport := &http.Transport{
TLSClientConfig: tlsConfig,
}
client := &http.Client{
Transport: transport,
}
// Usar client para requisições
resp, err := client.Get(url)
}
✅ Validação de certificados
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// Node.js - Validar certificados
const https = require('https');
// ❌ ERRADO - Nunca desabilitar
const agent = new https.Agent({
rejectUnauthorized: false
});
// ✅ CORRETO - Sempre validar
const agent = new https.Agent({
rejectUnauthorized: true
});
# Python - NUNCA desabilite verificação SSL
import requests
# ❌ ERRADO
response = requests.get(url, verify=False)
# ✅ CORRETO
response = requests.get(url, verify=True)
// Java - Certificate Pinning (opcional, alta segurança)
import okhttp3.CertificatePinner;
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("api.credsystem.com.br", "sha256/HASH_DO_CERTIFICADO")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
// C# - Validar certificados
using System.Net.Http;
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator // ❌ NUNCA fazer isso
};
// ✅ CORRETO - usar validação padrão
var client = new HttpClient(); // Validará automaticamente
<?php
// PHP - Validar certificados
// ❌ ERRADO - Nunca desabilitar
$context = stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
// ✅ CORRETO - Sempre validar (padrão)
$result = file_get_contents($url); // Valida automaticamente
// Ou com cURL
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // ✅ CORRETO
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // ✅ CORRETO
curl_exec($ch);
?>
package main
import (
"crypto/tls"
"net/http"
)
// Go - Validar certificados
func main() {
// ❌ ERRADO - Nunca desabilitar
tlsConfig := &tls.Config{
InsecureSkipVerify: true, // ❌ NUNCA fazer isso
}
// ✅ CORRETO - Validação padrão
client := &http.Client{} // Valida automaticamente
// Ou explicitamente
tlsConfig := &tls.Config{
InsecureSkipVerify: false, // ✅ CORRETO
}
transport := &http.Transport{
TLSClientConfig: tlsConfig,
}
client := &http.Client{
Transport: transport,
}
}
🔄 5. Rotação de Credenciais
Rotação Emergencial:
- 🚨 Suspeita de vazamento
- 🚨 Funcionário com acesso saiu da empresa
- 🚨 Sistema comprometido
- 🚨 Credenciais expostas em logs/código
- 🚨 Solicitação da equipe de segurança
Processo de Rotação
1. Solicitar novas credenciais à Credsystem (mesma forma do Passo 1)
2. Implementar estratégia dual:
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// Manter ambas credenciais temporariamente
const PRIMARY_CLIENT_ID = process.env.CREDSYSTEM_CLIENT_ID;
const PRIMARY_SECRET = process.env.CREDSYSTEM_CLIENT_SECRET;
const SECONDARY_CLIENT_ID = process.env.CREDSYSTEM_CLIENT_ID_NEW;
const SECONDARY_SECRET = process.env.CREDSYSTEM_CLIENT_SECRET_NEW;
// Tentar primária, fallback para secundária
async function authenticate() {
try {
return await authWithCredentials(PRIMARY_CLIENT_ID, PRIMARY_SECRET);
} catch (error) {
console.log('Tentando credenciais secundárias...');
return await authWithCredentials(SECONDARY_CLIENT_ID, SECONDARY_SECRET);
}
}
import os
# Manter ambas credenciais temporariamente
PRIMARY_CLIENT_ID = os.getenv('CREDSYSTEM_CLIENT_ID')
PRIMARY_SECRET = os.getenv('CREDSYSTEM_CLIENT_SECRET')
SECONDARY_CLIENT_ID = os.getenv('CREDSYSTEM_CLIENT_ID_NEW')
SECONDARY_SECRET = os.getenv('CREDSYSTEM_CLIENT_SECRET_NEW')
def authenticate():
try:
return auth_with_credentials(PRIMARY_CLIENT_ID, PRIMARY_SECRET)
except Exception as error:
print('Tentando credenciais secundárias...')
return auth_with_credentials(SECONDARY_CLIENT_ID, SECONDARY_SECRET)
// Java - Estratégia dual
public class DualCredentialsAuth {
private static final String PRIMARY_CLIENT_ID = System.getenv("CREDSYSTEM_CLIENT_ID");
private static final String PRIMARY_SECRET = System.getenv("CREDSYSTEM_CLIENT_SECRET");
private static final String SECONDARY_CLIENT_ID = System.getenv("CREDSYSTEM_CLIENT_ID_NEW");
private static final String SECONDARY_SECRET = System.getenv("CREDSYSTEM_CLIENT_SECRET_NEW");
public static String authenticate() {
try {
return authWithCredentials(PRIMARY_CLIENT_ID, PRIMARY_SECRET);
} catch (Exception e) {
System.out.println("Tentando credenciais secundárias...");
return authWithCredentials(SECONDARY_CLIENT_ID, SECONDARY_SECRET);
}
}
}
// C# - Estratégia dual
public class DualCredentialsAuth
{
private static readonly string PrimaryClientId = Environment.GetEnvironmentVariable("CREDSYSTEM_CLIENT_ID");
private static readonly string PrimarySecret = Environment.GetEnvironmentVariable("CREDSYSTEM_CLIENT_SECRET");
private static readonly string SecondaryClientId = Environment.GetEnvironmentVariable("CREDSYSTEM_CLIENT_ID_NEW");
private static readonly string SecondarySecret = Environment.GetEnvironmentVariable("CREDSYSTEM_CLIENT_SECRET_NEW");
public static async Task<string> Authenticate()
{
try
{
return await AuthWithCredentials(PrimaryClientId, PrimarySecret);
}
catch (Exception)
{
Console.WriteLine("Tentando credenciais secundárias...");
return await AuthWithCredentials(SecondaryClientId, SecondarySecret);
}
}
}
<?php
// PHP - Estratégia dual
class DualCredentialsAuth
{
private $primaryClientId;
private $primarySecret;
private $secondaryClientId;
private $secondarySecret;
public function __construct()
{
$this->primaryClientId = getenv('CREDSYSTEM_CLIENT_ID');
$this->primarySecret = getenv('CREDSYSTEM_CLIENT_SECRET');
$this->secondaryClientId = getenv('CREDSYSTEM_CLIENT_ID_NEW');
$this->secondarySecret = getenv('CREDSYSTEM_CLIENT_SECRET_NEW');
}
public function authenticate()
{
try {
return $this->authWithCredentials(
$this->primaryClientId,
$this->primarySecret
);
} catch (Exception $e) {
echo "Tentando credenciais secundárias...\n";
return $this->authWithCredentials(
$this->secondaryClientId,
$this->secondarySecret
);
}
}
}
?>
package main
import (
"fmt"
"os"
)
// Go - Estratégia dual
type DualCredentialsAuth struct {
PrimaryClientID string
PrimarySecret string
SecondaryClientID string
SecondarySecret string
}
func NewDualCredentialsAuth() *DualCredentialsAuth {
return &DualCredentialsAuth{
PrimaryClientID: os.Getenv("CREDSYSTEM_CLIENT_ID"),
PrimarySecret: os.Getenv("CREDSYSTEM_CLIENT_SECRET"),
SecondaryClientID: os.Getenv("CREDSYSTEM_CLIENT_ID_NEW"),
SecondarySecret: os.Getenv("CREDSYSTEM_CLIENT_SECRET_NEW"),
}
}
func (auth *DualCredentialsAuth) Authenticate() (string, error) {
token, err := authWithCredentials(
auth.PrimaryClientID,
auth.PrimarySecret,
)
if err != nil {
fmt.Println("Tentando credenciais secundárias...")
return authWithCredentials(
auth.SecondaryClientID,
auth.SecondarySecret,
)
}
return token, nil
}
3. Testar novas credenciais em ambiente de staging
4. Deploy gradual (canary/blue-green)
5. Revogar credenciais antigas após confirmação
🚦 6. Rate Limiting e Resiliência
Rate limiting, retry com backoff exponencial e circuit breaker são padrões operacionais detalhados no guia de Boas Práticas:
Do ponto de vista de segurança, atente-se a:
- ✅ Nunca desabilite rate limiting — protege contra abuso e ataques de força bruta
- ✅ Respeite o header
Retry-After— retornado pela API quando o limite é atingido (429) - ✅ Implemente jitter no retry — evita thundering herd (múltiplos clientes retentando simultaneamente)
- ✅ Limite tentativas de autenticação — máximo 3-5 retries antes de gerar alerta
- ✅ Monitore rate limit hits — picos podem indicar ataque ou mal uso
📊 7. Auditoria e Logging
O que Logar (✅)
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// ✅ Informações permitidas
logger.info('Autenticação iniciada', {
timestamp: new Date().toISOString(),
client_id: clientId, // OK - não é secreto
environment: 'production',
ip_address: req.ip,
user_agent: req.headers['user-agent']
});
logger.info('Token obtido com sucesso', {
token_prefix: token.substring(0, 10) + '...', // Apenas início
expires_in: expiresIn,
token_type: 'Bearer'
});
logger.error('Falha na autenticação', {
status_code: response.status,
error_type: 'invalid_credentials',
// NUNCA logar client_secret ou token completo
});
import logging
from datetime import datetime
# ✅ Informações permitidas
logging.info('Autenticação iniciada', extra={
'timestamp': datetime.utcnow().isoformat(),
'client_id': client_id, # OK - não é secreto
'environment': 'production',
'ip_address': request.remote_addr,
'user_agent': request.headers.get('User-Agent')
})
logging.info('Token obtido com sucesso', extra={
'token_prefix': token[:10] + '...', # Apenas início
'expires_in': expires_in,
'token_type': 'Bearer'
})
logging.error('Falha na autenticação', extra={
'status_code': response.status_code,
'error_type': 'invalid_credentials'
# NUNCA logar client_secret ou token completo
})
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// ✅ Informações permitidas
Logger logger = LoggerFactory.getLogger(CredsystemAuth.class);
logger.info("Autenticação iniciada - clientId: {}, env: {}, ip: {}",
clientId, // OK - não é secreto
environment,
request.getRemoteAddr()
);
logger.info("Token obtido com sucesso - prefix: {}..., expiresIn: {}",
token.substring(0, 10), // Apenas início
expiresIn
);
logger.error("Falha na autenticação - status: {}, tipo: {}",
response.statusCode(),
"invalid_credentials"
// NUNCA logar clientSecret ou token completo
);
using Microsoft.Extensions.Logging;
// ✅ Informações permitidas
_logger.LogInformation(
"Autenticação iniciada - ClientId: {ClientId}, Env: {Environment}, IP: {IP}",
clientId, // OK - não é secreto
environment,
httpContext.Connection.RemoteIpAddress
);
_logger.LogInformation(
"Token obtido com sucesso - Prefix: {TokenPrefix}, ExpiresIn: {ExpiresIn}",
token.Substring(0, 10) + "...", // Apenas início
expiresIn
);
_logger.LogError(
"Falha na autenticação - Status: {Status}, Tipo: {ErrorType}",
response.StatusCode,
"invalid_credentials"
// NUNCA logar ClientSecret ou token completo
);
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logger = new Logger('credsystem');
$logger->pushHandler(new StreamHandler('app.log', Logger::INFO));
// ✅ Informações permitidas
$logger->info('Autenticação iniciada', [
'timestamp' => date('c'),
'client_id' => $clientId, // OK - não é secreto
'environment' => 'production',
'ip_address' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT']
]);
$logger->info('Token obtido com sucesso', [
'token_prefix' => substr($token, 0, 10) . '...', // Apenas início
'expires_in' => $expiresIn,
'token_type' => 'Bearer'
]);
$logger->error('Falha na autenticação', [
'status_code' => $response->getStatusCode(),
'error_type' => 'invalid_credentials'
// NUNCA logar client_secret ou token completo
]);
?>
package main
import (
"log"
"time"
)
// ✅ Informações permitidas
type AuthLog struct {
Timestamp string
ClientID string // OK - não é secreto
Environment string
IPAddress string
UserAgent string
}
func logAuthStart(clientID, ip, userAgent string) {
log.Printf("Autenticação iniciada: %+v", AuthLog{
Timestamp: time.Now().Format(time.RFC3339),
ClientID: clientID,
Environment: "production",
IPAddress: ip,
UserAgent: userAgent,
})
}
func logTokenSuccess(token string, expiresIn int) {
log.Printf("Token obtido com sucesso - Prefix: %s..., ExpiresIn: %d",
token[:10], // Apenas início
expiresIn,
)
}
func logAuthFailure(statusCode int) {
log.Printf("Falha na autenticação - Status: %d, Tipo: invalid_credentials",
statusCode,
// NUNCA logar clientSecret ou token completo
)
}
O que NÃO Logar (❌)
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// ❌ NUNCA logue informações sensíveis
logger.error('Erro:', {
client_secret: clientSecret, // ❌ NUNCA
access_token: fullToken, // ❌ NUNCA
refresh_token: refreshToken, // ❌ NUNCA
password: userPassword // ❌ NUNCA
});
# ❌ NUNCA logue informações sensíveis
logging.error('Erro', extra={
'client_secret': client_secret, # ❌ NUNCA
'access_token': full_token, # ❌ NUNCA
'refresh_token': refresh_token, # ❌ NUNCA
'password': user_password # ❌ NUNCA
})
// ❌ NUNCA logue informações sensíveis
logger.error("Erro - secret: {}, token: {}",
clientSecret, // ❌ NUNCA
fullToken // ❌ NUNCA
);
// ❌ NUNCA logue informações sensíveis
_logger.LogError(
"Erro - Secret: {Secret}, Token: {Token}",
clientSecret, // ❌ NUNCA
fullToken // ❌ NUNCA
);
<?php
// ❌ NUNCA logue informações sensíveis
$logger->error('Erro', [
'client_secret' => $clientSecret, // ❌ NUNCA
'access_token' => $fullToken, // ❌ NUNCA
'refresh_token' => $refreshToken, // ❌ NUNCA
'password' => $userPassword // ❌ NUNCA
]);
?>
// ❌ NUNCA logue informações sensíveis
log.Printf("Erro - Secret: %s, Token: %s",
clientSecret, // ❌ NUNCA
fullToken, // ❌ NUNCA
)
Monitoramento de Segurança
Alertas Recomendados:
- 🚨 Múltiplas falhas de autenticação (possível ataque de força bruta)
- 🚨 Uso de credenciais de IPs não esperados
- 🚨 Picos anormais de requisições
- 🚨 Erros 401/403 acima do normal
- 🚨 Tentativas de acesso fora do horário comercial (se aplicável)
👉 Ver implementação de métricas e logging estruturado em 6 linguagens
🔬 8. Separação de Credenciais por Ambiente
Cada ambiente DEVE ter credenciais separadas. Nunca reutilize credenciais de produção em desenvolvimento ou staging.
# Desenvolvimento → Usa SSO de homologação
CREDSYSTEM_AUTH_URL=https://ssohml.credsystem.com.br/...
# Staging → Usa SSO de homologação (credenciais diferentes)
CREDSYSTEM_AUTH_URL=https://ssohml.credsystem.com.br/...
# Produção → Usa SSO de produção
CREDSYSTEM_AUTH_URL=https://ssoprd.credsystem.com.br/...
Benefícios:
- 🔒 Vazamento em dev não compromete produção
- 🧪 Testes seguros sem risco
- 🚨 Revogação de um ambiente não afeta outros
- 📊 Auditoria separada por ambiente
👉 Ver configuração por ambiente em 6 linguagens — Inclui padrões de config com timeout, log level e SSL por ambiente.
💡 Arquitetura Recomendada
✅ Arquitetura Correta
┌─────────────────┐
│ Frontend │ ← NUNCA fazer auth aqui
│ (Browser) │ ← Não tem credenciais
└────────┬────────┘ ← Não tem tokens
│ HTTPS
▼
┌─────────────────┐
│ Backend/API │ ← ✅ Autenticação aqui
│ Gateway │ ← ✅ Guarda credenciais
└────────┬────────┘ ← ✅ Cache do token
│
▼
┌─────────────────┐
│ APIs │
│ Credsystem │
└─────────────────┘
❌ Arquitetura Insegura (NÃO FAÇA)
┌─────────────────┐
│ Frontend │ ← ❌ Credenciais expostas
│ (Browser) │ ← ❌ Token pode ser roubado
│ │ ← ❌ Fácil de inspecionar
└────────┬────────┘
│ DIRETO
▼
┌─────────────────┐
│ APIs │ ← Cliente desprotegido
│ Credsystem │
└─────────────────┘
🔄 Fluxo de Autenticação Correto
Assim:
1. Frontend → Backend: "Preciso listar vendas"
2. Backend: Verifica se tem token válido em cache
3. Backend: Se não tem, gera novo token com suas credenciais
4. Backend → Credsystem API: GET /vendas + Authorization: Bearer {token}
5. Credsystem API → Backend: Response com dados
6. Backend → Frontend: Response (SEM expor token)
Benefícios:
- ✅ Credenciais protegidas no backend
- ✅ Token não exposto ao usuário final
- ✅ Cache centralizado (performance)
- ✅ Fácil rotação de credenciais
- ✅ Logs seguros no backend
⏱️ Validade dos Tokens
IMPORTANTE: A validade dos tokens pode variar dependendo de:
- Configurações específicas da sua integração
- Políticas de segurança do ambiente (homologação vs produção)
- Tipo de autenticação utilizado (Keycloak vs IDCS)
✅ SEMPRE use o campo expires_in retornado na resposta de autenticação. NUNCA assuma valores fixos no seu código.
- JavaScript
- Python
- Java
- C#
- PHP
- Go
// ✅ CORRETO - Usar expires_in da resposta
const tokenExpiry = Date.now() + (data.expires_in * 1000);
// ❌ ERRADO - Assumir valor fixo
const tokenExpiry = Date.now() + (300 * 1000); // Pode ser diferente!
from datetime import datetime, timedelta
# ✅ CORRETO - Usar expires_in da resposta
token_expiry = datetime.now() + timedelta(seconds=data['expires_in'])
# ❌ ERRADO - Assumir valor fixo
token_expiry = datetime.now() + timedelta(seconds=300) # Pode ser diferente!
import java.time.Instant;
// ✅ CORRETO - Usar expires_in da resposta
Instant tokenExpiry = Instant.now().plusSeconds(data.getExpiresIn());
// ❌ ERRADO - Assumir valor fixo
Instant tokenExpiry = Instant.now().plusSeconds(300); // Pode ser diferente!
using System;
// ✅ CORRETO - Usar expires_in da resposta
var tokenExpiry = DateTime.UtcNow.AddSeconds(data.ExpiresIn);
// ❌ ERRADO - Assumir valor fixo
var tokenExpiry = DateTime.UtcNow.AddSeconds(300); // Pode ser diferente!
<?php
// ✅ CORRETO - Usar expires_in da resposta
$tokenExpiry = time() + $data['expires_in'];
// ❌ ERRADO - Assumir valor fixo
$tokenExpiry = time() + 300; // Pode ser diferente!
?>
import "time"
// ✅ CORRETO - Usar expires_in da resposta
tokenExpiry := time.Now().Add(time.Duration(data.ExpiresIn) * time.Second)
// ❌ ERRADO - Assumir valor fixo
tokenExpiry := time.Now().Add(300 * time.Second) // Pode ser diferente!
✅ Checklist de Segurança
Antes de colocar em produção, verifique todos os itens:
🔐 Credenciais
- Credenciais armazenadas em variáveis de ambiente (não em código)
- Arquivo
.envno.gitignore - Produção usa gerenciador de secrets (AWS, Azure, etc.)
- Credenciais diferentes para cada ambiente (dev, staging, prod)
- Acesso às credenciais restrito (apenas quem precisa)
- Plano de rotação de credenciais definido (90-180 dias)
🔒 Tokens
- Token NUNCA exposto no frontend/navegador
- Token não armazenado em localStorage/sessionStorage
- Token enviado apenas em header Authorization
- Logs não expõem tokens completos (apenas 4 últimos caracteres)
- Token em cache com expiração respeitada
🌐 Comunicação
- HTTPS obrigatório em todas as conexões
- TLS 1.2+ configurado
- Certificados SSL validados (verify=True)
- Timeout configurado para requisições
- Rate limiting implementado (proteção contra abuso)
📊 Monitoramento
- Alertas para múltiplas falhas de autenticação
- Logs de auditoria com timestamp e IP
- Métricas de taxa de sucesso/falha
- Monitoramento de rate limit hits
- Alertas para acessos de IPs inesperados
🧪 Testes de Segurança
- Testado comportamento com credenciais inválidas
- Testado rotação de credenciais em staging
- Verificado que frontend não tem acesso a tokens
- Code review focado em segurança
- Scan de vulnerabilidades no código
📜 Compliance
- Documentação de segurança atualizada
- Equipe treinada em boas práticas
- Processo de incident response definido
- Conformidade com LGPD/GDPR se aplicável
🎯 Recursos Adicionais
Para mais informações sobre segurança:
👉 Boas Práticas Gerais - Guia completo de melhores práticas
👉 Troubleshooting - Problemas comuns e soluções
Ou volte para o Índice de Autenticação