DEV Community

Uptime Architect
Uptime Architect Subscriber

Posted on Originally published at uptimearchitect.com

Your Oracle Traffic Is Readable on the Wire. Here's the Proof, and the Fix.

The database is patched. The data files are encrypted with TDE. The audit trail is clean. And every query your application runs — along with every row it gets back — crosses the network as readable text. Not because anyone turned encryption off, but because nobody ever turned it on: out of the box, an Oracle client and an Oracle server agree to talk in plaintext.

That is not a theoretical weakness. Anyone who can see the packets — a compromised jump host, a mirrored switch port, a misconfigured cloud network, a curious admin with tcpdump — can read the SQL, the bind values, and the result sets. This post shows exactly what is on the wire, why the default is plaintext, the four lines of sqlnet.ora that fix it, how to verify a session is actually encrypted, and a lab that captures the same session before and after.

What is actually on the wire

Here is what a packet capture of an ordinary session looks like on a default install. The APP user connects over TCP and runs one query against a table holding a confidential value; tcpdump -A on port 1521 shows:

wire> select 'ROWS='||count(*) from vault where secret like 'CANARY-%'
wire> CANARY-7731-CONFIDENTIAL
Enter fullscreen mode Exit fullscreen mode

That is the SQL text and the result value, lifted straight out of the packets. No exploit, no credentials, no access to the database — just a view of the network path.

Two things are worth being precise about. First, the password is not the problem: Oracle's logon is a challenge-response, so the password itself does not cross in the clear even here. Everything after the logon does. Second, this is not an "old version" issue. The capture above is from the current Oracle Database Free release, with nothing misconfigured — it is simply the default.

Why the default is plaintext

Oracle's native network encryption is negotiated per connection, and each side has a setting in sqlnet.ora: SQLNET.ENCRYPTION_SERVER on the database host and SQLNET.ENCRYPTION_CLIENT on the client. Each can be one of four values:

  • REJECTED — never encrypt; refuse a peer that insists.
  • ACCEPTED — encrypt only if the other side asks. This is the default on both sides.
  • REQUESTED — ask for encryption, but fall back to plaintext if the other side won't.
  • REQUIRED — encrypt or refuse the connection.

The trap is in that default. When both ends are ACCEPTED, each is willing to encrypt but neither asks — so the negotiation succeeds with no encryption at all. Nothing is broken, nothing logs a warning, and the session runs in plaintext forever.

The integrity side works the same way with SQLNET.CRYPTO_CHECKSUM_SERVER / _CLIENT: a per-packet cryptographic checksum that detects tampering and replay in transit. It is a separate switch, and it defaults to off for the same reason.

Native network encryption is negotiated per connection from the two sides' settings. With

Native network encryption is negotiated per connection from the two sides' settings. With the default ACCEPTED on both ends, each side is willing but neither asks, so the session runs in plaintext. Setting the server to REQUIRED forces every new session to negotiate AES256 (clients default to ACCEPTED, so no client change is needed). A client that is explicitly REJECTED is refused with ORA-12660 rather than silently falling back to plaintext.

The fix: four lines on the server

Make encryption and integrity required on the database server, and pin strong algorithms. In the server's sqlnet.ora (normally $ORACLE_HOME/network/admin, or wherever TNS_ADMIN points):

SQLNET.ENCRYPTION_SERVER = REQUIRED
SQLNET.ENCRYPTION_TYPES_SERVER = (AES256)
SQLNET.CRYPTO_CHECKSUM_SERVER = REQUIRED
SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA256)
Enter fullscreen mode Exit fullscreen mode

Why the server, and why REQUIRED:

  • One change covers every client. Clients default to ACCEPTED, so once the server requires encryption, every client that hasn't explicitly rejected it simply negotiates it. No application change, no client redeploy.
  • REQUIRED, not REQUESTED. REQUESTED falls back to plaintext if a peer declines — which means a misconfigured client silently stays readable. REQUIRED makes that client fail loudly with ORA-12660 instead, which is what you want to find in testing, not in an incident.
  • Pin the algorithm list. List only the algorithms you accept (AES256, and AES192/AES128 if you must), so nothing negotiates down to a legacy cipher. Same for the checksum: SHA-2 family only.
  • Integrity is not optional. Encryption without the checksum hides the data but doesn't stop tampering or replay. Require both.

No restart is needed: the server reads sqlnet.ora when it starts a new server process for a new connection. That also means existing sessions keep whatever they negotiated — recycle connection pools after the change, or they will stay in plaintext until they reconnect.

Verify it, don't assume it

A config file is a claim. The proof is what each session actually negotiated, which Oracle exposes in V$SESSION_CONNECT_INFO:

SELECT network_service_banner
FROM   v$session_connect_info
WHERE  sid = SYS_CONTEXT('USERENV', 'SID');
Enter fullscreen mode Exit fullscreen mode

On an encrypted session you will see lines naming the algorithm in use — AES256 Encryption service adapter… and SHA256 Crypto-checksumming service adapter…. On a plaintext session you will only see the generic "Encryption service for Linux" / "Crypto-checksumming service" lines with no algorithm adapter. Run the same check across all sessions (drop the WHERE, join to V$SESSION for usernames and programs) to find any connection still in the clear.

Don't take my word for it — run it. The SQL*Net encryption lab stands up Oracle Database Free and a throwaway tcpdump sidecar that shares the database's network namespace. The APP user reads a confidential row over TCP: on the default config the session negotiates no encryption and the capture contains both the SQL text and the secret value. It then adds the four sqlnet.ora lines, runs the same session under the same capture, and asserts the session negotiated AES256 / SHA256 and that neither the SQL nor the secret appears anywhere in the packets. If the plaintext doesn't reproduce, or the fix doesn't hide it, the run fails. Proven on every CI push.

In the lab, the before-and-after is unambiguous:

default : session encryption: NONE    integrity: NONE    SQL seen 2x, secret seen 1x
fixed   : session encryption: AES256  integrity: SHA256  SQL seen 0x, secret seen 0x
Enter fullscreen mode Exit fullscreen mode

Native encryption or TLS?

Oracle offers two ways to encrypt the connection, and both are included in every edition at no extra cost:

  • Native network encryption (what this post configures) — a few lines of sqlnet.ora, no certificates, and it works with existing connect strings on port 1521. The fastest way to get every session off plaintext.
  • TLS (TCPS) — certificate-based, usually on a separate port. Its key advantage is server authentication: the client verifies it is talking to the real database, which protects against a rogue or spoofed listener in the middle. Native encryption has no such check. TLS is also what many compliance frameworks name explicitly, and it encrypts the connect packet that native encryption leaves readable.

A practical path: turn on native encryption with REQUIRED now — it closes the plaintext gap today with almost no risk — and plan TLS where you need server authentication or an auditor asks for it by name.

What teams get wrong

  • Assuming TDE covers it. TDE encrypts data at rest — data files and backups. The moment a row is read and sent to a client, TDE is out of the picture. In-transit protection is a separate control.
  • Assuming the network is private. "It's all inside the VPC / the data center" is exactly the assumption an attacker with one foothold relies on. Internal traffic is where lateral movement happens.
  • Setting REQUESTED and calling it done. It looks like encryption is on, but any peer that declines gets a plaintext session with no error. Use REQUIRED and let incompatible clients fail where you can see them.
  • Encrypting without integrity. Without CRYPTO_CHECKSUM, traffic is hidden but can still be modified or replayed in transit. Require both.
  • Never verifying. A sqlnet.ora in the wrong directory, an overriding TNS_ADMIN, or a pool that never reconnected all leave sessions in plaintext while the config looks correct. Check V$SESSION_CONNECT_INFO.
  • Expecting everything to be hidden. The initial connect packet — service name, client program, OS user — is sent before negotiation and stays readable with native encryption. The database username, SQL and data do not. If the connect descriptor itself is sensitive, that's an argument for TLS.

Frequently asked questions

Is Oracle database network traffic encrypted by default?

No. By default both the Oracle client and the database server have their native network encryption setting at ACCEPTED, which means each side is willing to encrypt but neither one requests it, so the connection is negotiated without encryption. SQL statements, bind values and result sets then cross the network as readable bytes that anyone who can capture the packets can read. The logon password is protected by Oracle's challenge-response authentication, but everything after logon is in plaintext until you configure encryption, either with native network encryption in sqlnet.ora or with TLS (TCPS).

How do I enable Oracle native network encryption?

Add four lines to the database server's sqlnet.ora (normally in $ORACLE_HOME/network/admin or the directory TNS_ADMIN points to): SQLNET.ENCRYPTION_SERVER = REQUIRED, SQLNET.ENCRYPTION_TYPES_SERVER = (AES256), SQLNET.CRYPTO_CHECKSUM_SERVER = REQUIRED, and SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA256). Because clients default to ACCEPTED, every new connection then negotiates AES256 encryption and SHA-256 integrity without any client change. No database restart is needed, but existing sessions keep what they already negotiated, so recycle application connection pools afterwards and verify the result in V$SESSION_CONNECT_INFO.

What is the difference between ACCEPTED, REQUESTED and REQUIRED?

They control how each side negotiates encryption. REJECTED never encrypts and refuses a peer that requires it. ACCEPTED, the default, encrypts only if the other side asks. REQUESTED asks for encryption but falls back to plaintext if the other side declines. REQUIRED insists on encryption and refuses the connection otherwise. Encryption is used when at least one side requests or requires it and the other does not reject it; if one side is REQUIRED and the other REJECTED, the connection fails with ORA-12660. Using REQUIRED on the server is the safe choice because it turns a silent plaintext fallback into a visible error.

How can I check whether an Oracle session is encrypted?

Query V$SESSION_CONNECT_INFO for the session, for example SELECT network_service_banner FROM v$session_connect_info WHERE sid = SYS_CONTEXT('USERENV','SID'). An encrypted session shows a line naming the algorithm adapter in use, such as 'AES256 Encryption service adapter', and an integrity-protected session shows a line such as 'SHA256 Crypto-checksumming service adapter'. A plaintext session only shows the generic encryption and crypto-checksumming service lines with no algorithm adapter. To audit the whole instance, query it for all sessions joined to V$SESSION to see which users and programs are still connecting without encryption.

Does native network encryption need a separate license?

No. Oracle native network encryption and data integrity, as well as TLS (TCPS) for database connections, were once part of the separately licensed Advanced Security option, but Oracle made network encryption available in all editions of the database years ago. Transparent Data Encryption and Data Redaction remain part of Advanced Security, but encrypting traffic between clients and the database does not require it.

Should I use native network encryption or TLS?

Both encrypt the traffic. Native network encryption is configured with a few sqlnet.ora parameters, needs no certificates, and works with existing connect strings, which makes it the fastest way to get every session off plaintext. TLS (TCPS) uses certificates and adds server authentication, so the client verifies it is connected to the genuine database and is protected against a spoofed listener or man-in-the-middle; it also encrypts the initial connect packet and is the control many compliance frameworks name explicitly. A common approach is to enable native encryption with REQUIRED immediately, and adopt TLS where server authentication or a specific compliance requirement calls for it.

Does Transparent Data Encryption protect data in transit?

No. Transparent Data Encryption (TDE) encrypts data at rest, in data files, redo, and backups, so that stolen files or media are unreadable. When a session queries the data, the database decrypts it and sends the result to the client, and at that point TDE no longer applies. Protecting data as it travels between the client and the database requires network encryption, either native network encryption or TLS. A complete setup uses both: TDE for data at rest and network encryption for data in transit.

Network encryption is the in-transit half of a picture the rest of the security series builds from other angles: the hardening checklist decides who can connect and how, TDE protects the data files and backups at rest, redaction narrows which values reach a screen, and unified auditing records who asked. None of them help once a result set is crossing the network in readable bytes. Set the server to REQUIRED, pin AES256 and SHA-256, verify every session in V$SESSION_CONNECT_INFO, and then prove it the way that ends the argument: with the SQL*Net encryption lab, where the same query is readable in a packet capture before the fix and unreadable after.


Originally published at uptimearchitect.com.

Top comments (0)