Secure Oracle Database with cnspec
Scan Oracle databases, users, roles, and profiles against security and compliance best practices with cnspec.
Scan your Oracle databases to find security risks before they become incidents. cnspec evaluates administrative accounts, default passwords, directly granted system privileges, roles, password and resource profiles, and initialization parameters.
cnspec connects over Oracle Net and inventories the database through read-only queries against the V$ views and the DBA_* data dictionary. It never touches your data. The provider uses a pure-Go driver, so it needs no Oracle Instant Client or other native libraries.
If you're new to cnspec, start with the Quickstart. For an overview of every database cnspec can scan, see the database scanning overview.
Prerequisites
To scan Oracle Database with cnspec, you must have:
- cnspec installed on your workstation
- Network access to the listener (port 1521 by default)
- A database user with
CREATE SESSIONandSELECT_CATALOG_ROLE
Authenticate
cnspec authenticates with a database user and password, connecting to a service name or a SID:
cnspec shell oracledb db.contoso.com --service ORCLPDB1 --user auditor --ask-passCreate a least-privileged auditing user. SELECT_CATALOG_ROLE reads everything the resources below need, without access to any application data:
CREATE USER auditor IDENTIFIED BY <password>;
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO auditor;Without those catalog privileges the users, roles, profiles, and parameters collections come back empty rather than failing the scan. An ORA-00942 or ORA-01031 is treated as "not permitted," so confirm oracledb.instance.users is populated before trusting a passing account check.
For a multitenant database, connect to the pluggable database (PDB) service you want to audit. The DBA_* views then reflect that container.
Connection options
| Option | Description |
|---|---|
--host | Database hostname or IP address (also accepted as the positional argument) |
--port | Oracle Net listener port (default 1521) |
--service | Service name to connect to, for example FREEPDB1 or ORCLPDB1 |
--sid | System identifier, as an alternative to --service |
--user, -u | User to authenticate as |
--password, -p | Password, or --ask-pass to be prompted |
--tls | Connect over TLS (TCPS) |
--tls-insecure | Skip TLS certificate verification (testing only) |
Verify with a quick Oracle check
cnspec shell oracledb db.contoso.com --service ORCLPDB1 --user auditor --ask-passcnspec> oracledb.instance { version instanceName isCdb containerName }
oracledb.instance: {
version: "23.26.2.0.0"
instanceName: "FREE"
isCdb: true
containerName: "FREEPDB1"
}If cnspec connects and shows the version, you're ready to scan.
Scan Oracle Database
cnspec scan oracledb db.contoso.com --service ORCLPDB1 --user auditor --ask-passWhen a scan completes, cnspec prints a summary of all the checks it ran, grouped by policy, along with a risk score from 0 (no risk) to 100 (highest risk). Failed checks include remediation guidance to help you fix issues. To learn more about reading scan results, read Understand cnspec Results.
Mondoo doesn't yet ship an out-of-the-box Oracle Database policy. The resources are shaped around the CIS Oracle Database Benchmark, so the checks below map closely to its recommendations. Use them as a starting point and create your own policies to meet your requirements.
Explore and test checks interactively
cnspec shell oracledb db.contoso.com --service ORCLPDB1 --user auditor --ask-passList users
cnspec> oracledb.instance.users { username accountStatus authenticationType oracleMaintained }Read initialization parameters
cnspec> oracledb.instance.parameters.where(name == "audit_trail" || name == "sql92_security" || name == "resource_limit") { name value }
oracledb.instance.parameters.where: [
0: { name: "audit_trail" value: "NONE" }
1: { name: "resource_limit" value: "TRUE" }
2: { name: "sql92_security" value: "TRUE" }
]Inspect a profile's password policy
cnspec> oracledb.instance.profiles.where(name == "DEFAULT") { name failedLoginAttempts passwordLifeTime passwordReuseMax passwordVerifyFunction }List roles
cnspec> oracledb.instance.roles { name authenticationType oracleMaintained }List a user's directly granted system privileges
cnspec> oracledb.instance.users.where(oracleMaintained == false) { username systemPrivileges roles }Example security checks
Ensure no account still uses a default password
cnspec> oracledb.instance.users.where(accountStatus == "OPEN").none(hasDefaultPassword == true)
[ok] value: trueEnsure administrative connections are limited to SYS
An account with isAdmin can connect as SYSDBA or SYSOPER from the password file, bypassing normal authorization:
cnspec> oracledb.instance.users.where(isAdmin).all(username == "SYS")
[ok] value: trueEnsure no application account holds SELECT ANY DICTIONARY
That privilege reads the entire data dictionary, including every other account's metadata:
cnspec> oracledb.instance.users.where(oracleMaintained == false).none(systemPrivileges.contains("SELECT ANY DICTIONARY"))
[ok] value: trueEnsure no application account holds a blanket ANY privilege
cnspec> oracledb.instance.users.where(oracleMaintained == false) { username systemPrivileges.where(_ == /ANY/) }Ensure auditing is enabled
cnspec> oracledb.instance.parameters.where(name == "audit_trail").none(value == "NONE")
[ok] value: trueEnsure resource limits are enforced
Without resource_limit, the profile limits below are recorded but never applied:
cnspec> oracledb.instance.parameters.where(name == "resource_limit").all(value == "TRUE")
[ok] value: trueEnsure remote OS authentication is off
remote_os_authent trusts the client operating system to say who the user is:
cnspec> oracledb.instance.parameters.where(name == "remote_os_authent").all(value == "FALSE")
[ok] value: trueEnsure every profile locks an account after failed logins
cnspec> oracledb.instance.profiles.none(failedLoginAttempts == "UNLIMITED")
[ok] value: trueEnsure every profile enforces password complexity
cnspec> oracledb.instance.profiles.none(passwordVerifyFunction == "NULL")
[ok] value: trueEnsure passwords expire
cnspec> oracledb.instance.profiles.none(passwordLifeTime == "UNLIMITED")
[ok] value: trueEnsure application accounts are not left on the DEFAULT profile
profile is a typed reference to the account's profile, so you can filter on it and traverse into its password policy rather than matching a bare name:
cnspec> oracledb.instance.users.where(oracleMaintained == false).none(profile.name == "DEFAULT")
[ok] value: trueReview accounts whose profile allows unlimited password reuse
cnspec> oracledb.instance.users.where(profile.passwordReuseMax == "UNLIMITED") { username profile { name } }Learn more
- Oracle Database Resource Pack Reference: every Oracle resource and field cnspec can query
- Write Effective MQL: guide to authoring checks and queries