[{"data":1,"prerenderedAt":404},["ShallowReactive",2],{"blog-how-to/database-encryption":3},{"id":4,"title":5,"body":6,"category":384,"date":385,"dateModified":386,"description":387,"draft":388,"extension":389,"faq":390,"featured":388,"headerVariant":391,"image":390,"keywords":390,"meta":392,"navigation":393,"ogDescription":394,"ogTitle":390,"path":395,"readTime":390,"schemaOrg":396,"schemaType":397,"seo":398,"sitemap":399,"stem":400,"tags":401,"twitterCard":402,"__hash__":403},"blog/blog/how-to/database-encryption.md","How to Encrypt Database Data",{"type":7,"value":8,"toc":368},"minimark",[9,13,17,21,27,30,43,48,51,54,58,82,86,112,157,173,189,205,221,244,248,269,275,279,284,287,291,294,298,301,305,308,330,349],[10,11],"category-badge",{"category":12},"How-To Guide",[14,15,5],"h1",{"id":16},"how-to-encrypt-database-data",[18,19,20],"p",{},"Protect sensitive data with encryption at every level",[22,23,24],"tldr",{},[18,25,26],{},"TL;DR (30 minutes):\nEnable encryption at rest (most managed databases do this by default), use TLS for connections (add\n?sslmode=require\nto your connection string), and encrypt sensitive fields in your application before storing them using a library like\ncrypto\nor a KMS service.",[18,28,29],{},"Prerequisites:",[31,32,33,37,40],"ul",{},[34,35,36],"li",{},"Database access (PostgreSQL, MySQL, MongoDB, etc.)",[34,38,39],{},"Understanding of your data sensitivity requirements",[34,41,42],{},"Node.js, Python, or similar backend language",[44,45,47],"h2",{"id":46},"why-this-matters","Why This Matters",[18,49,50],{},"Encryption is your last line of defense. If an attacker gets access to your database files, backups, or intercepts network traffic, encryption ensures they can't read your sensitive data.",[18,52,53],{},"Compliance regulations like GDPR, HIPAA, and PCI-DSS require encryption of personal and payment data. Even if you're not regulated yet, encryption protects you from the catastrophic consequences of a data breach.",[44,55,57],{"id":56},"understanding-encryption-layers","Understanding Encryption Layers",[59,60,61],"info-box",{},[31,62,63,70,76],{},[34,64,65,69],{},[66,67,68],"strong",{},"Encryption at rest:"," Data encrypted on disk - protects against physical theft or unauthorized disk access",[34,71,72,75],{},[66,73,74],{},"Encryption in transit:"," Data encrypted over the network - protects against eavesdropping",[34,77,78,81],{},[66,79,80],{},"Application-level encryption:"," Data encrypted before storage - protects even if database is compromised",[44,83,85],{"id":84},"step-by-step-guide","Step-by-Step Guide",[87,88,90,95,98,109],"step",{"number":89},"1",[91,92,94],"h3",{"id":93},"enable-encryption-at-rest","Enable encryption at rest",[18,96,97],{},"Most managed database services enable this by default:",[99,100,105],"pre",{"className":101,"code":103,"language":104},[102],"language-text","# Check encryption status for various platforms:\n\n# PostgreSQL (RDS) - enabled by default with AWS KMS\n# Verify in AWS Console → RDS → Database → Configuration\n\n# MongoDB Atlas - enabled by default\n# Verify in Atlas → Security → Encryption at Rest\n\n# Supabase - enabled by default with AES-256\n# No action needed\n\n# Self-hosted PostgreSQL - enable in postgresql.conf\n# Requires pgcrypto extension or disk-level encryption\n","text",[106,107,103],"code",{"__ignoreMap":108},"",[18,110,111],{},"For self-hosted databases, consider full-disk encryption (LUKS on Linux, BitLocker on Windows) or Transparent Data Encryption (TDE) if your database supports it.",[87,113,115,119,122,128,131],{"number":114},"2",[91,116,118],{"id":117},"enable-encryption-in-transit-tlsssl","Enable encryption in transit (TLS/SSL)",[18,120,121],{},"Always use encrypted connections to your database:",[99,123,126],{"className":124,"code":125,"language":104},[102],"// PostgreSQL connection with SSL\nconst connectionString =\n  'postgresql://user:pass@host:5432/db?sslmode=require';\n\n// MongoDB with TLS\nconst mongoUri =\n  'mongodb+srv://user:pass@cluster.mongodb.net/db?tls=true';\n\n// MySQL with SSL\nconst mysqlConfig = {\n  host: 'your-host.com',\n  user: 'user',\n  password: 'pass',\n  database: 'db',\n  ssl: {\n    rejectUnauthorized: true\n  }\n};\n",[106,127,125],{"__ignoreMap":108},[18,129,130],{},"SSL modes for PostgreSQL:",[31,132,133,139,145,151],{},[34,134,135,138],{},[106,136,137],{},"disable"," - No SSL (never use in production)",[34,140,141,144],{},[106,142,143],{},"require"," - Encrypt connection, don't verify certificate",[34,146,147,150],{},[106,148,149],{},"verify-ca"," - Verify server certificate is signed by trusted CA",[34,152,153,156],{},[106,154,155],{},"verify-full"," - Verify certificate and hostname match (most secure)",[87,158,160,164,167],{"number":159},"3",[91,161,163],{"id":162},"implement-application-level-encryption","Implement application-level encryption",[18,165,166],{},"Encrypt sensitive data before storing it:",[99,168,171],{"className":169,"code":170,"language":104},[102],"// Node.js with built-in crypto\nimport crypto from 'crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst KEY = Buffer.from(process.env.ENCRYPTION_KEY, 'hex'); // 32 bytes\n\nfunction encrypt(text) {\n  const iv = crypto.randomBytes(16);\n  const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);\n\n  let encrypted = cipher.update(text, 'utf8', 'hex');\n  encrypted += cipher.final('hex');\n\n  const authTag = cipher.getAuthTag().toString('hex');\n\n  // Return IV + authTag + encrypted data\n  return iv.toString('hex') + ':' + authTag + ':' + encrypted;\n}\n\nfunction decrypt(encryptedData) {\n  const [ivHex, authTagHex, encrypted] = encryptedData.split(':');\n\n  const iv = Buffer.from(ivHex, 'hex');\n  const authTag = Buffer.from(authTagHex, 'hex');\n  const decipher = crypto.createDecipheriv(ALGORITHM, KEY, iv);\n\n  decipher.setAuthTag(authTag);\n\n  let decrypted = decipher.update(encrypted, 'hex', 'utf8');\n  decrypted += decipher.final('utf8');\n\n  return decrypted;\n}\n\n// Usage\nconst ssn = '123-45-6789';\nconst encryptedSSN = encrypt(ssn);\n// Store encryptedSSN in database\n\nconst decryptedSSN = decrypt(encryptedSSN);\n// Returns: '123-45-6789'\n",[106,172,170],{"__ignoreMap":108},[87,174,176,180,183],{"number":175},"4",[91,177,179],{"id":178},"use-a-key-management-service","Use a key management service",[18,181,182],{},"Don't hardcode encryption keys - use a KMS:",[99,184,187],{"className":185,"code":186,"language":104},[102],"// AWS KMS example\nimport { KMSClient, EncryptCommand, DecryptCommand } from \"@aws-sdk/client-kms\";\n\nconst kms = new KMSClient({ region: \"us-east-1\" });\nconst KEY_ID = process.env.KMS_KEY_ID;\n\nasync function encryptWithKMS(plaintext) {\n  const command = new EncryptCommand({\n    KeyId: KEY_ID,\n    Plaintext: Buffer.from(plaintext)\n  });\n\n  const response = await kms.send(command);\n  return Buffer.from(response.CiphertextBlob).toString('base64');\n}\n\nasync function decryptWithKMS(ciphertext) {\n  const command = new DecryptCommand({\n    CiphertextBlob: Buffer.from(ciphertext, 'base64')\n  });\n\n  const response = await kms.send(command);\n  return Buffer.from(response.Plaintext).toString('utf8');\n}\n",[106,188,186],{"__ignoreMap":108},[87,190,192,196,199],{"number":191},"5",[91,193,195],{"id":194},"encrypt-specific-fields-in-your-models","Encrypt specific fields in your models",[18,197,198],{},"Create a pattern for handling encrypted fields:",[99,200,203],{"className":201,"code":202,"language":104},[102],"// Prisma middleware example\nimport { PrismaClient } from '@prisma/client';\n\nconst prisma = new PrismaClient();\nconst SENSITIVE_FIELDS = ['ssn', 'taxId', 'bankAccount'];\n\n// Encrypt on write\nprisma.$use(async (params, next) => {\n  if (['create', 'update'].includes(params.action)) {\n    for (const field of SENSITIVE_FIELDS) {\n      if (params.args.data?.[field]) {\n        params.args.data[field] = encrypt(params.args.data[field]);\n      }\n    }\n  }\n  return next(params);\n});\n\n// Decrypt on read\nprisma.$use(async (params, next) => {\n  const result = await next(params);\n\n  if (result && typeof result === 'object') {\n    for (const field of SENSITIVE_FIELDS) {\n      if (result[field]) {\n        result[field] = decrypt(result[field]);\n      }\n    }\n  }\n  return result;\n});\n",[106,204,202],{"__ignoreMap":108},[87,206,208,212,215],{"number":207},"6",[91,209,211],{"id":210},"handle-encryption-key-rotation","Handle encryption key rotation",[18,213,214],{},"Plan for key rotation from the start:",[99,216,219],{"className":217,"code":218,"language":104},[102],"// Store key version with encrypted data\nfunction encryptWithVersion(text, keyVersion = 'v1') {\n  const key = getKeyByVersion(keyVersion);\n  const encrypted = encrypt(text, key);\n  return `${keyVersion}:${encrypted}`;\n}\n\nfunction decryptWithVersion(data) {\n  const [version, encrypted] = data.split(':', 1);\n  const key = getKeyByVersion(version);\n  return decrypt(encrypted, key);\n}\n\n// Re-encrypt data with new key\nasync function rotateEncryption(records) {\n  for (const record of records) {\n    const decrypted = decryptWithVersion(record.sensitiveField);\n    record.sensitiveField = encryptWithVersion(decrypted, 'v2');\n    await record.save();\n  }\n}\n",[106,220,218],{"__ignoreMap":108},[222,223,224,227],"warning-box",{},[18,225,226],{},"Critical Security Considerations:",[31,228,229,232,235,238,241],{},[34,230,231],{},"Never store encryption keys in your database or code repository",[34,233,234],{},"Use authenticated encryption (AES-GCM) to prevent tampering",[34,236,237],{},"Generate keys using cryptographically secure random generators",[34,239,240],{},"Encrypted fields can't be searched - plan your queries accordingly",[34,242,243],{},"Back up encryption keys separately from database backups",[44,245,247],{"id":246},"how-to-verify-it-worked","How to Verify It Worked",[249,250,251,257,263],"ol",{},[34,252,253,256],{},[66,254,255],{},"Check SSL connection:"," Verify your connection is encrypted",[34,258,259,262],{},[66,260,261],{},"Inspect stored data:"," Look at the raw database values",[34,264,265,268],{},[66,266,267],{},"Test decryption:"," Ensure data round-trips correctly",[99,270,273],{"className":271,"code":272,"language":104},[102],"-- PostgreSQL: Check if connection is encrypted\nSELECT ssl, version FROM pg_stat_ssl WHERE pid = pg_backend_pid();\n\n-- View raw encrypted data\nSELECT id, ssn FROM users LIMIT 1;\n-- Should show something like: \"v1:a3f2c1...\"\n\n-- MongoDB: Check TLS status\ndb.serverStatus().security\n",[106,274,272],{"__ignoreMap":108},[44,276,278],{"id":277},"common-errors-troubleshooting","Common Errors & Troubleshooting",[280,281,283],"h4",{"id":282},"error-bad-decrypt","Error: \"bad decrypt\"",[18,285,286],{},"Wrong key or corrupted data. Check you're using the correct key version and the data wasn't modified.",[280,288,290],{"id":289},"error-iv-length-must-be-16-bytes","Error: \"IV length must be 16 bytes\"",[18,292,293],{},"Your initialization vector is wrong size. Always generate a fresh 16-byte IV for each encryption.",[280,295,297],{"id":296},"performance-issues-with-encryption","Performance issues with encryption",[18,299,300],{},"Encryption is CPU-intensive. Consider encrypting only truly sensitive fields, not everything. Use caching for frequently accessed encrypted data.",[280,302,304],{"id":303},"cant-search-encrypted-fields","Can't search encrypted fields",[18,306,307],{},"This is expected - encrypted data can't be searched directly. Use deterministic encryption for searchable fields (less secure) or maintain a separate search index.",[309,310,311,318,324],"faq-section",{},[312,313,315],"faq-item",{"question":314},"Which fields should I encrypt?",[18,316,317],{},"Encrypt PII (SSN, government IDs), financial data (bank accounts, credit cards), health records, and any data that would cause significant harm if exposed. Don't encrypt everything - it adds complexity and performance overhead.",[312,319,321],{"question":320},"Should I hash or encrypt passwords?",[18,322,323],{},"Always hash passwords with bcrypt or argon2 - never encrypt them. Hashing is one-way; even if your database is compromised, the original passwords can't be recovered.",[312,325,327],{"question":326},"Is encryption at rest enough?",[18,328,329],{},"It protects against physical theft and some attack vectors, but not against attackers who gain database access through your application. For sensitive data, combine it with application-level encryption.",[18,331,332,335,340,341,340,345],{},[66,333,334],{},"Related guides:",[336,337,339],"a",{"href":338},"/blog/how-to/hash-passwords-securely","Hash Passwords Securely"," ·\n",[336,342,344],{"href":343},"/blog/how-to/vault-basics","HashiCorp Vault Basics",[336,346,348],{"href":347},"/blog/how-to/aws-secrets-manager","AWS Secrets Manager",[350,351,352,358,363],"related-articles",{},[353,354],"related-card",{"description":355,"href":356,"title":357},"Step-by-step guide to enabling HTTPS with SSL certificates. Learn Let's Encrypt setup, platform-specific configuration f","/blog/how-to/https-setup","How to Set Up HTTPS for Your Website",[353,359],{"description":360,"href":361,"title":362},"Step-by-step guide to securing image uploads. Image validation, resizing, EXIF metadata removal, storage security, and p","/blog/how-to/image-upload-security","How to Secure Image Uploads",[353,364],{"description":365,"href":366,"title":367},"Step-by-step guide to implementing CSRF protection in Next.js and Express. Token-based protection, SameSite cookies, and","/blog/how-to/implement-csrf-protection","How to Implement CSRF Protection",{"title":108,"searchDepth":369,"depth":369,"links":370},2,[371,372,373,382,383],{"id":46,"depth":369,"text":47},{"id":56,"depth":369,"text":57},{"id":84,"depth":369,"text":85,"children":374},[375,377,378,379,380,381],{"id":93,"depth":376,"text":94},3,{"id":117,"depth":376,"text":118},{"id":162,"depth":376,"text":163},{"id":178,"depth":376,"text":179},{"id":194,"depth":376,"text":195},{"id":210,"depth":376,"text":211},{"id":246,"depth":369,"text":247},{"id":277,"depth":369,"text":278},"how-to","2026-01-12","2026-01-19","Step-by-step guide to database encryption. Implement encryption at rest, in transit, and application-level encryption for sensitive data.",false,"md",null,"yellow",{},true,"Step-by-step guide to implementing database encryption at multiple levels.","/blog/how-to/database-encryption","[object Object]","HowTo",{"title":5,"description":387},{"loc":395},"blog/how-to/database-encryption",[],"summary_large_image","ZUNEkYlyHYLKQ196afp4zOKVTn3PK8ZZkAe0SJzhW0I",1775843928710]