FourPlay

Security

Boas práticas de segurança para integrações FourPlay — HMAC, JWT, HTTPS e proteção de credenciais

Modelo de Segurança

A comunicação FourPlay usa HMAC-SHA256 em todas as requisições — tanto do operador para a FourPlay quanto nos callbacks da FourPlay para o operador. Não existe autenticação por Bearer token ou API Key isolada.

AspectoMecanismo
AutenticaçãoHMAC-SHA256 por requisição
Proteção anti-replayJanela de timestamp ±5 minutos
Sessão de jogadorJWT HS256 com expiração de 24 horas
TransporteHTTPS obrigatório em produção

Proteção de Credenciais

Nunca exponha a Secret Key

A Secret Key (sk_live_...) deve existir somente no servidor backend. Ela não deve aparecer em:

  • Código-fonte versionado (git)
  • Frontend / JavaScript do browser
  • Logs de aplicação
  • Variáveis de ambiente do cliente

Uso correto:

# .env (nunca versionar)
FOURPLAY_API_KEY=op_live_...
FOURPLAY_SECRET_KEY=sk_live_...
// server.js (backend apenas)
const SECRET_KEY = process.env.FOURPLAY_SECRET_KEY;
// Nunca enviar SECRET_KEY em resposta HTTP ou log

Rotação de Chaves

Rotacione as chaves se:

  • Suspeitar de vazamento
  • Membro da equipe com acesso sair
  • Repositório privado se tornar público acidentalmente

Após rotacionar no Portal Admin (https://admin.fourplay.studio), atualize as variáveis de ambiente e faça deploy imediatamente.


Validação de Callbacks

Todo callback recebido da FourPlay deve ter sua assinatura validada antes de qualquer processamento.

Algoritmo:

  1. Extraia X-Operator-Id, X-Timestamp, X-Signature dos headers
  2. Verifique: |agora_ms - X-Timestamp| <= 300000 (5 minutos)
  3. Reconstrua: data = X-Operator-Id + X-Timestamp + rawBody
  4. Compare: HMAC-SHA256(secretKey, data) == X-Signature
  5. Use comparação em tempo constante (timingSafeEqual, hash_equals, etc.)

Use sempre comparação em tempo constante para evitar timing attacks. === em strings é insuficiente.

// Correto: timingSafeEqual
crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(received, 'hex'));

// Errado: comparação simples
expected === received // vulnerável a timing attack

Proteção anti-Replay (Timestamp)

A FourPlay rejeita qualquer requisição com timestamp fora da janela de ±5 minutos. Isso impede que requisições interceptadas sejam reutilizadas por um atacante.

Mantenha o relógio do servidor sincronizado:

# Verificar sincronização NTP
timedatectl status

# Ativar sincronização automática (Linux/systemd)
sudo timedatectl set-ntp true

# Checar drift do relógio
ntpq -p

Se seu servidor tiver drift de relógio superior a 5 minutos, todas as requisições serão rejeitadas com erro de timestamp expirado.


HTTPS e TLS

Servidor de Callbacks

Em produção, seu servidor de callbacks deve usar TLS 1.2 ou superior. HTTP simples será rejeitado pela FourPlay em produção.

Verificar certificado:

# Checar TLS do seu endpoint
openssl s_client -connect api.seusite.com.br:443 -tls1_2

# Verificar validade do certificado
echo | openssl s_client -connect api.seusite.com.br:443 2>/dev/null | openssl x509 -noout -dates

Cadeia de certificados: certifique-se de que o certificado inclui a cadeia intermediária completa. Certificados auto-assinados não serão aceitos.


JWT: Token de Sessão

O token JWT retornado pelo /session/create contém os seguintes claims:

{
  "playerId":   "uuid-interno-mines",
  "operatorId": "uuid-interno-mines",
  "username":   "João Silva",
  "isDemo":     false,
  "iat":        1719532800,
  "exp":        1719619200
}

Boas práticas:

  • O token é opaco para o jogador — não contém saldo ou dados financeiros
  • Expiração de 24 horas após emissão
  • Não armazene o token em localStorage — prefira cookies HttpOnly ou sessão server-side
  • Não decodifique para autorização sem verificar a assinatura (use o endpoint /validate para isso)

Idempotência nas Transações

Implemente idempotência usando gameId + type como chave única:

// Correto: verificar antes de processar
const existing = await db.transactions.findOne({ gameId, type: 'DEBIT' });
if (existing) return { balance: currentBalance, transactionId: existing.id };

// Errado: processar sem verificar
await debitPlayer(amount); // pode resultar em double-charge

Por que isso é segurança, não apenas qualidade:

  • Retry automático da FourPlay pode acionar o mesmo callback múltiplas vezes
  • Sem idempotência, o jogador pode ser debitado mais de uma vez pela mesma aposta

Proteção do Servidor de Callbacks

Aceitar apenas IPs da FourPlay

Você pode restringir seu servidor de callbacks para aceitar requisições apenas dos IPs do FourPlay Mines. Consulte a equipe FourPlay pelo e-mail suporte@fourplay.studio para obter a lista de IPs.

Validar Content-Type

Rejeite requisições que não sejam application/json:

app.use('/wallet', (req, res, next) => {
  if (!req.headers['content-type']?.includes('application/json')) {
    return res.status(415).json({ error: 'Unsupported Media Type' });
  }
  next();
});

Logs sem dados sensíveis

Nunca logue:

  • X-Signature (pode ser usada para replay se combinada com timestamp válido)
  • Valores de saldo completos em sistemas de log externos
  • Secret Key em qualquer situação

Checklist de Segurança para Go-Live

  • Secret Key armazenada apenas em variáveis de ambiente do servidor
  • Secret Key ausente do repositório git (verificar histórico com git log -S "sk_live_")
  • Validação HMAC implementada em todos os 4 endpoints de callback
  • Comparação em tempo constante sendo usada
  • Janela de timestamp validada (±5 minutos)
  • HTTPS com TLS 1.2+ no servidor de callbacks
  • Certificado SSL válido e com cadeia intermediária
  • Idempotência implementada nos endpoints de debit, credit e rollback
  • Logs não expõem Secret Key nem assinaturas
  • Servidor de callbacks responde em menos de 5 segundos

On this page