Cybersecurity

Database security that survives an audit: SOX controls, audit logging and least privilege

When a database holds financial records such as contracts, billing and accounting entries, it falls within the scope of SOX controls, and the questions an auditor asks are surprisingly consistent. Across several engagements, putting these controls in place reduced our measured security-risk exposure by about half. Most of it is discipline rather than tooling.

1. Know who can do what

Least privilege starts with an inventory. List every account, what it's for, who owns it and what it can do. Then remove what isn't needed.

  • Named accounts for people, never shared logins. Shared accounts make every audit log entry meaningless.
  • Roles, not direct grants. Grant privileges to roles such as app_read or support_ops and assign people to roles.
  • No standing DBA access for developers. Elevated access is requested, approved, time-boxed and logged.
-- PostgreSQL: who holds which roles?
SELECT r.rolname AS role, m.rolname AS member
FROM   pg_auth_members am
JOIN   pg_roles r ON r.oid = am.roleid
JOIN   pg_roles m ON m.oid = am.member
ORDER  BY 1, 2;

2. Review access on a schedule

Periodic access reviews, often run as a system access review (SAR) with peer validation, are the control auditors test most. Export the role membership, send it to the business owner, record their sign-off and remove what they reject. Leavers and role-changers are where findings usually come from, so tie the review to HR movement data where you can.

3. Separate duties

The person who writes a change shouldn't be the person who deploys it to production, and neither should be able to alter the audit trail. In practice that means separate deployment accounts, release approvals in your change tool (ServiceNow or similar), and audit logs written somewhere the DBA team can't edit.

4. Log the right things

Logging everything produces noise nobody reads. Log what matters for financial integrity and security:

  • Logins, failed logins and privilege changes.
  • DDL in production schemas.
  • DML on sensitive tables by anyone other than the application account.

Oracle's unified auditing and PostgreSQL's pgaudit extension both support this kind of targeted policy. Ship the logs off the server to a central store, and alert on the events that should never happen, such as a direct update to a ledger table outside a release window.

5. Control change

Every production change needs a ticket, an approval, a tested script, a rollback script and evidence that it ran. Keep database code (packages, functions, migrations) in version control, and deploy from there rather than from someone's laptop.

6. Prove you can recover

Backups that haven't been restored are a hope, not a control. Schedule disaster-recovery drills, restore to a separate environment, time the recovery against your RTO and RPO, and keep the evidence. Auditors ask for the last drill report, and so will your business when something actually fails.

7. Protect data in transit and at rest

Enforce TLS for client connections, encrypt backups, and sign the code you deploy so that what reaches production is what was approved. Remove test copies of production data, or mask them, before they reach lower environments.

A good control is boring: it runs on a schedule, it leaves evidence, and it doesn't depend on one person remembering. If a control only works when you're on shift, it isn't one yet.

None of this slows a capable team down for long. Once access, logging and change are routine, audits become a matter of handing over evidence rather than a scramble to produce it.