************************************************************
* Description: Oracle session tracing via client identifier
* Date: 04:28 PM EST, 04/26/2018
************************************************************
<1> In real world, application usually uses a single database credential to access database via connection string:
|
|__ o. And, application user is using app credential to logon app, which are usually saved in a database table.
|
|__ o. So, it will be impossible to track or audit application users' actions in the database.
|
|__ o. Since Oracle 9i, the software provides a method for tracking app users' behavior via "CLIENT_IDENTIFIER" as a column in v$session.
|
|__ o. The CLIENT_IDENTIFIER is a predefined attribute of the built-in application context namespace, USERENV, and can be used to capture the application user name for use with
global application context, or it can be used independently.
<2> Taking C# connecting Oracle database as example:
|
|__ o. Following is the C# code uses embeded "ClientId" to tell Oracle database this variable can be saved under column CLIENT_IDENTIFIER in v$session for tracking purpose:
var_conn = new Oracle.DataAccess.Client.OracleConnection( connectionString ); #== 1. Define a connection variable
var_conn.Open(); #== 2. Grab a available connection process from connection pool
var_conn.ClientId = EmeralitClientID + "." + EmeralitUserRef; #== 3. Telling Oracle this is the CLIENT_IDENTIFIER to be stored in v$session
var_conn.dispose(); #== 4. Cleaning up the session to avoid taking physical memory resource.
<3> After Oracle database received notice from "var_conn.ClientId", it will execute below Oracle integrated procedure to set a user environment variable under session namespace:
|
|__ o. SQL> EXEC DBMS_SESSION.set_identifier('3006.amosgeng');
<4> Then, this application user's behavior in database can be tracked:
|
|__ o. Enable tracing for this app user by CLIENT_IDENTIFIER in v$session;
|
| SQL> exec DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE( client_id => '3006.amosgeng', binds => TRUE, waits => TRUE );
|
|
|
|
|__ o. Below query can fetch all the sessions are being traced:
|
| SQL> select trace_type, primary_id, QUALIFIER_ID1, waits, binds from DBA_ENABLED_TRACES;
|
|
|
|
|__ o. After tracing start, run following query to address the dumped trace file. The default location is defined by db parameter "user_dump":
|
| SQL> select a.client_identifier, b.tracefile from v$session a, v$process b where a.paddr=b.addr and a.client_identifier='3006.amosgeng';
|
|
|
|
|__ o. Disable tracing to avoid trace file size is overwhelm:
|
| SQL> exec DBMS_MONITOR.CLIENT_ID_TRACE_DISABLE( client_id => '3006.amosgeng' );
|
|
|
|
|__ o. After Oracle database received notice from "var_conn.dispose()", it will execute below Oracle integrated procedure to cleanup session:
|
| SQL> EXEC DBMS_SESSION.clear_identifier;
|
|
|
|
|__ o. Since session cleaned, then the CLIENT_IDENTIFIER will be NULL afterwards. The username is the one in C# connection string:
SQL> SELECT USER AS username, SYS_CONTEXT('userenv', 'client_identifier') AS client_identifier FROM dual;
USERNAME CLIENT_IDENTIFIER
-------------------------------------- ---------------------------------
DB_USER_FOR_APP_CONNECTION NULL
<5> Tracing file naming convention:
|
|__ o. Usually, the trace file physical loction can be found out via "show parameter udump".
|
|__ o. The name usually consists of __.trc. For example:
ORATAX_j000_9201.trc ................. This is the trace file dumped by Oracle DBMS scheduler job j000.
ORATAX_smon_8612.trc ................. This is the trace file dumped by Oracle smon process.
ORATAX_mmon_402.trc ................. This is the trace file dumped by Oracle mmon process.
ORATAX_ora_3651.trc ................. This is the trace file dumped by Oracle user process, which turned on manually and indicated by "ora" in the middle.
<6> For C# applications, in some case, use connection pool to create database sessions is preferred:
|
|__ o. The purpose of connection pool is that, everytime when application connects to database, in physical layer, application needs to talk to listener for network hand shake,
| exchange authentication, etc which is taking time.
|
|__ o. As a solution, .NET frame provides a pool of database connection process. The process in this pool connecting to database all the time, so when a new application user
| opens a session, only needs to do is pick up one available connection prccess from the pool. So, there will be no need to exchange authentication info to save time.
|
|__ o. The pool size can be designed as needed, and when one db session done, the context info will be cleard, and connection process will be freed back to pool.
|
|__ o. Following C# code is connecting database through pool:
|
|
| Conn = new Oracle.DataAccess.Client.OracleConnection( connectionstring )
| Conn.ClientId = “3006.amosgeng";
| Conn.Open() #=== 1. Open() only reserves an available connection process from the pool.
| #=== 2. Until this point, no data packet exchange with database server.
| OracleCommand cmd = conn.CreateCommand();
| cmd.CommandText = sql;
| using (OracleDataReader rdr = cmd.ExecuteReader()) #=== 3. ExecuteReader() sends the Command Text to the connection, then database session starts from then.
|
|
|
|__ o. Only after above #3, the client_identifier has value. In prior, the value would be NULL.
<7> Reference:
|
|__ o. https://www.integrigy.com/oracle-security-blog/oracle-audit-vault-oracle-client-identifier-and-last-login
|
|__ o. https://oracle-base.com/articles/misc/dbms_session
Your Comments