Most database breaches are visible in the database's own audit trail, but in many organisations that trail is switched off, kept on the same server as the data, or never reaches the SOC. Attackers who steal a database or warehouse credential, abuse an application account through SQL injection, or misuse administrator rights all leave logins, permission changes and unusual queries behind. Detection engineers usually start from the log they have, so this guide is organised by log source: what SQL Server Audit, Oracle Unified Auditing, PostgreSQL and pgAudit, MySQL, MongoDB, Amazon RDS Database Activity Streams and Snowflake actually record, how those events map to ATT&CK, which detections are worth building, and how to keep the logs trustworthy when the administrators are in scope. It is for detection engineers, SOC analysts and database administrators. For the wider control set see the Database Security Guide.
Key takeaways
- Nothing is captured until you define it. SQL Server audits are created disabled, pgAudit logs nothing at its default, MySQL Enterprise Audit is an Enterprise Edition feature and Database Activity Streams capture only the policies you activate.
- Start with identity events, not query text. Logins, failed logins, role membership changes, new principals and audit configuration changes are low volume and catch most credential abuse.
- Superusers can blind local auditing. The pgAudit documentation states superusers cannot be reliably audited; ship logs off the host quickly and use platform features that DBAs cannot change, such as Activity Streams.
- Audit logs can leak secrets. Statement logging can record plaintext passwords and sensitive literals, so the audit store needs its own access control.
- Service accounts are the baseline that matters. Application and integration logins are predictable; a service account logging in from a new address or client tool is one of the strongest signals in this data.
What each engine records
| Engine or service | Audit mechanism | Default state | Security-relevant events | Destination |
|---|---|---|---|---|
| Microsoft SQL Server and Azure SQL | SQL Server Audit: server audit plus server and database audit specifications built from action groups | Audits are disabled when created | FAILED_LOGIN_GROUP, SUCCESSFUL_LOGIN_GROUP, SERVER_ROLE_MEMBER_CHANGE_GROUP, SERVER_PRINCIPAL_CHANGE_GROUP, DATABASE_PERMISSION_CHANGE_GROUP, AUDIT_CHANGE_GROUP, BACKUP_RESTORE_GROUP, SCHEMA_OBJECT_ACCESS_GROUP | Audit file, or Windows Security or Application log |
| Oracle Database | Unified Auditing; records in the AUDSYS schema, read through UNIFIED_AUDIT_TRAIL | ORA_SECURECONFIG and ORA_LOGON_FAILURES enabled for new databases | Logon failures, security configuration changes, plus custom policies from CREATE AUDIT POLICY and AUDIT POLICY | Database table; export to SIEM |
| PostgreSQL | Core logging (log_connections, log_disconnections, log_statement) plus the pgAudit extension | log_connections empty, log_statement = none, pgaudit.log = none | Connection receipt, authentication and authorisation; pgAudit classes ROLE, DDL, READ, WRITE, FUNCTION, MISC | stderr, csvlog, jsonlog or syslog |
| MySQL | MySQL Enterprise Audit (audit_log plugin) in Enterprise Edition; community builds rely on other plugins or the general query log | Not installed by default | Connects, disconnects, databases and tables accessed, optional query statistics | audit.log (new-style XML by default, audit_log_format to change) |
| MongoDB | Auditing in Enterprise Advanced and Atlas (M10 and larger) | Off until a destination is set | authenticate, createUser, DDL; CRUD only with auditAuthorizationSuccess | Console, syslog, JSON or BSON file |
| Amazon RDS for Oracle and SQL Server | Database Activity Streams | Nothing captured until audit policies are active | Whatever the engine's policies capture, as JSON DatabaseActivityMonitoringRecord events | Kinesis data stream encrypted with a KMS key |
| Snowflake | Account Usage views such as LOGIN_HISTORY, plus query and access history views | Always on; up to 120 minutes latency for LOGIN_HISTORY | CLIENT_IP, REPORTED_CLIENT_TYPE, FIRST_AUTHENTICATION_FACTOR, SECOND_AUTHENTICATION_FACTOR, IS_SUCCESS | Views queried by the SIEM or a connector; 365 days |
Engine-specific hardening is covered in the Microsoft SQL Server Security Guide, Oracle Database Security Guide, PostgreSQL Security Guide, MySQL Security Guide and MongoDB Security Guide.
Mapping attacker behaviour to database events
ATT&CK v20 ties detections to data components. Database audit trails mostly supply DC0002 User Account Authentication, DC0014 User Account Creation, DC0010 User Account Modification and DC0038 Application Log Content.
| Behaviour | ATT&CK | Events to use |
|---|---|---|
| Password guessing or spraying against database logins | T1110 Brute Force | SQL Server FAILED_LOGIN_GROUP; Oracle ORA_LOGON_FAILURES; PostgreSQL authentication failures with log_connections; Snowflake IS_SUCCESS = FALSE |
| Use of a stolen database or warehouse credential | T1078 Valid Accounts, T1078.004 Cloud Accounts | Successful logins with new source address, new client type, missing second factor |
| Creating a backdoor login or granting privileges | T1136 Create Account, T1098 Account Manipulation | SERVER_PRINCIPAL_CHANGE_GROUP, SERVER_ROLE_MEMBER_CHANGE_GROUP, DATABASE_ROLE_MEMBER_CHANGE_GROUP; pgAudit ROLE; MongoDB createUser |
| Bulk reading of sensitive tables | T1213.006 Databases | SCHEMA_OBJECT_ACCESS_GROUP on sensitive objects; pgAudit object audit through pgaudit.role with pgaudit.log_rows; Snowflake query history |
| Persistence through stored procedures or triggers | T1505.001 SQL Stored Procedures | DDL events: SQL Server schema change groups, pgAudit DDL, Oracle policies on CREATE PROCEDURE and CREATE TRIGGER |
| Backups taken or restored to an unexpected place | T1048 Exfiltration Over Alternative Protocol | BACKUP_RESTORE_GROUP; warehouse unload or copy commands to external stages |
| Destroying or encrypting data | T1485 Data Destruction, T1486 Data Encrypted for Impact | Bursts of DROP, TRUNCATE or mass UPDATE from pgAudit DDL and WRITE or engine equivalents |
| Silently changing records | T1565.001 Stored Data Manipulation | Object-level write auditing on high-value tables |
| Turning auditing off | T1685 Disable or Modify Tools | AUDIT_CHANGE_GROUP (changes to an audit are recorded in that audit); Oracle audit policy changes; gaps in event flow |
A baseline audit configuration per engine
Begin with identity and configuration events everywhere, then add object-level auditing only for crown-jewel tables.
- SQL Server: one server audit with a server audit specification containing
FAILED_LOGIN_GROUP,SUCCESSFUL_LOGIN_GROUP,SERVER_ROLE_MEMBER_CHANGE_GROUP,SERVER_PRINCIPAL_CHANGE_GROUP,LOGIN_CHANGE_PASSWORD_GROUP,AUDIT_CHANGE_GROUPandBACKUP_RESTORE_GROUP; database audit specifications withDATABASE_ROLE_MEMBER_CHANGE_GROUP,DATABASE_PERMISSION_CHANGE_GROUP,DATABASE_OBJECT_PERMISSION_CHANGE_GROUPandSCHEMA_OBJECT_ACCESS_GROUPscoped to sensitive schemas. Remember to setSTATE = ON. UseBATCH_COMPLETED_GROUPsparingly: it records full batch text, and Microsoft's tables disagree on whether it needs SQL Server 2019 or 2022. - Oracle: keep
ORA_SECURECONFIGandORA_LOGON_FAILURESenabled, add policies for privilege grants, user management and DDL on application schemas, and grantAUDIT_ADMINonly to a security role separate from DBAs. Give the SOCAUDIT_VIEWER. - PostgreSQL:
log_connections = 'receipt,authentication,authorization'(PostgreSQL 18 syntax; older releases take a boolean),log_disconnections = on, alog_line_prefixwith user, database, application and client address, andlog_destinationincludingjsonlogorcsvlogfor parsing. Load pgAudit throughshared_preload_libraries, runCREATE EXTENSION pgaudit, setpgaudit.log = 'role, ddl', and usepgaudit.rolefor object auditing of sensitive tables. Setpgaudit.log_catalog = offto cut noise from client tools. - MySQL: on Enterprise Edition, install the
audit_logplugin with filters for connection events and table access on sensitive schemas. On community builds, use a supported audit plugin from your distribution or vendor rather than the general query log, which records everything and is hard to protect. - MongoDB: set an audit destination and filter on
atypevalues such asauthenticate,createUseranddropCollection; enableauditAuthorizationSuccessonly for targeted collections because it adds load. Note the server terminates if it cannot write to the audit destination, so monitor disk space. - Amazon RDS for Oracle and SQL Server: enable Database Activity Streams on the primary, then create and activate the engine audit policies; DAS captures nothing without them.
- Snowflake: pull
LOGIN_HISTORYand query and access history into the SIEM on a schedule that accounts for view latency. See the Snowflake Security Guide.
Rollout order:
- Inventory databases and classify them; pick the crown jewels.
- Enable identity and configuration auditing on all production instances and confirm events arrive in the SIEM.
- Measure volume and performance on a pilot instance for a week before enabling object-level auditing.
- Add object-level read and write auditing for sensitive tables only.
- Build detections, run them in report-only mode for two to four weeks, then promote to alerting.
- Rollback: audit specifications and policies can be disabled individually, so remove the noisiest object policy first rather than turning auditing off.
Detection use cases worth building
- Service account anomaly: an application or integration login from a source address, host or client type not seen in the last 30 days. Service accounts have stable patterns, so this is high fidelity. See the Database Credential and Access Security Guide.
- Human login without MFA on a warehouse: Snowflake
SECOND_AUTHENTICATION_FACTORis null for a human user. TreatREPORTED_CLIENT_TYPEas a hint only, because it is self-reported. - Failed then successful login: many failures for one login followed by a success from the same source.
- Privilege change outside change windows: any server role, sysadmin, DBA or superuser grant, and any new login, correlated with your change tickets.
- Audit tampering: any
AUDIT_CHANGE_GROUPevent, Oracle audit policy change, pgAudit setting change, or a source that stops sending for longer than its normal interval. - Bulk access: row counts or result volumes well above the baseline for that account on sensitive tables, or warehouse unloads to new external locations.
- Destructive bursts: several
DROPorTRUNCATEstatements in a short window from one session. Pair this with cloud control plane logs, because deletion can bypass the database entirely: in the PocketOS database deletion an AI coding agent removed a production database and its backups through a single platform API call with an over-scoped token.
Correlate database events with identity provider and network logs: a database login is far more suspicious when the same account had an impossible-travel sign-in an hour earlier.
Tuning noise and volume
Database audit volume grows with workload, and the pgAudit documentation warns that logs can exhaust disk space and add latency to busy transactional systems. Plan for this rather than discovering it in production.
| Lever | Effect | Trade-off |
|---|---|---|
| Audit identity and configuration events only | Very low volume, high signal | No visibility of what data was read |
Object-level auditing on named sensitive tables (pgaudit.role, scoped SCHEMA_OBJECT_ACCESS_GROUP, Oracle policies on specific objects) | Read and write visibility where it matters | Needs an accurate data classification to choose the tables |
Exclude catalogue and monitoring noise (pgaudit.log_catalog = off, filters for monitoring logins) | Removes high-volume, low-value events | Attackers who masquerade as the monitoring account are harder to see; baseline that account tightly |
Log row counts rather than full statements (pgaudit.log_rows) | Enables bulk-access detections with less sensitive content | Less detail for forensic reconstruction |
Full statement capture (BATCH_COMPLETED_GROUP, log_statement = all) | Complete record for investigations | Highest volume and risk of capturing secrets; use for short, targeted periods |
Small versus large organisations: a small team with a handful of databases should enable identity events everywhere and object auditing on one or two critical tables, then rely on managed services such as Activity Streams or warehouse views. A large estate needs a central audit standard per engine, deployed as code, with exceptions approved and recorded.
Keeping the audit trail trustworthy
- Separate duties: the people who administer the database should not administer its audit. Oracle's
AUDIT_ADMINrole and RDS Activity Streams, where the documentation states DBAs cannot alter the audit policy or remove audit logs, support this. - Ship off-host fast: pgAudit is best-effort and not transactional, and MongoDB may lose buffered events if the server stops. Forward continuously rather than in nightly batches.
- Protect sensitive content: PostgreSQL warns that logged statements can contain plaintext passwords; keep
log_file_modeat 0600 and restrict SIEM indexes holding database statements. - Retain long enough to scope: breach scoping often needs months of login history; align retention with your notification obligations. See the CIS Control 8 Audit Log Management Guide.
- Record enough context: user, client address, application name, database and object for every event, so an analyst can answer who, from where and what without going back to the server.
Measuring coverage
- Share of production databases sending identity and configuration audit events to the SIEM (target: 100 percent).
- Share of crown-jewel tables with object-level read auditing.
- Median delay from event to SIEM availability, per engine.
- Number of service accounts with a baselined behaviour profile.
- Silent-source alerts raised and closed within one working day.
- Detections for the techniques in the mapping table that have been tested with a safe simulation in the last 12 months.
Framework mapping
| Requirement | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | CIS Controls v8.1 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|---|
| Define and generate database audit events | AU-2, AU-12 | PR.PS-04 | Control 8 | A.8.15 |
| Content of records (who, where, what) | AU-3 | PR.PS-04 | Control 8 | A.8.15 |
| Review and correlate | AU-6, SI-4 | DE.AE-02, DE.AE-03, DE.CM-03 | Control 8, Control 13 | A.8.16 |
| Protect audit data from administrators | AU-9 | PR.PS-04 | Control 8 | A.8.15 |
| Retention for investigation | AU-11 | PR.PS-04 | Control 8 | A.8.15 |
| Failed logon handling | AC-7 | DE.CM-03 | Control 6 | A.8.5 |
Common failure patterns
- Auditing created but never enabled. SQL Server audits and specifications are off until
STATE = ON; teams pass a configuration review and capture nothing. - Everything or nothing. Turning on full statement logging fills disks and is switched off within a week; start with identity events.
- Logs stay on the database host. An attacker with superuser rights, or a ransomware operator, removes the evidence along with the data.
- No baseline for service accounts. Shared application logins used by people and tools make anomaly detection impossible.
- Warehouse logs ignored. Cloud data platforms log well, but the views are never pulled into the SIEM.
- Secrets in the audit store. Statement text containing passwords ends up readable by everyone with SIEM access.
Lessons from incidents
In the Snowflake customer breaches of 2024, a threat group used infostealer credentials to log in to customer accounts that had no MFA and no network restrictions, then stole data from around 165 organisations. The behaviour was visible in exactly the fields this guide recommends watching: logins from unfamiliar addresses and commercial VPNs, new client tools, no second factor, and large data copy operations to temporary stages. The lesson is that warehouse login and query history should be in the SIEM with alerts on those conditions before an incident, not exported afterwards for scoping. For the response side see the Database Breach Response Playbook.
Practitioner checklist
- List every production database and warehouse with its audit mechanism and current state.
- Enable login, failed login, principal, role and audit-change events on all of them.
- Add object-level auditing for crown-jewel tables after a volume test.
- Forward audit events off-host continuously with user, client address, application and object fields.
- Separate audit administration from database administration.
- Baseline service account logins and alert on new sources and client types.
- Alert on any audit configuration change and on silent sources.
- Restrict access to audit stores that contain statement text.
- Pull cloud warehouse login and query history into the SIEM on a schedule.
- Test detections with safe simulations at least yearly.
Standards and references
- Microsoft Learn: SQL Server Audit Action Groups and Actions
- Oracle: Introduction to Auditing (Database Security Guide 19c)
- PostgreSQL: Error Reporting and Logging
- pgAudit: PostgreSQL Audit Extension
- MySQL: MySQL Enterprise Audit
- MongoDB: Auditing
- AWS: Overview of Database Activity Streams
- Snowflake: LOGIN_HISTORY view
Related NHI Mgmt Group resources: Database Security Guide · Database Breach Response Playbook · Database Credential and Access Security Guide · CIS Control 8 Audit Log Management Guide