Chapter 28: Database, Data and Storage Security
28.1 Why.
Welcome to Chapter 28.
I am Ravindra Bagale.
Raju had no engineering degree. He had a database that answered anyone who knew the address, a bucket with a name he thought was secret, and a backup on the same account as the application. He said the data was "just a lab." Guru-ji agreed it was a lab, and still moved the chair closer.
"Data is the reason the other controls exist," Guru-ji said. "Identity without a data boundary is a key to every room. A backup that the same key can delete is not a backup. A log that contains the secret is a second database you forgot to lock."
Raju looked at the bucket policy and saw a star where a name should be. "Suppose we are Paytm or PayFast and a dump of tokens sits next to application logs. Suppose we are Swiggy or Zomato and an order export is shared to a personal account so a report can be finished at home. Suppose we are Amazon and a snapshot is readable by every account in the organization because the default was friendly. Suppose we are WhatsApp-shaped messaging and the store keeps more history than the product promised. Suppose we are BHIM UPI and the connection string is in Git because the rotation felt risky. Suppose we are Netflix and a recommendation store is private, but the debug bucket beside it is not. Suppose we are Infosys and the client asks where personal data lives, who can query it, and how a deleted user becomes deleted. If you cannot point at the store, you cannot protect it."
Data is what attackers want
Most breaches end with data leaving a database or a storage bucket. Protecting data at rest, in transit and in logs is the core job.
Exposed Elasticsearch and MongoDB
Researchers keep finding databases open to the internet without passwords. In 2017 attackers wiped and ransomed tens of thousands of open MongoDB instances.
Aadhaar-linked leaks
Several Indian reports over the years found personal data exposed through misconfigured government and partner portals. They show that data rules must cover every copy, not only the main database.
28.2 What.
Classification and ownership. Name the data before you name the product. Public, internal, personal, payment, secret. Each class has an owner and a purpose. A column you cannot classify does not ship. Payment card data pulls a fence around every system that touches it, which is why Paytm-shaped and PayFast-shaped designs keep card numbers off their own disks and use a specialist token. Orders for Swiggy or Zomato are personal data even when they look ordinary. Message content is personal. A lab uses synthetic rows that look real and are not. Write the class on the catalog row from Chapter 26.
Encryption at rest and in transit. Transit encryption means the client talks to the database and to object storage over a protected channel, and you verify the peer. Rest encryption means the disk or the object is encrypted with a key you can rotate and disable. Encryption does not replace access control. A bucket that anyone can read is still readable if the objects are encrypted with a key the same policy hands out. Manage keys in a service built for keys. Do not store the key beside the data in the same repository. Separate the duty: the application role can use the key to decrypt what it must, and cannot export the key material or change who else may use it.
Identity and least privilege. People do not share a database superuser. Applications do not use the superuser either. Create a role that can run the statements the service needs and nothing else. A payment worker might insert and update payments in one schema and might not read the identity table. An analyst role might read a masked view and might not write. Admin rights live in a break-glass role that is logged and expires. Cloud database authentication can map a workload identity to a database user so the password is not a long-lived string in an environment variable. Where a password remains, store it in the secret store and rotate it. Network isolation matches the role. The database sits on a private endpoint. It does not listen on the internet because a firewall rule was wide on a Friday.
Injection, as a defense. SQL injection is a class of bug where data becomes a command. The defense is structural. Use parameters. Use an interface that does not build queries by gluing strings. Give the application role no right to run arbitrary administration commands. Review code for string-built queries the way Chapter 5 taught review. This chapter will not show malicious statements. You do not need a sample attack to insist on parameters. If a report tool must accept a filter, allow a list of column names you wrote, not a fragment the caller wrote.
Rows, columns, masks, and tokens. Row-level rules limit which customer a query can see even if the application is confused. Column-level rules keep a national identifier out of a role that only needs a display name. Masking shows a partial value to support staff. Tokenization replaces a sensitive value with a stand-in, and the real value lives in a smaller system with a smaller list of callers. Use these when the data class demands them. Do not collect the sensitive value "just in case" and then spend a year masking it. The cheapest control is not having the column.
Backups and destructive programs. A backup is a copy with its own identity. It is encrypted. It is not deletable by the application role. It is tested by restore, on a schedule, into a lab or a quarantine account. Ransomware resilience is this sentence made real: the credential that runs the app cannot erase history. Immutable retention for a defined window, plus a second copy in an account the app cannot reach, is the pattern. A restore that brings back public access and old keys is a second incident. Check the configuration after restore, not only the rows.
Object storage. Buckets and blobs need an explicit block on public access at the account, not only a hope in one policy. Listing, reading, and writing are different rights. A world-readable object is a publication. A world-listable bucket is an index you did not mean to print. Access points and private endpoints keep traffic off the public internet. Log the reads of sensitive prefixes. Lifecycle rules delete or archive on purpose. An orphaned export from a notebook is still data. Netflix-shaped media may be meant for public delivery through a controlled edge, while the origin bucket stays private. Do not confuse the edge cache with the origin.
Retention and deletion. Keep data for a purpose and a time, then delete it, including backups when the duty says so. A deletion that leaves five copies is not a deletion. Personal-data requests are easier when classification and ownership are real. Logs are data. A log that captured a password or a card number inherits the stricter class. Chapter 22 told you not to log secrets. This chapter audits whether you did.
Activity evidence. Database audit logs should show admin logins, schema changes, and unusual bulk reads. Ship them off the database server so a person who can edit rows cannot quietly edit history. Alert on a new superuser, a grant that widens a role, and a bulk export from a production store. Tune the alert so a known nightly job does not train you to ignore it. Infosys-shaped evidence packs include a sample of this log with secrets redacted.
Exfiltration is a shape, not a tool. Watch for a client that reads far more rows than the feature needs, a role that suddenly queries every table, a snapshot shared outside the account, a bucket policy that changed at an odd hour. The response is Chapter 23: contain, preserve, rotate, tell. Prevention is least privilege, private networks, alerts, and not building a single role that can read everything "because reports are easier."
Keys and connection secrets. Rotate database credentials and encryption keys on a calendar. Practice the rotation in the lab before you need it on a bad day. Dual control helps: one role requests, another approves, the workload identity picks up the new secret without a human pasting it into a file. A connection string in a chat log is a leak. Treat it as one.
A teaching sample: a small role. Lab only. Names are fake.
-- own lab database, synthetic data
CREATE ROLE lab_orders_app NOINHERIT LOGIN;
GRANT SELECT, INSERT, UPDATE ON orders TO lab_orders_app;
-- no GRANT on credentials, no superuser, no drop database
The application connects as lab_orders_app. Your break-glass admin is a different role you do not put in the service.
Ravindra Bagale's Tip
If the application uses a superuser, every bug is an admin bug. Fix the role before you buy another scanner.
Practice task
In a lab database you own, create a least-privilege role, enable a private-only listener, turn on audit for grants, and confirm a storage bucket blocks public access. Use synthetic rows. Do not connect to Paytm, Amazon, or any database you do not own.
Seminar notes. Raju wanted to encrypt the database and feel finished. Guru-ji asked who could still select the rows. Encryption answers a stolen disk. Authorization answers a stolen role. You need both, and you need them on the backup and on the log. Walk the lifecycle out loud. Collect only what the feature needs. Classify it. Store it in a private place with a small role. Transmit it on a verified channel. Mask it for the support-shaped role. Retain it for a written time. Delete it, including the copies you forgot, such as exports, snapshots, and debug logs. If a step has no owner, the step will not happen. BHIM UPI-shaped and PayFast-shaped data deserve the strict walk. A marketing image does not. The reference is the same shape with different tightness, not a different theory.
Key rotation is a drill, not a slogan. In the lab, create a second key version, point the app at it, read a synthetic row, then disable the old version and prove the app still works only because it moved. If the app cached the old key in a config file, you have found the real design. Fix that before you talk about hardware modules. The application role may use a key. It may not administer who else uses keys. That split is separation of duties inside the data plane.
Ransomware resilience is the backup identity. Say it in one line on the catalog card. The app role cannot delete backups. The backup role cannot change application code. A third, break-glass role can restore and is logged. Test restore quarterly in lab time, which for you may mean before this chapter ends. A restore that revives a public bucket and a superuser connection string is a failed test even if the rows look right. Check configuration after the rows.
Exfiltration is a pattern you can alert on without studying criminals. A nightly export of one known report is normal if you named it. A role that suddenly scans every table, or a snapshot shared outside the account, is not. Alert on the grant and on the share. Tune the known report so the page means something. Object storage follows the same grammar. Public access blocked at the account. Prefix policies. Access logs shipped away. Lifecycle so a one-off export dies. Amazon-shaped origins for public media still keep the origin private and the edge deliberate. Do not learn that lesson by making a bucket public to see what happens on a real account. Learn it when your lab policy denies you.
Tokenization and row rules are for when you must hold something sensitive and most callers must not see it. Prefer not holding it. When you must, the map from token to value lives in a smaller service with a smaller allow list. Column grants and masked views let a support role do the job without a copy of the raw table into a spreadsheet. The spreadsheet is how data walks out of an otherwise careful design. Give people a paved, masked path so they do not invent the spreadsheet. Zomato-shaped and Swiggy-shaped support tools should query views, not production tables as a superuser, even in a story. In your lab, make the view and refuse the table.
One more pass. Write the connection path as a sentence a new teammate can audit. The app runs as a workload identity. The identity may authenticate as lab_orders_app. That role may use three statements on one schema. The secret store holds any remaining password and rotates it. The network path exists only from the app subnet to the private endpoint. Admin tools jump through the bastion idea from Chapter 8, with a second factor and a log. If any clause is false in the lab, the design is not done. Activity monitoring is the clause that keeps the others honest: a grant, a new role, and a bulk read of the synthetic table should each leave a line in the archive the app cannot edit. That line is evidence. A screenshot of the console is not.
28.3 How.
- List every lab store: databases, buckets, disks, and log folders. Write the data class and owner on each.
- Replace any real personal data with synthetic rows before you continue.
- Turn on encryption in transit for the lab database and refuse plain connections.
- Turn on encryption at rest with a key the application cannot export.
- Create an application role that can touch only the tables the lab app needs.
- Remove superuser rights from that role and from any connection string in the repo.
- Put the remaining secret in a lab secret store and rotate it once so you know the path.
- Place the database on a private address and confirm it does not accept connections from the open internet.
- Rewrite one lab query to use parameters if it was built by gluing text. Do not invent attack strings.
- Add a masked view for a pretend support role and prove the support role cannot select the raw column.
- Block public access on the lab bucket at the account setting, then try a public read and expect failure.
- Write a bucket policy that allows only the app role to read one prefix.
- Turn on data-access logs for that prefix and ship them off the bucket.
- Take a backup with a role the application does not have.
- Delete a synthetic row, restore into a second lab database, and confirm the row returned and public access did not.
- Set a retention rule so a synthetic export expires.
- Add an alert for a new grant of wide rights, then perform that grant and watch the alert.
- Revoke the test grant.
- Document backup location, restore time, and who can delete backups.
- Add the data class to the service catalog row.
- Review logs for accidental secrets and redact the lab sample you will keep as evidence.
- Stop. Do not scan buckets, dump data, or test logins on Swiggy, Paytm, Amazon, BHIM UPI, WhatsApp, Zomato, Netflix, Infosys, PayFast, or any store you do not own.
Protect the data layer
- Keep databases in private subnets; no public IP.
- Turn on authentication and TLS.
- Give each app its own least-privilege user.
- Encrypt at rest with managed keys.
- Keep passwords hashed with bcrypt or argon2, never logged.
- Test restores from backups every month.
MySQL
bind-address to private IP; require_secure_transport=ON; separate users per app.
PostgreSQL
pg_hba.conf with scram-sha-256; ssl=on; row-level security when needed.
MongoDB
Enable authorization; never bind to 0.0.0.0 publicly; use TLS.
Redis
Protected mode, requirepass or ACLs, private network only.
Amazon S3 / Azure Blob / GCS
Block public access, default encryption, versioning.
28.4 A real public incident.
Name. Facebook's disclosure of passwords stored in readable form in internal logs.
Date. Facebook published the account on 21 March 2019.
Source. Facebook Newsroom, "Keeping Passwords Secure," 21 March 2019. Facebook described passwords stored in plaintext in internal data storage systems, affecting Facebook Lite, other Facebook users, and Instagram. The company said it fixed the storage and found no evidence of abuse inside its investigation. This chapter does not repeat any method for recovering passwords.
What happened. Passwords are supposed to be stored so that a copy of the database does not reveal the password itself. Facebook said a bug caused some passwords to be written in readable form into internal logs. That is a data-classification failure as much as a cryptography failure. Logs became a second password database. Access to logs became access to secrets. The company described notification to affected people and a reminder about features that watch for suspicious logins. For you, the engineering lesson is to decide what a log is allowed to hold, to protect log storage with the same seriousness as the primary store, and to alert when a secret-shaped string lands in a log pipeline. A scanner on the application repo would not have seen a runtime log. Runtime data needs its own control.
How to stop.
- Stop the process that is writing secrets into logs or into a readable store.
- Restrict access to the copies that already exist and preserve them for a lawful investigation.
- Purge the secret-bearing logs on a schedule you can defend, after evidence needs are met.
- Rotate the affected credentials so a copy is no longer enough.
- Tell the people whose secrets were exposed, in the terms the duty requires.
How to prevent it again.
- Hash passwords with a slow, modern password hash. Do not invent your own.
- Treat logs as a data store with classification, access control, and retention.
- Alert on secret-shaped values in logs in a lab test you control.
- Give applications least privilege so a log reader is not a database superuser.
- Review new debug flags before they reach production. Debug is how secrets leak.
- Practice on synthetic passwords in a lab you own.
This is for learning only, and only inside a system you are allowed to test.
28.5 A simple picture.
Classify, then lock the network, the role, the key, and the backup. Public access is a separate door that should already be shut.
28.6 A short animation.
A bucket briefly shows a public mark. The account block shuts it. The log line remains.
28.7 Conclusion.
Raju's lab role could insert an order and could not drop the database. The bucket refused the public read. Guru-ji closed the notebook. "In Chapter 29 the server disappears and the event remains. A function still has an identity. A queue is still a door. Do not let the word serverless mean permissionless."
- Classify data, then give each role the smallest query rights.
- Encrypt, and still do access control. Encryption is not a public bucket.
- Backups need a different identity from the application.
- Facebook's 21 March 2019 note is the lesson that logs are a data store.
- Own lab only.
END OF CHAPTER 28 MASTER CLASS
| Risk | Control |
|---|---|
| Open database | Private network + auth |
| Passwords in logs | Log filters, hashing only |
| Lost data | Tested backups and versioning |