Database security is the set of controls that keep the data in a database confidential, accurate and available: which hosts can reach the server, which accounts can log in, what each identity can read or change, how connections and backups are protected, and what gets recorded along the way.
Most of those controls are keyed to a database account, and that works as long as an account means one person. Often it doesn’t. A BI tool connects as one service account for every dashboard viewer, and a coding agent tends to run with a database password pasted into its environment, so the database ends up applying the right rules to the wrong name.
Why is database security important?
Database security matters because the database holds the records an attacker is after: customer accounts, payment details, health data, secrets. IBM’s Cost of a Data Breach Report 2026 puts the average breach worldwide at USD 4.99 million, which IBM calls “a 12% increase over last year and a record high”. Database breaches can also look like ordinary work while they happen, since a valid login reading a table is what the database sees all day.
Regulators write the same controls into law. The HIPAA Security Rule’s technical safeguards ask systems holding electronic health information for access control, audit controls, integrity protection, person or entity authentication, and transmission security. Each one maps to a control further down this page, so database privacy and security work usually ends up as one project with two sets of auditors.
What are the biggest database security threats?
The biggest database security threats are stolen or shared credentials, privileges nobody uses anymore, insiders and stale accounts, injection through the applications and BI tools in front of the database, misconfigured or unpatched servers, exposed connections and backups, and AI agents querying production with borrowed access. In most of them the database accepts a login and runs queries that login was allowed to run.
Stolen and shared credentials
A database password in a .env file, a CI variable or a wiki page works for anyone who finds it, from anywhere the network allows. The OWASP cheat sheet says credentials should never be stored in application source code, and that’s the easy half. Shared logins cause the harder half: when the whole data team connects as analytics, a leaked password and a colleague look identical in the log. Privileged access management covers the accounts where that hurts most.
Excessive and standing privileges
Grants pile up. A migration needed ALTER once, an incident needed write access for an afternoon, and both grants are still there a year later. A role that can SELECT from the customers table can also read every row of it in one query, and an admin role held around the clock is available to whoever steals the laptop it’s configured on. Removing privileged access that nobody is using right now is the idea behind zero standing privileges.
Insider misuse and stale accounts
Insider threats include the careless as well as the malicious: an engineer copying a production table to a laptop to debug, a contractor whose access outlived the contract. Offboarding removes the SSO account, while a native database login or a scheduled job created under that person can keep running. ManoMano’s case study lists this among its results: “Identified former employees with active Metabase jobs”.
SQL injection and compromised apps or BI tools
Applications and BI tools sit in front of the database with accounts of their own, so a flaw in either hands an attacker that account’s reach. SQL injection is the classic attack. Our Field CISO wrote about a Metabase flaw scored CVSS 10.0: someone who had never logged in could inject SQL and make themselves a Metabase admin, which comes with the access Metabase holds to every connected database. Parameterized queries fix injection in your own code. For a third-party tool, what limits the damage is how little its database account can do.
Misconfiguration, human error and unpatched software
Default accounts left enabled, a database port reachable from the internet, a test instance restored from a production snapshot: misconfiguration is usually somebody’s shortcut that nobody went back to. Unpatched database software adds known vulnerabilities on top, from privilege escalation bugs to the occasional zero day, and database upgrades get postponed because they need a maintenance window.
Exposed connections and backups
A client that connects without TLS sends queries and results in clear text across every network hop. Backups are the quieter exposure. They’re full copies of the database in a storage bucket, sometimes with looser permissions than the database itself, and ransomware goes after them because they’re what you’d restore from.
AI agents and LLM tools querying production
Coding agents, analytics agents in Slack and internal tools with a model behind them now send SQL to production databases. The quickest setup gives the agent a database password or a long-lived key, so every session holds standing access and the log shows a service account instead of the agent or the person who launched it. An agent also acts on text it has just read, so an instruction hidden in a ticket or on a page it was asked to summarize (prompt injection) can steer its next query. Agentic AI security maps those risks across the other tools an agent uses.
Types of database security
There’s no standard list of types. Some guides count four, others five, and they rarely agree on which. Splitting database security by layer works better, because each layer answers a different question and a strong one doesn’t make up for a weak one.
| Layer | Question it answers | Controls |
|---|---|---|
| Physical and host | Who can touch the server and its operating system? | Data center access, a hardened and patched OS, protected backups |
| Network | Which hosts can reach the database port? | An isolated segment, firewall rules, no public endpoint |
| Access | Who is connecting, and what may they do? | Authentication, least privilege, just-in-time access |
| Data | What does a reader get, and what does a thief get? | Encryption in transit and at rest, masking |
| Audit | Who did what, and can you show it later? | Query logs tied to an identity, native audit logs |
On a managed cloud database, the physical layer and most of the host layer belong to the provider. The other three stay with you.
Database security controls
The OWASP Database Security Cheat Sheet is the closest thing to a shared baseline, and most of the controls below follow it. Where a control depends on knowing who is connecting, we’ve added how that works when the client is a BI tool or an AI agent, which the baseline doesn’t go into.
Network isolation
Put the database server on an internal network segment and let only the hosts that need it reach the port. OWASP adds that a thick client, such as a desktop app, should go through an API that enforces access control instead of connecting to the database directly. On a managed service, that means no public endpoint and security groups scoped to the application’s hosts instead of a whole VPC range.
Authentication without shared credentials
OWASP asks for authentication on every connection, for each account to be “Used by a single application or service”, and for credentials to stay out of source code. Database credentials in a vault are a big step up from credentials in a repository, but whoever reads them out of the vault still holds a working password.
A proxy that signs in on the user’s behalf removes that step. In Formal, the database password belongs to a Native User, and the secret can sit encrypted in Formal, live in an environment variable on the Connector, or come from a hook that runs when the connection opens. The docs put it plainly: “Formal identities never need to know or send these upstream credentials.” A CEL expression picks the Native User for each identity, so an analyst can land on a read-only role and the on-call engineer on a wider one without either of them seeing a password. With SSO and MFA at the identity provider in front, a stolen laptop carries an access token that expires instead of a password that doesn’t.
Database access control and least privilege
NIST defines least privilege as “A security principle that a system should restrict the access privileges of users (or processes acting on behalf of users) to the minimum necessary to accomplish assigned tasks.” The words in parentheses cover a BI tool, a scheduled job and an AI agent.
In the database, OWASP’s version is concrete. Use separate accounts for development, UAT and production. Never let an application connect as the built-in root, sa or SYS account. Grant permissions at the table, column or row level where the engine supports it, and review accounts and permissions regularly. PostgreSQL’s row security policies, for example, restrict per user which rows a query can return or change, and the Postgres page covers how they behave behind a proxy.
Grants stop at the statement type, though. A role allowed to UPDATE a table can update every row of it, from any client, at any hour. A policy evaluated on each query can also look at the environment, the statement and the person’s groups. This one is from Formal’s policy examples:
package formal.v2
import future.keywords.if
request := {
"action": "block",
"type": "block_with_formal_message",
"reason": "Write operations not allowed in production"
} if {
input.resource.environment == "production"
input.sql_query.statement_type in ["INSERT", "UPDATE", "DELETE", "DROP"]
}
The block comes back to the client with that reason as its message. Notion’s case study describes the same move across 100+ RDS instances: “Eliminated broad, long-lived permissions through fine-grained, group-based rules”. Database access management in Formal PAM applies policies like this one to every database behind the Connector.
Just-in-time access
Some access should exist only while a task needs it: write access to production during an incident, or one read of a regulated table for an investigation. Just-in-time access grants it for a set time after someone approves and removes it on its own. In Formal, a policy suspension exempts one user or group from a blocking policy, either once or until it expires, and a workflow can create the suspension after an approver says yes in Slack.
Encryption in transit and at rest
OWASP asks for encrypted connections only, TLSv1.2 or later with modern ciphers, and clients that verify the server’s certificate. Require TLS on the server so that a client which forgets to ask for it is refused instead of served in clear text.
A proxy in the path means two connections to encrypt. The Formal Connector serves TLS to clients with a certificate that Formal creates and renews, or one you manage on your own hostname. Each database then has its own TLS setting for the Connector’s upstream connection: disable, insecure-skip-verify, insecure-verify-ca-only or verify-full. We’d use verify-full for anything in production. There’s also a separate FIPS 140-3 build of the Connector.
Encryption at rest is a job for the database engine, the storage layer or the cloud provider: transparent data encryption, encrypted volumes, keys held in a key management service. It protects the disk, the snapshot and the backup file. It does nothing against a valid login, because the database decrypts rows for anyone allowed to read them. Formal doesn’t encrypt data at rest.
Data masking
Masking covers the reader that encryption lets through: someone authorized to query a table who needs the shape of the data but not every value, like a support engineer who gets a partially redacted email address while a fraud analyst with a reason gets the full one. Mask by classification label. A rule written against table names misses the sensitive column that turns up in a new table next quarter, and a label-based rule catches it once discovery has labeled the column. Techniques, per-user rules and where masking stops are on the data masking page.
Auditing and monitoring
Native audit logs record the database role. A query from a shared BI account shows that account, and the viewer who opened the dashboard isn’t in the line at all. Keep native audit logging on anyway, since it also records sessions that went around whatever sits in front of the database. Then record each query against the person or agent who sent it, somewhere outside the database that its admins can’t edit, and forward it to the SIEM your security team already reads. What each record should hold, the three places a monitor can sit and which alerts to start with are covered in the database activity monitoring guide.
Hardening and patching
OWASP’s hardening list starts below the database server, with an operating system built from a published baseline (it names the CIS Benchmarks and Microsoft’s Security Baselines). On the database itself, “Install any required security updates and patches”, run the database service under a low-privileged OS account, remove default accounts and sample databases, and disable stored procedures and features you don’t need (on SQL Server, the cheat sheet names xp_cmdshell and CLR execution). It has engine-specific notes for SQL Server, MySQL and MariaDB, PostgreSQL, MongoDB and Redis.
Backups
Back up on a schedule and give the backups the same care as the database. OWASP’s wording is that they should be “protected with appropriate permissions, and ideally encrypted”. Keep at least one copy somewhere an account that can drop the production database can’t delete, and restore from it on a schedule so you find out early when a restore doesn’t work.
Database security for AI agents
An agent that needs data usually gets whatever password is easiest to hand it. Formal’s docs walk through the alternative with Devin in a setup guide. Devin is a cloud coding agent, and the same shape works for other agents.
Devin signs in to Formal with a short-lived OIDC token issued for the session. That token resolves to a devin machine user, with whoever started the Devin session recorded as the end user. Nothing gets through at first. A policy blocks that machine user by default, so Devin’s first query fails with “Devin is blocked by default; request JIT access”, and Devin asks for access with a reason and a duration; the requester approves in Slack, and the approval creates a policy suspension for that session. The approval workflow runs as a separate machine user, “so Devin can never create its own suspension.” In the logs, the session shows the devin machine user with the requester’s email as the end user.
None of that is specific to Devin. The agent never receives a secret it could keep, it holds no standing access between tasks, and every query carries two names. Log entries also mark each identity as human or machine in user.formal.user.type, which lets you review agent traffic as a group of its own. That is NIST’s “processes acting on behalf of users” in practice: the agent’s access is sized to the task its person gave it, and expires with it. How agents prove who they are without static secrets, and how the person behind them gets recorded, is the subject of the AI agent identity guide.
Detection works differently. The AI session monitors in Formal cover SSH and Kubernetes, and database queries get no AI risk score, so on databases you detect misuse from the query log and from your policies.
Database security best practices checklist
These are the database security best practices we’d check first, roughly in this order; the database security checklist below can double as an audit worksheet. The items on network, authentication, grants, hardening and backups follow the OWASP cheat sheet.
- List every account that can reach each database (people, service accounts, BI tools, jobs and AI agents) and give each one an owner.
- Give every person, application and agent its own identity, and retire shared logins.
- Keep database credentials out of code, CI variables and agent environments.
- Grant each role the least privilege it needs, at table, column or row level, and review grants on a schedule.
- Make write and admin access to production just-in-time, with an approver and an expiry.
- Keep databases on an internal network segment with no public endpoint.
- Require TLS 1.2 or later and verify certificates on every leg of the connection.
- Encrypt storage and backups at rest, and mask sensitive columns for readers who don’t need the raw values.
- Record every query against the person or agent behind it, and keep native audit logs on.
- Patch the database and its OS, remove default accounts, and scan configuration and vulnerabilities against a baseline.
- Back up on a schedule to storage the production credentials can’t delete, and test restores.
For regulated data, keep the evidence as you go: who reviewed access, when, and what changed as a result. Database security and compliance audits ask for that record more than for the controls themselves.
Cloud database security
Moving to a managed database service hands the provider the bottom layers: the data center, the hosts, the operating system and, for most services, engine patching. Accounts, grants, network exposure, encryption settings, logging and what happens to the data all stay with you. The common cloud database security issues sit on your side of that line: a snapshot shared publicly, a security group open to 0.0.0.0/0, an IAM role far broader than its job.
A managed service also takes away the host, so there’s nowhere to install monitoring software. Database security in cloud computing leaves two places for a control: the service’s own features, logs and APIs, or the connection path. Native controls differ by engine and edition. Snowflake’s masking policies need Enterprise Edition or higher, while PostgreSQL has row security policies built in. The Snowflake and Postgres pages go through what each engine does natively and what a proxy adds on top.
Database security tools and solutions
Database security solutions are sold by category, and no single product covers every layer. Most database security software falls into one of these:
| Category | What it covers | Where it runs |
|---|---|---|
| Native DBMS controls | Roles, grants, row-level security, native audit logs, transparent data encryption | Inside the engine |
| Database access proxies and PAM | Per-identity sign-in, credentials users never hold, query-level policy, just-in-time access | In the connection path |
| Database activity monitoring (DAM) | Query records, alerts and access reports | Engine, host or connection path |
| Data discovery and masking | Finding and labeling sensitive columns, masking results | Scans, plus the engine or the connection path |
| Encryption and key management | Encryption at rest, key storage and rotation | Storage layer and key management service |
| Vulnerability and configuration scanners | Missing patches, weak settings, exposed instances | Outside, on a schedule |
| Database and web application firewalls | Blocking known-bad statements and requests, such as injection patterns | In front of the engine or the application |
| Backup and recovery | Copies you can restore after deletion or ransomware | Separate storage |
Enterprise database security usually combines the engine’s own controls with a few of these. Ask each tool where it gets its identity from: one that only sees database roles inherits every shared account you have. Formal is in the access proxy row. Formal PAM runs on a Connector that detects each client’s wire protocol on a single port, and the same Connector writes the query log a DAM program reads.
How does Formal approach database security?
Formal approaches database security from the connection path. The Formal Connector is a protocol-aware reverse proxy you deploy in your own infrastructure, between your users and your databases. People and agents connect with their own identity through their usual client, and the Connector authenticates them, evaluates policy on each query and its results, and logs it.
Engineers keep psql, mysql and mongosh; formal connect starts a local listener and signs them in. On the database side, the Connector authenticates as a Native User, so no person or agent holds the upstream secret. Policies are written in Rego and apply when a session opens, to each request and to each response, where they can block, mask, filter or rewrite: block DDL in production, add a LIMIT, mask every column labeled as an email address. Before a policy enforces anything, dry-run mode records what it would have done. Every query is logged with the identity, the query as received and as sent, and the policies that fired, and the logs can be exported to object storage on AWS, Google Cloud or Azure, or to Datadog, Splunk and Panther. The Connector covers Postgres, MySQL, MariaDB, Redshift, MongoDB, Snowflake, BigQuery, ClickHouse, DynamoDB and Redis.
Anything that reaches the database without passing the Connector is invisible to Formal, so close the direct network routes. Formal doesn’t encrypt data at rest. And it doesn’t document automatic rotation of upstream database passwords: rotating one means changing the Native User’s secret, or letting a connection-time hook read the current value from your secrets manager.
Cursor secures PostgreSQL, Redis, ClickHouse, SSH and Kubernetes through the Connector, per its case study.
“With Formal, we can finally manage least privilege access across our entire infrastructure through a single control plane.”
— Tom Daniels, CISO @ Cursor
The query-level controls above are database access management in Formal PAM, and masking has its own data masking page. If you’re starting from shared logins, we’d go in this order: one identity per person and agent, then per-query policy, then just-in-time access for what’s left, because each step relies on the one before it to know who is asking.