Nobody breaks into an Oracle database. They log in.
SYSTEM/manager. An app account whose password is its own username. DBSNMP, exactly where the installer
left it. The first move against your listener isn’t some nation-state zero-day — it’s a thirty-year-old
password list run against your accounts until one opens. No skill required. It works constantly.
Meanwhile “hardening” has become a performance. Someone opens a 200-item benchmark, disappears for three
weeks tuning parameters nobody can pronounce, produces a green spreadsheet, and calls it done — while
SYSTEM/manager still logs in. That’s not security. It’s theater with a compliance stamp, and the
attacker doesn’t care about your spreadsheet.
Here’s the part nobody wants to hear: most of that benchmark is noise. Six controls stop the overwhelming majority of incidents that actually happen — the other 194 are how you stay busy while the two that matter sit untouched. Below are the six, in the order an attacker would thank you for ignoring, each with how it gets exploited and the one command that tells you whether you’re exposed. Do these before you touch anything else.
1. The accounts you already left open
Start here, because it’s the most reliably exploited weakness in the entire Oracle world and fixing it costs nothing but the admission that it was there. No exploit, no CVE, no clever chain — just Oracle’s own factory passwords, still working, because “we’ll rotate those later” quietly became never.
Stop guessing which accounts are exposed. Oracle keeps the list of its own known-default password hashes and hands you a view that checks yours against it:
SELECT username, account_status FROM dba_users_with_defpwd ORDER BY username;
Every row is an unlocked door with your data behind it. Lock the ones you don’t use and expire them; give
the ones you keep a password that isn’t a punchline. (Sample schemas like HR, OE, and SCOTT
shouldn’t be anywhere near production — Oracle stopped installing them by default years ago, so if they’re
there, someone put them there.)
ALTER USER scott ACCOUNT LOCK PASSWORD EXPIRE; -- don't need it → close it
ALTER USER dbsnmp IDENTIFIED BY "<a real secret>"; -- need it → stop using the default
Then hunt down the accounts that are simply open and forgotten — SELECT username FROM dba_users WHERE account_status = 'OPEN', and lock every one that no human or application actually signs in as. The
service account left behind by a migration two years ago, still carrying CREATE SESSION, is exactly the
kind of thing the wrong person finds first. An account that never authenticates has no business being
able to.
2. Take back what you handed out
Almost every over-privilege problem in Oracle traces back to a grant somebody made to stop being asked about it. Three of them do most of the damage.
PUBLIC means everyone — including the account an attacker just took over. For years Oracle granted
EXECUTE on the network and file packages — UTL_HTTP, UTL_TCP, UTL_SMTP, UTL_INADDR, UTL_FILE —
to PUBLIC. Those let PL/SQL open outbound connections and read files. In the hands of a low-privilege
account someone’s already popped, that’s a ready-made exfiltration kit you shipped them. Find what
PUBLIC can run that it has no business running:
SELECT table_name, privilege
FROM dba_tab_privs
WHERE grantee = 'PUBLIC'
AND table_name IN ('UTL_HTTP','UTL_TCP','UTL_SMTP','UTL_INADDR','UTL_FILE','DBMS_LOB');
Revoke it from PUBLIC, then grant it back to the specific schemas that actually need it — a much
shorter list than you’d guess.
The ANY privileges are a skeleton key. SELECT ANY TABLE, EXECUTE ANY PROCEDURE, ALTER ANY … —
every one of them steps over every schema boundary in the database. They land on application and
reporting accounts because enumerating the real objects was more work than someone felt like doing. List
the ones your own accounts hold (Oracle’s maintained accounts legitimately carry a few):
SELECT p.grantee, p.privilege
FROM dba_sys_privs p JOIN dba_users u ON u.username = p.grantee
WHERE u.oracle_maintained = 'N'
AND p.privilege LIKE '%ANY%';
DBA (or PDB_DBA) on an application account is the same mistake wearing a nicer suit. An app owns
its objects and holds exactly the privileges its code needs — not the role that can read, alter, and drop
everything in the database. Check DBA_ROLE_PRIVS the same way and swap the role for a tailored one. The
app that “just needs DBA to work” is the app that hands over the whole database the day it’s compromised.
3. Lock the front door
You can harden the database to a mirror shine and still be trivially reachable if the listener sitting in front of it is wide open. Three controls matter more than the rest.
- Valid-node checking. An allow-list at the network layer, before authentication even happens —
TCP.VALIDNODE_CHECKING,TCP.INVITED_NODES,TCP.EXCLUDED_NODESinsqlnet.ora. Decide who’s even allowed to knock. - Admin restrictions. Set
ADMIN_RESTRICTIONS_<listener> = ONso nobody reconfigures the listener remotely at runtime. A listener you cansetfrom across the network is a listener an attacker cansetfrom across the network. - Never on the public internet. A database listener on
0.0.0.0:1521reachable from outside your network is the single finding behind an embarrassing share of Oracle compromises. It belongs on a private subnet, behind a security group that lets in the app tier and nothing else. If you do one thing on this whole list, do this one.
And encrypt the traffic — native network encryption in sqlnet.ora, or TCPS/TLS. An unencrypted
connection is your credentials and your data in cleartext on the wire, readable by anyone sitting on the
path.
4. Encrypt what’s sitting still
Everything so far protects the database while it’s running. Transparent Data Encryption protects it
when it isn’t — when a datafile, a backup piece, or a decommissioned disk walks out the door. Without TDE,
a stolen datafile is a file an attacker runs strings on over coffee. With it, it’s noise without the
keystore.
TDE encrypts at the tablespace (or column) level and the application never notices — no SQL changes. Set up a keystore once, then encrypt:
-- one-time: configure and open a keystore (WALLET_ROOT), then:
ADMINISTER KEY MANAGEMENT SET KEY FORCE KEYSTORE IDENTIFIED BY "<pwd>" WITH BACKUP;
ALTER TABLESPACE users ENCRYPTION ONLINE USING 'AES256' ENCRYPT;
Encrypt new tablespaces from creation, not “someday.” And TDE is the thing that makes an RMAN backup safe to store off-site in the first place — pair it with the recovery discipline in the RMAN Recovery Runbook so the backup you can restore is also the one you don’t have to lose sleep over.
5. Make brute force expensive
Closing the known passwords (control #1) shuts the doors an attacker already has keys to. A failed-login
lockout shuts the guessable ones, by making an online brute-force die after a handful of tries. It’s
one ALTER PROFILE on the DEFAULT profile — which nearly every account inherits — and it’s astonishing
how often it’s left wide open:
SELECT resource_name, limit
FROM dba_profiles
WHERE profile = 'DEFAULT'
AND resource_name IN ('FAILED_LOGIN_ATTEMPTS','PASSWORD_LIFE_TIME','PASSWORD_VERIFY_FUNCTION');
If FAILED_LOGIN_ATTEMPTS comes back UNLIMITED, an attacker can guess until the heat death of the
universe. Give it a finite lockout, a password lifetime, and a verify function so nobody can set
welcome1 in the first place:
ALTER PROFILE DEFAULT LIMIT
FAILED_LOGIN_ATTEMPTS 10
PASSWORD_LIFE_TIME 180
PASSWORD_VERIFY_FUNCTION ora12c_strong_verify_function;
(Temper it with reality: too aggressive a threshold plus a shared service account is a denial-of-service you inflicted on yourself. Ten is sane; service accounts should live on long, rotated secrets and not lean on lockout at all.)
6. Audit the few things worth watching
Most databases have no useful audit trail for one reason: someone once flipped on “audit everything,” drowned in a firehose nobody read, and quietly switched it back off. Unified Auditing — the model since 12c, writing to a protected table instead of loose OS files — makes selective, low-noise auditing easy through policies. You don’t need dozens. You need the two that catch privilege abuse and credential attacks, and Oracle pre-defines both:
-- what's actually enabled right now:
SELECT policy_name, enabled_option FROM audit_unified_enabled_policies ORDER BY policy_name;
-- the sensible baseline:
AUDIT POLICY ora_secureconfig; -- privileged actions, structural changes (on by default — keep it)
AUDIT POLICY ora_logon_failures; -- failed logins: the brute force from item 5, recorded
ORA_SECURECONFIG is on by default in a modern database — so if it’s not enabled, that’s your finding
right there. From that floor, add narrow policies for what’s specific to you (reads of a sensitive table,
use of a powerful role), never broad ones. Still on the legacy AUDIT/AUD$ model? Move to pure Unified
Auditing now — it’s faster, it can’t be tampered with from outside the database, and it’s the only game
left in 23ai, where traditional auditing is gone entirely. (There’s a whole post on doing it right:
Oracle Unified Auditing Without the Noise.)
flowchart TD A["Where do I start?"] --> B["1. Default passwords<br/>DBA_USERS_WITH_DEFPWD → lock/expire"] B --> C["2. Least privilege<br/>PUBLIC · %ANY% · DBA on apps"] C --> D["3. Listener + network<br/>valid-node, admin restrict, private subnet"] D --> E["4. TDE at rest<br/>datafiles + backups useless if stolen"] E --> F["5. Password profile<br/>failed-login lockout + verify fn"] F --> G["6. Unified Auditing<br/>ORA_SECURECONFIG + ORA_LOGON_FAILURES"] G --> H["Re-audit on a schedule<br/>hardening drifts"]
Don’t take my word for it — run it. The hardening-audit lab stands up an Oracle Database Free container in a deliberately terrible state, scores it against these controls with
./run.sh audit— default passwords,PUBLIC/ANYgrants,DBA-on-app-accounts, the failed-login profile, the auditing baseline — then./run.sh hardenfixes them and re-audits until every check flips FAIL → PASS. The CI matrix runs the whole cycle on every push, so the numbers aren’t a claim. They’re a test.
What teams get wrong
- Chasing the whole benchmark. Two hundred controls in document order means the five that matter fight for attention with the trivia. Rank by exploitability, not by how complete the checklist looks.
- Auditing everything and reading nothing. A firehose gets muted, then disabled. Two baseline policies plus a few sharp ones — and actually read them.
- Leaving default and unused accounts open.
DBA_USERS_WITH_DEFPWDandaccount_status = 'OPEN'are one query each. There’s no excuse for an open account with a factory password. - Granting
DBA(or%ANY%) for convenience. The app that “just needs it to work” is the account that reads and drops every schema the moment it’s compromised. - Treating hardening as one-time. Grants pile up, accounts appear, someone disables a policy to debug and forgets. It drifts. Re-audit on a cadence, the same way you patch.
- Encrypting nothing because “it’s internal.” Internal networks get breached and backups get lost. TDE turns both from incidents into non-events.
Frequently asked questions
What is the most important first step in Oracle database hardening?
Closing accounts that still have a default or known password. It is the most reliably exploited Oracle weakness and requires no exploit — an attacker simply tries known username/password pairs against the listener. Oracle provides the DBA_USERS_WITH_DEFPWD view, which lists every account whose password matches a known default; lock and expire the ones you do not need and set real passwords on the ones you do. It costs one query and a few ALTER USER statements and removes the easiest way in.
How do I find accounts with default passwords in Oracle?
Query DBA_USERS_WITH_DEFPWD: SELECT username, account_status FROM dba_users_with_defpwd. Oracle maintains an internal list of known default password hashes and this view reports any account whose current password matches one. Each row is an account that can be accessed with a publicly known password, so treat every one as a finding: lock and expire unused accounts, and change the password on accounts you still need.
Should I revoke EXECUTE on packages from PUBLIC?
For the network and file packages — UTL_HTTP, UTL_TCP, UTL_SMTP, UTL_INADDR, UTL_FILE — yes, in most environments. Granted to PUBLIC, they let any authenticated account (including a compromised low-privilege one) open outbound network connections or read files, which is an exfiltration and lateral-movement capability. Revoke EXECUTE from PUBLIC and grant it back only to the specific schemas that genuinely require it. Test first, because application code occasionally relies on these grants.
What is the difference between traditional and Unified Auditing in Oracle?
Traditional auditing (the legacy AUDIT command writing to SYS.AUD$ or to OS files) is configured statement by statement and can be verbose and hard to protect. Unified Auditing, the model since Oracle 12c, consolidates all audit data into a single protected, read-only internal table and is configured through policies you enable or disable as a unit. Unified Auditing is faster, harder to tamper with, and easier to keep low-noise. Oracle recommends moving to pure Unified Auditing; enable the ORA_SECURECONFIG and ORA_LOGON_FAILURES policies as a baseline.
Does hardening an Oracle database require downtime?
Most of it does not. Locking accounts, revoking privileges, altering the DEFAULT profile, and enabling Unified Auditing policies are all online operations. Encrypting existing tablespaces with TDE can be done online in current releases, though the initial keystore configuration may involve a restart depending on how WALLET_ROOT is set. Listener and sqlnet.ora changes take effect on a listener reload, not a database restart. Plan TDE and network-encryption rollouts, but the high-value account and privilege work is all no-outage.
Is Transparent Data Encryption (TDE) necessary if my database is on an internal network?
It protects a different threat than network controls do. Listener and firewall rules protect the running database from unauthorized connections; TDE protects the data when it is at rest and leaves the running system — a stolen or misplaced datafile, an RMAN backup piece copied off-site, a decommissioned disk. Internal networks are breached and backups do get lost, so for any database holding sensitive or regulated data, TDE is the control that turns those events into non-events. It is transparent to the application.
How often should I re-audit an Oracle database against a hardening checklist?
On a defined cadence, the same way you patch — quarterly is a reasonable baseline, plus after any significant change. Hardening drifts: privileges get granted for a one-off task and never revoked, accounts get created, someone disables an audit policy to debug an issue and forgets to re-enable it. A one-time hardening project degrades steadily unless you re-check it. Automating the checks (a script or a lab-style audit that reports PASS/FAIL) makes the re-audit cheap enough to actually do.
What are the ANY privileges and why are they a hardening risk?
The system privileges containing ANY — SELECT ANY TABLE, EXECUTE ANY PROCEDURE, ALTER ANY TABLE, and others — grant an operation across every schema in the database, ignoring object ownership boundaries. They are commonly granted to application or reporting accounts as a shortcut instead of enumerating the specific objects needed. If such an account is compromised, the ANY privilege lets the attacker read, alter, or drop objects anywhere. Find them with DBA_SYS_PRIVS filtered to accounts where ORACLE_MAINTAINED = N, and replace them with grants on the specific objects the account actually uses.
Hardening and patching are the two halves of security-and-ops:
patching closes the holes Oracle warns you about, hardening closes the ones your own configuration quietly
opened. Neither is a project you finish — both are a cadence. Start with the six above, prove them with the
hardening-audit lab, and put the re-audit
on the same calendar as your quarterly Release Update. The best time to close SYSTEM/manager was install
day. The second best is before you finish reading this.
Have a question or some feedback?
I write here in a personal capacity and enjoy comparing notes with other Oracle folks. Say hello.
Get in touch