Ory Kratos uses multiple layers of encryption and hashing to protect sensitive user data.
Password hashing
Kratos supports two password hashing algorithms:
Argon2id (recommended)
Argon2id is the recommended hashing algorithm for new deployments:
hashers:
algorithm: argon2
argon2:
memory: 131072 # 128 MB
iterations: 3 # Time cost
parallelism: 4 # CPU threads
salt_length: 16 # Salt bytes
key_length: 32 # Output hash length
Memory cost in KB. Higher values increase security but require more RAM.Recommended: 128 MB (131072 KB) or higher
Time cost (number of iterations). Higher values increase computation time.Recommended: 3-4 iterations
Degree of parallelism (number of threads). Should match available CPU cores.Recommended: 4 threads
Length of the random salt in bytes.Recommended: 16 bytes
Length of the resulting hash in bytes.Recommended: 32 bytes
Bcrypt (legacy)
Bcrypt is supported for backward compatibility:
hashers:
algorithm: bcrypt
bcrypt:
cost: 12 # Work factor (2^12 iterations)
Bcrypt work factor. Each increment doubles the computation time.Recommended: 12 or higher (minimum: 10)
Argon2id is more resistant to GPU and ASIC attacks than bcrypt. Use Argon2id for new deployments.
Password hashing comparison
| Algorithm | Security | Performance | Resistance to |
|---|
| Argon2id | Excellent | Good | GPU, ASIC, side-channel attacks |
| Bcrypt | Good | Moderate | General attacks |
Credential encryption
Kratos encrypts sensitive credential data at rest using AES-256-GCM:
OIDC provider tokens
OAuth tokens from OIDC providers are encrypted:
// From identity/credentials_oidc.go
type CredentialsOIDC struct {
Providers []ProviderCredentials `json:"providers"`
}
type ProviderCredentials struct {
Provider string `json:"provider"`
Subject string `json:"subject"`
InitialIDToken string `json:"initial_id_token"` // Encrypted
InitialAccessToken string `json:"initial_access_token"` // Encrypted
InitialRefreshToken string `json:"initial_refresh_token"` // Encrypted
}
WebAuthn credentials
WebAuthn authenticator data is encrypted:
// From identity/credentials_webauthn.go
type CredentialsWebAuthn struct {
Credentials []CredentialWebAuthn `json:"credentials"`
}
type CredentialWebAuthn struct {
ID []byte `json:"id"`
PublicKey []byte `json:"public_key"` // Encrypted
AttestationType string `json:"attestation_type"`
Authenticator webauthn.Authenticator `json:"authenticator"` // Encrypted
}
Cipher secrets
Configure encryption secrets in your configuration:
secrets:
cipher:
- your-32-character-secret-key-here
- old-key-for-rotation-support
Cipher secrets must be exactly 32 characters (256 bits) for AES-256 encryption.
Generate secure cipher secrets
Secret rotation
Kratos supports secret rotation for both cipher and cookie secrets:
secrets:
cipher:
- new-secret-active-key
- old-secret-for-decryption-only
- older-secret-for-legacy-data
cookie:
- new-cookie-secret
- old-cookie-secret
Rotation process
Add new secret
Add the new secret at the beginning of the array:secrets:
cipher:
- new-secret-key # Used for new encryptions
- old-secret-key # Still valid for decryption
Deploy configuration
Deploy Kratos with the updated configuration. Both keys are now valid.
Wait for transition period
Wait for all encrypted data to be re-encrypted with the new key. This happens gradually as data is accessed and updated.
Remove old secret
After sufficient time (e.g., 30-90 days), remove the old secret:secrets:
cipher:
- new-secret-key # Only key
Database encryption
Encryption at rest
Enable database-level encryption:
PostgreSQL
MySQL
CockroachDB
Use Transparent Data Encryption (TDE) or encrypted storage:-- Enable pgcrypto extension
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Use encrypted tablespaces (requires OS-level encryption)
CREATE TABLESPACE encrypted LOCATION '/encrypted/data';
ALTER DATABASE kratos SET TABLESPACE encrypted;
Or use storage-level encryption (e.g., LUKS, AWS EBS encryption). Enable InnoDB encryption:-- my.cnf
[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring
-- Enable encryption for Kratos tables
ALTER TABLE identities ENCRYPTION='Y';
ALTER TABLE sessions ENCRYPTION='Y';
Use encryption at rest:cockroach start \
--certs-dir=certs \
--store=path=/data,attrs=ssd \
--enterprise-encryption=path=/data,key=/path/to/aes-128.key,old-key=/path/to/old-key
Encrypted connections
Always use SSL/TLS for database connections:
dsn: postgres://user:pass@host/db?sslmode=require
SSL mode for PostgreSQL:
require: Require SSL (recommended minimum)
verify-ca: Require SSL and verify CA certificate
verify-full: Require SSL and verify hostname (most secure)
disable: No SSL (never use in production)
Session tokens
Session tokens are cryptographically secure random strings:
// From session/tokenizer.go
const (
TokenLength = 32 // 256-bit token
)
func NewToken() string {
b := make([]byte, TokenLength)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return base64.RawURLEncoding.EncodeToString(b)
}
Tokens are:
- 256 bits of entropy
- Base64 URL-safe encoded
- Cryptographically random
- Stored as opaque values
Cookie encryption
Session cookies are encrypted and signed:
secrets:
cookie:
- your-32-character-cookie-secret
Cookie secrets are used for:
- Encrypting session data in cookies
- Signing cookies to prevent tampering
- CSRF token generation
Kratos uses gorilla/securecookie for cookie encryption with AES-256-GCM and HMAC-SHA256.
HTTPS/TLS
Always use HTTPS in production:
serve:
public:
base_url: https://auth.example.com/
admin:
base_url: https://admin.example.com/
cookies:
secure: true # Only send cookies over HTTPS
TLS configuration
Terminate TLS at a reverse proxy (recommended):server {
listen 443 ssl http2;
server_name auth.example.com;
ssl_certificate /etc/ssl/certs/auth.example.com.crt;
ssl_certificate_key /etc/ssl/private/auth.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://kratos:4433;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Configure Kratos to handle TLS directly:serve:
public:
host: 0.0.0.0
port: 4433
tls:
cert:
path: /etc/ssl/certs/kratos.crt
key:
path: /etc/ssl/private/kratos.key
Encryption key management
Best practices
Use a key management service
Store encryption keys in a secure key management service:
- AWS KMS
- Google Cloud KMS
- Azure Key Vault
- HashiCorp Vault
Rotate keys regularly
Implement a key rotation schedule (e.g., every 90 days).
Never commit keys to version control
Use environment variables or secret management tools.
Audit key usage
Enable logging and monitoring for key access.
Example: AWS KMS integration
#!/bin/bash
# Retrieve cipher secret from AWS KMS
CIPHER_SECRET=$(aws kms decrypt \
--ciphertext-blob fileb://encrypted-secret.bin \
--query Plaintext \
--output text | base64 --decode)
export SECRETS_CIPHER="$CIPHER_SECRET"
kratos serve -c config.yml
Compliance
GDPR
Encryption supports GDPR compliance:
- Data protection by design
- Pseudonymization of personal data
- Security of processing (Article 32)
PCI DSS
For payment card data:
- Use strong cryptography (AES-256)
- Protect encryption keys
- Regularly rotate keys
- Encrypt data in transit and at rest
Monitoring
Monitor encryption-related events:
log:
level: info
format: json
Key events to track:
- Decryption failures (may indicate key rotation issues)
- Hashing failures
- TLS handshake errors
- Key rotation events
Next steps
Best practices
Complete security hardening guide
CSRF protection
Learn about CSRF tokens