Criptografia
Security Module
O módulo de Security centraliza operações críticas de segurança do framework, fornecendo encriptação simétrica para dados sensíveis e gestão de tokens JWT para autenticação entre serviços.
O Que Faz Este Módulo?
O Security Module é a "caixa-forte" do framework. Sempre que precisas de proteger dados que vão ser armazenados (como tokens de terceiros, dados pessoais, ou credenciais de APIs externas), usas o Encrypter. Quando precisas de trocar informação autenticada entre serviços (como provar que um pedido veio realmente do ERP), usas o JwtManager.
Vamos ver cada um em detalhe.
Encrypter (Encriptação Simétrica)
Imagina que tens de guardar o token de acesso à API de um banco na base de dados. Se alguém aceder à base de dados, esse token não pode estar em texto limpo. O Encrypter resolve isto — encripta os dados com AES-256 antes de os guardar, e desencripta quando precisas de os usar.
A encriptação é "simétrica" porque usa a mesma chave (APP_KEY) para encriptar e desencriptar. É rápida e eficiente para dados que só a tua aplicação precisa de ler.
Uso Básico
A Facade Crypt torna isto muito simples. Podes encriptar strings, arrays, ou objectos — o Encrypter serializa tudo automaticamente.
use Og\Modules\Common\Facades\Crypt;
// Encriptar dados simples
$encrypted = Crypt::encrypt('token-secreto-do-banco');
// Encriptar arrays (serializados automaticamente)
$encrypted = Crypt::encrypt([
'access_token' => 'xxx',
'refresh_token' => 'yyy',
'expires_at' => now()->addHours(2)
]);
// Desencriptar
$data = Crypt::decrypt($encrypted);
echo $data['access_token']; // 'xxx'
Quando Usar?
Usa o Encrypter para proteger dados que a tua aplicação precisa de ler mais tarde. Exemplos típicos:
- • Tokens de APIs externas: OAuth tokens, API keys de parceiros.
- • Dados pessoais sensíveis: NIFs, números de conta, dados de saúde (dependendo do contexto legal).
- • Configurações secretas por tenant: Credenciais de email, keys de SMS.
Nota: Para passwords de utilizadores, continua a usar hashing (bcrypt). O Encrypter é para dados que precisas de recuperar, não para validar.
JwtManager (JSON Web Tokens)
Os JWTs são usados para provar identidade entre sistemas. Ao contrário do Encrypter, um JWT não esconde o conteúdo — qualquer pessoa pode ler o payload. O que garante é que o conteúdo não foi alterado e que foi emitido por quem diz ter sido.
No OfficeGest, usamos JWTs principalmente para comunicação com a Autoridade Tributária e outros sistemas externos que exigem assinaturas digitais.
Algoritmos Disponíveis
O algoritmo determina como a assinatura é criada. Os mais comuns:
- HS256 (simétrico): Usa uma "secret" partilhada entre as duas partes. Bom para comunicação interna onde ambos os sistemas são teus.
- RS256 (assimétrico): Usa um par de chaves (privada para assinar, pública para verificar). Essencial quando o verificador não pode conhecer a chave de assinatura — como no caso da AT.
Uso Básico
use Og\Modules\Common\Facades\Jwt;
// Criar um JWT assinado com RS256
$payload = [
'iss' => 'officegest.com', // Quem emitiu
'sub' => $user->getId(), // Sujeito (utilizador)
'iat' => time(), // Issued at
'exp' => time() + 3600, // Expira em 1 hora
'data' => ['nif' => '123456789']
];
$token = Jwt::encode($payload, $privateKey, 'RS256');
// Verificar e decodificar um JWT recebido
try {
$decoded = Jwt::decode($token, $publicKey, 'RS256');
echo $decoded->sub; // ID do utilizador
} catch (ExpiredException $e) {
// Token expirou
} catch (SignatureInvalidException $e) {
// Assinatura inválida — alguém alterou o token
}
Gestão de Chaves
Trabalhar com chaves RSA pode ser frustrante. Ficheiros exportados de diferentes sistemas vêm com formatações diferentes — alguns têm BOMs invisíveis, outros usam \r\n em vez de \n, e por aí fora. O JwtManager actua como um "médico de chaves" que corrige estes problemas automaticamente.
Normalização Automática
Quando passas uma chave ao encode() ou decode(), o JwtManager detecta e corrige automaticamente:
- • BOM (Byte Order Mark): Caracteres invisíveis no início do ficheiro — removidos.
- • Linhas literais: Se a chave tem
\ncomo texto em vez de quebras de linha reais — convertido. - • CRLF do Windows:
\r\nconvertido para\n.
Ofuscação de Chaves
Para alguns cenários (como integração com a AT), precisamos de guardar chaves RSA na base de dados. Em vez de as guardar em texto limpo, podemos ofuscá-las.
// Ofuscar uma chave antes de guardar na BD
$obfuscated = Jwt::encodeRsaKey($privateKey);
// Guardar $obfuscated na base de dados...
// Restaurar quando precisar de usar
$privateKey = Jwt::decodeRsaKey($obfuscated);
$token = Jwt::encode($payload, $privateKey);
Configuração da APP_KEY
A APP_KEY é a chave mestra do Encrypter. Sem ela, nada funciona. Deve ser uma string de 32 bytes codificada em base64, começando com base64:.
Gerar uma Nova Chave
Usa o comando CLI. A chave é gerada e automaticamente guardada no ficheiro de configuração.
docker compose exec app php og key:generate
⚠️ Nunca Partilhes a APP_KEY
A APP_KEY é como a chave mestra de um cofre. Se alguém a obtiver, pode desencriptar todos os dados protegidos. Nunca a comitas em repositórios git, nunca a partilhes em logs ou mensagens. Cada ambiente (local, staging, produção) deve ter a sua própria chave.
Resolução de Problemas
"Unsupported cipher or incorrect key length"
Este erro significa que a APP_KEY está mal formatada. Verifica que:
- • Começa com
base64: - • O conteúdo após
base64:tem exactamente 44 caracteres (que decodificam para 32 bytes) - • Não tem espaços ou quebras de linha no meio
"JWT Signature Verification Failed"
Estás a usar a chave errada para verificar. Lembra-te:
- • encode() usa a chave Privada
- • decode() usa a chave Pública
- • Se usares a chave errada, a verificação falha