Segurança e Criptografia

BackupAlly implementa criptografia de nível enterprise para proteção de dados.

Modelo de Criptografia

Envelope Encryption

BackupAlly usa envelope encryption (criptografia em envelope):

┌──────────────────────────────────────────────┐
│              Backup File (.ally)             │
├──────────────────────────────────────────────┤
│  ┌────────────────────────────────────────┐  │
│  │  RSA-4096 encrypted AES-256 key        │  │
│  └────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────┐  │
│  │  AES-256-GCM encrypted data            │  │
│  └────────────────────────────────────────┘  │
└──────────────────────────────────────────────┘

Por que Envelope Encryption?

  1. Performance: AES é muito mais rápido que RSA para dados grandes
  2. Segurança: Chave AES diferente a cada backup
  3. Flexibilidade: Pode rotacionar chave RSA sem re-criptografar dados

Algoritmos

Criptografia Assimétrica

Aspecto Valor
Algoritmo RSA
Tamanho da chave 4096 bits
Padding OAEP-SHA256
Uso Proteger chave AES

Criptografia Simétrica

Aspecto Valor
Algoritmo AES-256
Modo GCM (Galois/Counter Mode)
Nonce 12 bytes (aleatório)
Tag 16 bytes (autenticação)
Uso Criptografar dados

Integridade

Aspecto Valor
Checksum BLAKE3
Autenticação GCM tag
Verificação Pós-download

Modos de Chave

Default

Usa a mesma chave RSA para todos os backups:

[crypto]
key_mode = "default"
default_key_ref = "/etc/backupally/keys/default.key"

Vantagens:

  • Simples de gerenciar
  • Uma chave para todos os restores

Desvantagens:

  • Comprometimento afeta todos os backups

Ephemeral

Gera nova chave para cada backup:

[crypto]
key_mode = "ephemeral"

Vantagens:

  • Isolamento total entre backups
  • Comprometimento afeta apenas um backup

Desvantagens:

  • Requer gerenciamento de múltiplas chaves
  • Necessita servidor central para recovery

None

Sem criptografia (não recomendado):

[crypto]
key_mode = "none"

Uso válido:

  • Dados públicos
  • Ambientes de desenvolvimento
  • Storage já criptografado

Key Fingerprint

Cada backup contém o fingerprint da chave que o criptografou:

Fingerprint = SHA256(public_key)[0:8]

8 bytes que identificam unicamente a chave.

Verificar Fingerprint

backupally keys fingerprint /etc/backupally/keys/default.key
# Key fingerprint: a1b2c3d4e5f6a7b8

Uso no Header

O fingerprint está no header do arquivo .ally, permitindo:

  • Identificar qual chave usar para restore
  • Detectar chave errada antes de tentar descriptografar

Gerenciamento de Secrets

Armazenamento

Secrets são armazenados em /etc/backupally/secrets.db:

  • SQLite criptografado
  • Chave derivada da chave mestra
  • Permissões 0600

Uso em Configuração

password = "${secret:mysql_password}"
api_key = "${secret:aws_key}"

Proteção

  • Secrets nunca aparecem em logs
  • Valores descriptografados apenas em memória
  • Memória zerada após uso (zeroize)

Servidor Central

Para disaster recovery, registre chaves no servidor central:

[central]
enabled = true
server_url = "https://central.backupally.io"
api_key = "seu-api-key"
auto_sync = true

Recuperação

backupally keys recover \
  --fingerprint a1b2c3d4e5f6a7b8 \
  --server https://central.backupally.io \
  --api-key your-key

Segurança de Rede (v0.10.0)

A partir da v0.10.0, BackupAlly implementa múltiplas camadas de proteção de rede.

TLS Pinning (SPKI)

O binário embute pins SPKI (SHA-256 do SubjectPublicKeyInfo) de hosts críticos, extraídos em build-time pelo build.rs. Um ServerCertVerifier customizado valida o pin do certificado end-entity após a chain validation padrão:

Host Propósito
get.backupally.com CDN de binários (canal de auto-update)
api.github.com Metadata de releases

Se o pin não corresponder (e.g., MITM com certificado falso), a conexão é rejeitada.

Cliente HTTP Centralizado

Todas as requisições HTTP passam por get_http_client() — um singleton com:

  • TLS pinning ativo (verifier integrado)
  • User-Agent com versão
  • Timeouts e pool de conexões consistentes

Message Signing

Mensagens enviadas ao servidor central são assinadas com RSA-SHA256 (PKCS#1 v1.5), incluindo:

  • client_key_hash: identificador da chave do cliente
  • nonce: anti-replay (uso único)
  • timestamp: validado com janela de 5 minutos
  • signature: base64 da assinatura do payload

Anti-Replay

  • Nonce store: Nonces vistos são armazenados em memória com TTL de 10 minutos
  • Timestamp validation: Mensagens fora da janela de 5 minutos são rejeitadas
  • Clock sync check: init_security() verifica sincronização NTP via timedatectl no startup

Inicialização de Segurança

Ao iniciar, BackupAlly executa init_security() que:

  1. Verifica quais pins TLS estão ativos (não placeholder)
  2. Verifica sincronização NTP
  3. Emite warnings para condições degradadas (não aborta)

Boas Práticas

Chaves

  1. Backup offline: Mantenha cópia em local seguro
  2. Anote fingerprints: Para recovery
  3. Rotação periódica: A cada 6-12 meses
  4. Permissões: 0600 em arquivos de chave

Secrets

  1. Nunca em código: Use ${secret:name}
  2. Rotação: Atualize periodicamente
  3. Mínimo privilégio: Usuários de backup com acesso limitado

Storage

  1. Criptografia em trânsito: HTTPS sempre
  2. Criptografia em repouso: S3 SSE, B2 SSE
  3. Acesso restrito: IAM policies específicas

Verificação de Integridade

Pós-Backup

[global]
backup_verification = true

Após upload, BackupAlly:

  1. Download parcial do arquivo
  2. Verifica header e checksum
  3. Confirma integridade

Pré-Restore

backupally restore --verify --target mysql-prod --snapshot snap_xxx

Verifica:

  1. Checksum do arquivo
  2. GCM tag (autenticação)
  3. Fingerprint da chave

Envelope v2 em Blocos

Desde a 0.29.0 o payload cifrado do .ally é uma sequência de blocos de 1 MiB (construção STREAM, a mesma usada por Tink e age), cada um com sua própria tag AES-256-GCM. A chave de dados continua aleatória por backup e envelopada em RSA-OAEP com a chave do servidor; os 48 bytes do header entram como dado autenticado de todos os blocos.

header .ally (48 B)
ALYE | 2 | key_len | chave envelopada (RSA-OAEP) | nonce base (8 B) | chunk_size
bloco 0 || tag · bloco 1 || tag · … · bloco final || tag

O que isso muda para você:

  • Backup e restore em memória constante, qualquer tamanho
  • Arquivo que acaba antes do bloco final é truncado e o restore falha dizendo em que bloco — nunca entrega um restore parcial
  • Qualquer byte alterado invalida o bloco; header alterado invalida o bloco 0
  • Versões anteriores à 0.29.0 não leem este formato. A 0.29.0 lê todos os anteriores (envelope v1 e RSA em blocos), em memória

Contenção de Path no Download/Restore (0.35.0)

`storage download` e `restore --from-storage` resolvem uma key/remote_path do `history` contra o storage — a superfície pede contenção de path porque a entrada pode vir de um sistema que não é o BackupAlly (ex.: uma UI).

  • toda resolução canonicaliza base (raiz do storage) e destino, e exige que o resultado fique dentro da base. Raiz relativa ou / deixou de ter fallback — evitava escapar do storage local via raiz mal configurada;
  • symlink apontando para fora da base é recusado, não seguido — inclusive quando é um subdiretório que é symlink para fora. Não guarde backup atrás de um symlink que sai do diretório do storage;
  • a key recusa .., / inicial e \ (E1002) antes de qualquer leitura;
  • raiz rasa é fraca: a contenção, na busca pela raiz, é relativa à parte fixa do path do [[storage]] driver = "local" (tudo antes do primeiro ${...}). Uma raiz rasa como /var/${x}/bk → parte fixa /var deixa baixar qualquer arquivo sob /var que o processo consiga ler. Use raízes absolutas, profundas e dedicadas ao BackupAlly (ex.: /var/backups/backupally/${hostname}, nunca /, /var ou /home);
  • --database é validado e sanitizado antes de compor qualquer caminho: até 255 caracteres; só letras, dígitos, _, -, $, .; recusa vazio, .., começar por ./- sem = (--database=-legacy funciona, --database -legacy não — o clap trata como flag), /, \ e caractere de controle. Inválido → E1002, antes de qualquer leitura no storage.

Essas regras valem para o BackupAlly resolvendo a key/--database que recebe por argumento. Elas não protegem um path de storage mal configurado que já aponte, por si só, para fora de onde deveria — a raiz é escolha de quem escreve a task.

Verificação do Binário de Release

Cada release é assinada com minisign (Ed25519). O self-update já verifica a assinatura antes de substituir o binário; esta seção é para quem baixa o arquivo à mão.

A chave pública está publicada no CDN (desde 0.27.2):

  • https://get.backupally.com/x86_64/pubkey.pub
  • https://get.backupally.com/aarch64/pubkey.pub
  • https://get.backupally.com/pubkey.pub

Fingerprint da chave de release: 1C6665A7E22166F5

Confira o fingerprint contra este documento na primeira vez (TOFU, trust on first use) e guarde a chave. Verificação:

ARCH=$(uname -m)
curl -fsSL -o backupally         "https://get.backupally.com/${ARCH}/backupally"
curl -fsSL -o backupally.minisig "https://get.backupally.com/${ARCH}/backupally.minisig"
curl -fsSL -o pubkey.pub         "https://get.backupally.com/${ARCH}/pubkey.pub"

minisign -Vm backupally -p pubkey.pub

Saída esperada: Signature and comment signature verified. Qualquer outra coisa significa binário ou assinatura adulterados — não instale.

By Borlot.com.br on 11/02/2026