Open any Oracle security benchmark and you’ll find two hundred–plus line items, each with a rationale, a check, and a remediation. It is thorough, it is auditable, and it is where most hardening projects go to die — three weeks of setting obscure parameters that shave a rounding error off your risk while the two gaps that actually get databases breached sit untouched because they weren’t near the top of the alphabet.
Hardening is not a completeness exercise. A short list of controls stops the overwhelming majority of real incidents: accounts that still have their default password, privileges handed out for convenience, a listener open to the world, unencrypted data at rest, and no audit trail to notice any of it. Here’s that list — in priority order, each with why it matters, how it actually gets exploited, and the one query or command that tells you where you stand.
The short version. Do these six, roughly in order. (1) Find and close accounts with default passwords —
DBA_USERS_WITH_DEFPWDnames them for you. (2) Take back what you granted for convenience:EXECUTEon the network packages (UTL_HTTP,UTL_TCP,UTL_SMTP) from PUBLIC, any%ANY%system privilege, andDBAon application accounts. (3) Lock the listener — valid-node checking, admin restrictions, and never on the public internet. (4) Turn on TDE so a stolen datafile or backup is useless. (5) Give theDEFAULTprofile a failed-login lockout and a password verify function. (6) Enable Unified Auditing for the few things worth watching (ORA_SECURECONFIG+ORA_LOGON_FAILURES), not everything. Everything past this is refinement.
1. Kill the default passwords
This is number one because it is the single most reliably exploited weakness in an Oracle estate, and it
is free to fix. Every attacker’s first move against an Oracle listener is to try known account/password
pairs — SYSTEM/manager, DBSNMP/dbsnmp, SCOTT/tiger, an app account whose password equals its
username. It requires no exploit, no CVE, nothing but a dictionary.
You do not have to guess which of your accounts are exposed, because Oracle ships a view that checks the password hashes against its own list of known defaults:
SELECT username, account_status FROM dba_users_with_defpwd ORDER BY username;
Every row is an account an attacker can walk into. For each one: if you don’t need it, lock and
expire it; if you do, give it a real password. Sample schemas (HR, OE, SCOTT) shouldn’t be in a
production database at all — they’re not installed by default anymore, so if they’re present, someone
added them.
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
While you’re here, look at what’s simply open and unused: SELECT username FROM dba_users WHERE account_status = 'OPEN' and lock every account no human or application actually logs in as. An account
that can’t authenticate can’t be the way in.
2. Least privilege: PUBLIC, the ANY privileges, and DBA-for-convenience
Almost every over-privilege problem in Oracle traces back to a grant someone made to stop being asked about it. Three of them do most of the damage.
PUBLIC means every user, including the one an attacker just compromised. Historically Oracle
granted EXECUTE on the network and file packages — UTL_HTTP, UTL_TCP, UTL_SMTP, UTL_INADDR,
UTL_FILE — to PUBLIC. Those packages let PL/SQL open outbound network connections and read files;
in the hands of a low-privilege account that’s been popped, they’re an exfiltration and
lateral-movement kit. Find what PUBLIC can execute that it shouldn’t:
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 from PUBLIC and grant back to the specific schemas that genuinely need it — usually far fewer
than you expect.
The ANY privileges are a skeleton key. SELECT ANY TABLE, EXECUTE ANY PROCEDURE, ALTER ANY …
— each one crosses every schema boundary in the database. They get granted to application accounts and
reporting users because it was easier than enumerating objects. List the ones held by accounts you
created (Oracle’s own maintained accounts legitimately hold some):
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 role. An app should own
its objects and hold exactly the privileges its code needs — never the role that can read, alter, and
drop everything. Check with DBA_ROLE_PRIVS filtered the same way, and replace the role with a tailored
one.
3. Lock the front door: the listener and the network
The database can be perfectly hardened and still be trivially reachable if the listener in front of it isn’t. Three controls matter more than the rest:
- Valid-node checking. Restrict which hosts may even connect to the listener with
TCP.VALIDNODE_CHECKING,TCP.INVITED_NODES, andTCP.EXCLUDED_NODESinsqlnet.ora. It’s an allow-list at the network layer, before authentication. - Admin restrictions. Set
ADMIN_RESTRICTIONS_<listener> = ONso nobody can reconfigure the listener remotely at runtime, and make sure it requires OS authentication for admin — a listener you cansetremotely is a listener an attacker cansetremotely. - Never on the public internet. A database listener on
0.0.0.0:1521reachable from outside your network is the finding behind a large share of Oracle compromises. It belongs on a private subnet, behind a security group that allows only the application tier.
If you can encrypt the client-to-database traffic, do — either native network encryption in sqlnet.ora
or TCPS/TLS. An unencrypted connection is credentials and data in cleartext on the wire.
4. Encrypt at rest: TDE
Everything above protects the running database. Transparent Data Encryption protects it when it
isn’t running — when a datafile, a backup piece, or a decommissioned disk leaves the building. Without
TDE, a stolen datafile or RMAN backup is just a file an attacker strings; with it, it’s noise without
the keystore.
TDE encrypts at the tablespace (or column) level and is transparent to the application — no SQL changes. You 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;
New tablespaces should be encrypted from creation. And TDE is what makes an RMAN backup safe to store off-site — pair it with the recovery discipline in the RMAN Recovery Runbook so the backup you can restore is also the backup you don’t have to worry about losing.
5. Password profiles: make brute force expensive
Default-password hygiene (item 1) closes the known passwords. A failed-login lockout closes the
guessable ones by making an online brute-force attack die after a handful of tries. This is one
ALTER PROFILE on the DEFAULT profile — which nearly every account inherits — and it’s often 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 reads UNLIMITED, an attacker can guess forever. Set a finite lockout, a
password lifetime, and attach a verify function so weak passwords can’t be set in the first place:
ALTER PROFILE DEFAULT LIMIT
FAILED_LOGIN_ATTEMPTS 10
PASSWORD_LIFE_TIME 180
PASSWORD_VERIFY_FUNCTION ora12c_strong_verify_function;
(Balance the lockout against your reality: too aggressive a threshold plus a shared service account is a self-inflicted denial of service. Ten is a sane default; service accounts should use long, rotated secrets rather than relying on lockout at all.)
6. Audit the few things worth auditing
The reason most Oracle databases have no useful audit trail is that “audit everything” produces a firehose nobody reads, so eventually someone turns it off. Unified Auditing — the model since 12c, which writes to a protected internal table instead of 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, which Oracle pre-defines:
-- 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 enabled by default in a modern database — the failure mode is someone having
turned it off. From that baseline, add narrow policies for the things specific to you (access to a
sensitive table, use of a powerful role) rather than broad ones. If you’re still on the legacy
AUDIT/AUD$ model, plan the move to pure Unified Auditing — it’s faster and its trail can’t be
tampered with from outside the database.
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"]
Run the checklist against a live database. The hardening-audit lab stands up an Oracle Database Free container in a deliberately weak state, runs
./run.sh auditto score it against these controls — default passwords,PUBLIC/ANYgrants,DBA-on-app-accounts, the failed-login profile, and the auditing baseline — then./run.sh hardenfixes them and re-audits to prove every check flips from FAIL to 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 sorted by document order means the critical five compete for attention with the trivial. Prioritize by exploitability, not by checklist completeness.
- Auditing everything, reading nothing. A firehose gets muted. Enable the two baseline policies plus a few targeted ones, and actually review them.
- Leaving default and unused accounts open.
DBA_USERS_WITH_DEFPWDandaccount_status = 'OPEN'take one query each. There is no excuse for an open account with a known password. - Granting
DBA(or%ANY%) for convenience. The app that “just needs it to work” becomes the account that can read and drop every schema once it’s compromised. - Treating hardening as one-time. Grants accumulate, accounts get created, someone disables a policy to debug and forgets. Configuration drifts; re-audit on a cadence, the same way you patch on a cadence.
- Encrypting nothing because “it’s internal.” Internal networks get breached and backups get lost. TDE is the control that makes those events non-events.
The one-paragraph version
Skip the two-hundred-item benchmark and do the six that matter, in order. Close accounts with default
passwords (DBA_USERS_WITH_DEFPWD finds them). Take back convenience grants — EXECUTE on the network
packages from PUBLIC, every %ANY% privilege, and DBA on application accounts. Lock the listener
with valid-node checking and admin restrictions, and keep it off the public internet. Turn on TDE so
stolen datafiles and backups are useless. Give the DEFAULT profile a failed-login lockout and a
verify function. Enable Unified Auditing for ORA_SECURECONFIG and ORA_LOGON_FAILURES, then add
narrow policies — not a firehose. Then re-audit on a schedule, because hardening drifts. That list stops
the incidents that actually happen; everything else is refinement.
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 the security-and-ops discipline: patching closes the vulnerabilities Oracle tells you about, hardening closes the ones your own configuration opened. Neither is a one-time project — both are a cadence. Start with the six controls above, prove them with the hardening-audit lab, and put the re-audit on the same calendar as your quarterly Release Update.
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