************************************************************ * 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