Skip to content

PCI DSS Pregunta 51 — Cifrado de credenciales (transmisión + almacenamiento)

CampoValor
SolicitanteTejal Rathod — QSA ControlCase
Pregunta"Para la plataforma de muestra y/o aplicaciones, proporcionar pruebas de que las contraseñas de autenticación están cifradas durante la transmisión y almacenamiento."
Activos en muestracde-pool-node-01 (cde-38q702), cde-fw (fintrix-production-cde-fw), microservicios del alcance
Fecha de extracción2026-06-16
Tipo de evidenciaCaptura en vivo de configuración de OS, TLS handshake real, código fuente del microservicio + DBaaS + Wazuh hashes + K8s Secrets
EstadoRESUELTO — todas las credenciales en alcance están cifradas en tránsito (TLSv1.3 / AES-256-GCM, AEAD ciphers) y en reposo (yescrypt, bcrypt, PBKDF2-HMAC-SHA256, K8s Secrets + DOKS LUKS)
Controles PCIReq 8.3.2 (strong-cryptography on credentials in transit + storage), 8.3.1 (general crypto), 4.2.1 (strong crypto in transit)
Paquete adjuntoq51-encryption-20260616.tar.gz

Resumen ejecutivo: Se verificó in-vivo que cada credencial en el alcance (sistema operativo del nodo CDE, comunicación entre microservicios y la base de datos, tokens JWT, hashes de password de Wazuh, K8s Secrets) usa criptografía fuerte tanto en transmisión como en almacenamiento. La verificación incluye: (1) algoritmo OS-level ENCRYPT_METHOD YESCRYPT en /etc/login.defs del nodo cde-38q702; (2) SSH ciphers AEAD (chacha20-poly1305, aes256-gcm), MACs SHA-2 family, KEX post-quantum hybrid; (3) DO Cloud Firewall cde-fw que rechaza port 22 y solo permite ports cifrados; (4) handshake TLSv1.3 / TLS_AES_256_GCM_SHA384 real capturado en una conexión activa de auth-service al DBaaS Postgres via pg_stat_ssl; (5) JWT firmados con RS256 (asimétrico, llave privada montada como Secret tmpfs); (6) passwords almacenados con PBKDF2-HMAC-SHA256 a 210,000 iteraciones + salt random 16-byte; (7) Wazuh internal users con bcrypt $2b$12$... cost=12; (8) K8s Secrets type Opaque + DOKS etcd encryption-at-rest con LUKS. Cero credenciales en plaintext, cero algoritmos débiles (MD5, SHA-1, 3DES, RC4, DES).


1. Mapeo de requisitos PCI DSS v4.0

RequisitoDescripciónImplementación
8.3.2Strong cryptography used to render all authentication factors unreadable during transmission and storageCubierto por las 9 secciones siguientes
8.3.1All authentication factors classified + protected with strong cryptographyyescrypt, bcrypt, PBKDF2-SHA256
4.2.1Strong cryptography in transit over open public networksTLSv1.3 / AES-256-GCM

2. cde-pool-node-01 — OS-level password storage

Q51-T1

Algoritmo: yescrypt (memory-hard KDF moderno, NIST-aligned)

SettingValorArchivo
ENCRYPT_METHODYESCRYPT/etc/login.defs
pam_unix algorithmyescrypt (opción obscure)/etc/pam.d/common-password
Hash format$y$<params>$<salt>$<hash> (256-bit)/etc/shadow

Observación operacional: los usuarios root y system tienen password disabled (* o !* en /etc/shadow) — el login al nodo es exclusivamente con SSH key.

Mapeo PCI 8.3.2:

  • ✓ yescrypt = strong-cryptography (memory-hard KDF, post-quantum-resistant)
  • ✓ No MD5 / SHA1 / DES en ENCRYPT_METHOD
  • /etc/shadow permisos 0600 (root-only read)
  • /etc/passwd no contiene hashes (campo password = x)

3. cde-pool-node-01 — SSH transport encryption

Q51-T2

Capturado con sshd -T directamente desde el nodo (privileged pod con chroot al host):

CategoríaAlgoritmos aceptadosAlgoritmos rechazados
Ciphers[email protected], aes128/[email protected], aes128/192/256-ctr✗ 3DES, DES, RC4, blowfish, arcfour
MACshmac-sha2-256/[email protected], umac-128/64-etm✗ MD5, SHA-1 in MAC primary
KEXmlkem768x25519-sha256, sntrup761x25519-sha512, curve25519-sha256, ecdh-sha2-nistp✗ diffie-hellman-group1-sha1, group14-sha1
Host keysssh-ed25519, ecdsa-sha2-nistp*, rsa-sha2-256/512✗ ssh-rsa (SHA-1), ssh-dss

Mapeo PCI 8.3.2:

  • ✓ AEAD ciphers (chacha20-poly1305, aes-gcm) — recomendación NIST + OWASP
  • ✓ Post-quantum KEX (mlkem768, sntrup761) — preparación criptográfica futura
  • ✓ MACs SHA-2 family con encrypt-then-MAC (etm)
  • ✗ Algoritmos legacy (MD5, SHA-1, 3DES, RC4) NO presentes

4. cde-fw — Cloud Firewall (transport layer policy)

Q51-T3

DigitalOcean Cloud Firewall fintrix-production-cde-fw (UUID 89879427-...) aplica deny-by-default a los nodos del pool CDE.

4.1 Inbound — solo encrypted-port channels

PuertoOrigen permitidoProtocolo cifrado
44310.100.1.0/24 (gateway subnet)HTTPS (TLS 1.2+)
351010.100.10.0/24card-vault-service (ClusterIP VPC private)
360010.100.10.0/24, 10.100.20.0/24tokenization-service
370010.100.10.0/24, 10.100.20.0/24auth-service
10250-1025510.100.0.0/16kubelet (mTLS only)

4.2 Puertos NO permitidos (gap explícitamente evitado)

  • Port 22 (SSH) — no rule, SSH inaccessible from any source
  • ❌ Port 80 (plain HTTP) — no rule, no plaintext HTTP ingress
  • ❌ Cualquier puerto desde 0.0.0.0/0 excepto VAPT compliance-scanner

4.3 Outbound

Solo TLS/SSL outgoing flows:

  • TCP 443 → 0.0.0.0/0 (HTTPS para DO API + package updates)
  • TCP 5432 → 10.100.10.0/24 (Postgres DBaaS, sslmode=require)
  • TCP 9092 → 10.100.20.0/24 (Kafka, SASL_SSL)
  • UDP 53 → 0.0.0.0/0 (DNS)

Mapeo PCI 8.3.2 + 4.2.1:

  • ✓ Zero clear-text credential paths possible
  • ✓ All allowed channels carry TLS or VPC-private encryption
  • ✓ Deny-by-default complies with PCI 1.2.1

5. Microservicios → Postgres — TLS en vivo

Q51-T4

Capturado desde un pod en vivo de auth-service corriendo en el cluster prod. La conexión activa al DBaaS se interrogó via pg_stat_ssl:

sql
SELECT version, cipher, bits 
FROM pg_stat_ssl 
WHERE pid = pg_backend_pid();
CampoValor
versionTLSv1.3
cipherTLS_AES_256_GCM_SHA384 (AEAD)
bits256

DATABASE_URL incluye sslmode=require que fuerza TLS y rechaza el connect si el servidor no lo soporta:

postgresql://doadmin:AVNS_***@private-fintrix-production-fintrix-pci-do-user-...
              .h.db.ondigitalocean.com:25060/auth_db?sslmode=require

Mapeo PCI 8.3.2 + 4.2.1:

  • ✓ TLSv1.3 (no v1.0/v1.1/SSLv3)
  • ✓ AEAD cipher AES-256-GCM
  • ✓ 256-bit symmetric key
  • sslmode=require nunca downgrade a plaintext

6. auth-service — JWT RS256 + RSA-2048

Q51-T5

Verificado por grep en el código compilado del pod live:

javascript
// system-users.module.js + rbac/admin-rbac/admin-rbac.guard.js
imports: [jwt_1.JwtModule.register({ signOptions: { algorithm: 'RS256' } })]
algorithms: ['RS256']
  • ✓ RS256 (RSA-SHA256, asymmetric) — sin shared secret
  • ✓ JWT key pair montado desde K8s Secret fintrix-pci-secrets como tmpfs (/app/keys/)
  • ✓ HS256 (symmetric) NO usado: grep -rE "HS256" /app/dist | wc -l0
  • ✓ Llave privada nunca abandona el pod (mount RO, never logged)

Mapeo PCI 8.3.2:

  • ✓ RS256 = SHA-256 + RSA-2048 (PCI strong-cryptography)
  • ✓ Asymmetric = even if verifier compromised, issuer key not exposed
  • ✓ Token signed → integrity verifiable by any party with public key

7. auth-service — Password STORAGE (PBKDF2-HMAC-SHA256)

Q51-T6

Implementación directamente del código fuente:

javascript
const iterations = 210000;
const salt = crypto.randomBytes(16);
const hash = crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256');
return `pbkdf2$sha256$${iterations}$${salt.toString('hex')}$${hash.toString('hex')}`;

Sample real generado con la misma función:

pbkdf2$sha256$210000$1b47785582cfe7ea61ac6087ac374bae$
9e2fe25233c5da0ff4ae739d86ba525fae506bd19191c2710eada88c02504df8
CampoValorPCI 8.3.2
Schemepbkdf2NIST SP 800-132 approved ✓
AlgorithmHMAC-SHA256strong ✓
Iterations210000OWASP 2024 recommendation ✓
Salt16 random bytes per userNo rainbow tables ✓
Hash32 bytes / 256 bitsexceeds PCI floor ✓

Storage location: iam_core.iam_user_credentials.password_hash (Postgres text column). Network access: solo via TLSv1.3 (§5).


8. Wazuh — bcrypt cost=12

Q51-T7

Extraído del backup de OpenSearch security index:

yaml
admin:
  hash: "$2b$12$5bcTe7sNqmIRME1aT2k99unDWbTeThpKGHPTJ1YFsh7DOdpd5QLPi"
kibanaserver:
  hash: "$2b$12$vqzB78WZhakz7/BAcJpISONaBovU8g13626wZ1SvVh4TjP11u.OdG"
  • $2b$ — bcrypt variant 2b (most secure variant)
  • $12$ — cost factor 2^12 = 4096 rounds per attempt
  • 22-char salt + 31-char hash = 60-char total

Storage: OpenSearch security index .opendistro_security (encrypted at rest by DOKS LUKS). Transmission: HTTPS (443) con cert pinning en /etc/wazuh-*-certs/.


9. K8s Secrets — envelope encryption

Q51-T8

Secret fintrix-pci-secrets (type Opaque) contiene 12 credenciales — todas en base64:

KeyContenido
AUTH_DATABASE_URLpostgresql://...?sslmode=require
DB_PASSWORD, POSTGRES_PASSWORD, VAULT_DB_PASSWORDDBaaS passwords (DO-generated)
ENCRYPTION_KEY32-byte AES-256 master key (envelope encryption en card-vault)
HMAC_KEY32-byte HMAC-SHA256 integrity key
JWT_PRIVATE_KEYRSA-2048 private key (PEM, base64)
JWT_PUBLIC_KEYRSA-2048 public key (PEM, base64)

9.1 Storage layer

LayerEncryption
etcd cluster (DOKS managed)LUKS volumes + cloud-provider key (DO docs ref)
kube-apiserverTLS 1.2+ only, requires RBAC for get secret
In-pod mounttmpfs (RAM-only, never written to node disk)
AuditEach Secret read logged → Spaces (Q32)

9.2 Access controls

  • ✓ K8s API requires mTLS + RBAC (Q39 §SoD)
  • kubectl requires per-user kubeconfig + cluster cert
  • ✓ Pods receive Secrets via tmpfs volume mount (verified: df /app/keys returns tmpfs)
  • ✓ Audit log captures every get secret call

10. Matriz de cobertura — todas las credenciales en alcance

Q51-T9

AssetIn transitAt rest
cde-pool-node-01 (OS)SSH AEAD 256-bityescrypt /etc/shadow
cde-pool-node-02 (OS)SSH AEAD 256-bityescrypt /etc/shadow
cde-fw (Cloud Firewall)No plaintext ingressN/A
auth-service ↔ PostgresTLSv1.3 / AES-256-GCMPBKDF2-HMAC-SHA256 (210k iter)
payments-api ↔ PostgresTLSv1.3 / AES-256-GCM(no human creds stored)
tokenization-service ↔ PostgresTLSv1.3 / AES-256-GCMAES-256-GCM envelope
card-vault-service ↔ PostgresTLSv1.3 / AES-256-GCMAES-256-GCM envelope (PAN tokens)
webhooks-service ↔ PostgresTLSv1.3 / AES-256-GCMHMAC-SHA256 signing keys
Kong ↔ microservicesClusterIP private VPCN/A
External ↔ Kong (WAF Coraza)TLS 1.2+ (HTTPS)N/A
JWT tokensRS256 over HTTPSK8s Secret (tmpfs + LUKS)
Wazuh user passwordsHTTPS to indexerbcrypt $2b$12$
Wazuh API tokensHTTPS to managerK8s Secret
K8s Secrets (all)kube-apiserver mTLSetcd LUKS (DOKS managed)
Kafka SASL credentialsSASL_SSLK8s Secret

PCI 8.3.2 verdict: zero plaintext credential transmission, zero plaintext storage. All flows use FIPS-approved, OWASP-recommended, NIST-aligned algorithms.


11. Política de referencia

12. Paquete de evidencia

q51-encryption-20260616.tar.gz contiene:

  • T1..T9.txt — output CLI completo (8 archivos)
  • screenshots/*.png — 9 terminales de evidencia
  • gen_terminal_screenshots.py — generator de PNGs
  • pg-tls-check.js — script de verificación de TLS activo
  • wazuh-snap/internal_users.yml — extracto de hashes bcrypt

Reproducible end-to-end con acceso al cluster K8s prod.

Documentación Confidencial — Solo para uso interno y auditoría PCI DSS