apex and virtual private database

44
Apex and Virtual Private Database Jeffrey Kemp InSync Perth, Nov 2013

Upload: jeffrey-kemp

Post on 20-Jun-2015

1.260 views

Category:

Technology


1 download

DESCRIPTION

Oracle Application Express and Oracle Row-Level Security (RLS) (aka Virtual Private Database) work very well together. Using RLS you can have one database serve different groups of users while virtually guaranteeing that no-one will be able to view or update data they aren't supposed to. That was the sales pitch. This presentation will be a case study on one small Apex application with complex security requirements. The author started with a complex solution using views which performed poorly and didn't satisfy all the user's requirements. When he switched to a solution using RLS, the application became significantly simpler and faster, and all user requirements were met.

TRANSCRIPT

Page 1: Apex and Virtual Private Database

Apex and Virtual Private Database

Jeffrey Kemp

InSync Perth, Nov 2013

Page 2: Apex and Virtual Private Database

Why use VPD?

• Security

• Simplicity

• Flexibility

• No backdoors

Page 3: Apex and Virtual Private Database
Page 4: Apex and Virtual Private Database

Acronym Overload

• Virtual Private Database

• Row Level Security

• Fine-Grained Access Control

Page 5: Apex and Virtual Private Database

His

tory

8i VPD introduced; supports tables and views

9i global application contexts support for synonyms policy groups

10g column-level privacy column masking static policies shared policies

11g integrated into Enterprise Manager

12c improved security for expdp fine-grained context-sensitive policies

Page 6: Apex and Virtual Private Database
Page 7: Apex and Virtual Private Database

Requirements

• Enterprise Edition

• execute on DBMS_RLS

Page 8: Apex and Virtual Private Database

Disclaimer

not an expert

expertise

Page 9: Apex and Virtual Private Database

Case Study: eBud

• Budgeting solution for a large government department

• Groups of users: “Super Admins”, “Finance”, “Managers”

• Super Admin: "access all areas"

• Finance: "access to most areas"

• Managers: "limited access"

Page 10: Apex and Virtual Private Database

eBud Data Model BUDGETS budget_id budget_owner budget_publicity

BUDGET_ENTRIES chart amount

COST_CENTRES cost_centre branch_code

USERS username role_list

Row-level security required

Page 11: Apex and Virtual Private Database

Solution #1 Query:

SELECT budget_id, name FROM budgets_vw WHERE budget_id = :b1;

View:

CREATE VIEW budgets_vw AS

SELECT * FROM budgets WHERE budget_owner = v('APP_USER');

Page 12: Apex and Virtual Private Database

Solution #2

V.P.D.

Image source: http://www.executiveinvestigationandsecurity.com/security/

Page 13: Apex and Virtual Private Database

Row Level Security The query you asked for:

SELECT budget_id, name FROM budgets WHERE budget_id = :b1;

What we executed:

SELECT budget_id, name FROM budgets WHERE budget_id = :b1

AND budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER');

(not exactly, but this gives the general idea)

Page 14: Apex and Virtual Private Database

Package spec PACKAGE vpd_pkg IS

PROCEDURE new_session;

FUNCTION budgets_policy

( object_schema IN VARCHAR2

, object_name IN VARCHAR2

) RETURN VARCHAR2;

END vpd_pkg;

Page 15: Apex and Virtual Private Database

Initialise an Apex Session

PROCEDURE new_session IS

BEGIN

set_context('APP_USER', v('APP_USER'));

set_context('SUPERADMIN', is_superadmin);

set_context('FINANCE', is_finance_user);

END new_session;

Page 16: Apex and Virtual Private Database

Set Context PROCEDURE set_context ( i_attr IN VARCHAR2 , i_value IN VARCHAR2 ) IS BEGIN DBMS_SESSION.set_context ( namespace => 'EBUD_CTX' , attribute => i_attr , value => i_value , client_id => v('APP_USER') || ':' || v('SESSION') ); END set_context;

Page 17: Apex and Virtual Private Database

Create an Application Context

CREATE CONTEXT EBUD_CTX USING VPD_PKG ACCESSED GLOBALLY;

Page 18: Apex and Virtual Private Database

Apex Setup 1. Authentication Scheme

2. (no step 2!)

Page 19: Apex and Virtual Private Database
Page 20: Apex and Virtual Private Database

Policy Function body #1 FUNCTION budgets_policy

( object_schema IN VARCHAR2

, object_name IN VARCHAR2

) RETURN VARCHAR2 IS

BEGIN

RETURN q'[

budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER')

]';

END budgets_policy;

Page 21: Apex and Virtual Private Database

(old quote syntax) FUNCTION budgets_policy

( object_schema IN VARCHAR2

, object_name IN VARCHAR2

) RETURN VARCHAR2 IS

BEGIN

RETURN '

budget_owner = SYS_CONTEXT(''EBUD_CTX'',''APP_USER'')

';

END budgets_policy;

Page 22: Apex and Virtual Private Database

Create a Policy begin

DBMS_RLS.add_policy

( object_name => 'BUDGETS'

, policy_name => 'budgets_policy'

, policy_function => 'VPD_PKG.budgets_policy'

);

end;

/

Page 23: Apex and Virtual Private Database

Create a Policy begin

DBMS_RLS.add_policy

( object_name => 'BUDGETS'

, policy_name => 'budgets_policy'

, policy_function => 'VPD_PKG.budgets_policy'

, statement_types => 'SELECT'

);

end;

/

Page 24: Apex and Virtual Private Database

DBMS_RLS.add_policy • object_schema (NULL for current user) • object_name (table or view) • policy_name • function_schema (NULL for current user) • policy_function • statement_types

(default is SELECT, INSERT, UPDATE, DELETE) • policy_type • (other optional parameters)

Page 25: Apex and Virtual Private Database

How it works Query:

SELECT budget_id, name FROM budgets WHERE budget_id = :b1;

Parser calls function:

budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER')

Executed:

SELECT budget_id, name FROM ( SELECT * FROM budgets budgets WHERE budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER') ) WHERE budget_id = :b1;

Page 26: Apex and Virtual Private Database

Policy Function body #2 FUNCTION budgets_policy

(object_schema IN VARCHAR2

,object_name IN VARCHAR2

) RETURN VARCHAR2 IS

BEGIN

RETURN q'[

budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER')

OR budget_publicity = 'PUBLIC'

]';

END budgets_policy;

Page 27: Apex and Virtual Private Database

Policy Function body #3 FUNCTION budgets_policy

(object_schema IN VARCHAR2

,object_name IN VARCHAR2

) RETURN VARCHAR2 IS

BEGIN

RETURN q'[

budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER')

OR budget_publicity = 'PUBLIC'

OR (budget_publicity = 'FINANCE'

AND SYS_CONTEXT('EBUD_CTX','FINANCE') = 'Y')

OR SYS_CONTEXT('EBUD_CTX','SUPERADMIN') = 'Y'

]';

END budgets_policy;

Page 28: Apex and Virtual Private Database

Policy Function body #4 FUNCTION budgets_policy (object_schema IN VARCHAR2 ,object_name IN VARCHAR2 ) RETURN VARCHAR2 IS o_predicate VARCHAR2(4000); BEGIN IF SYS_CONTEXT('EBUD_CTX','SUPERADMIN') = 'Y' THEN o_predicate := ''; ELSE o_predicate := q'[ budget_publicity = 'PUBLIC' OR (budget_publicity = 'FINANCE' AND SYS_CONTEXT('EBUD_CTX','FINANCE') = 'Y') OR budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER') ]'; END IF; RETURN o_predicate; END budgets_policy;

Page 29: Apex and Virtual Private Database

Polic

y Fu

nct

ion

bo

dy

#5

FUNCTION budgets_policy (object_schema IN VARCHAR2 ,object_name IN VARCHAR2 ) RETURN VARCHAR2 IS o_predicate VARCHAR2(4000); BEGIN IF SYS_CONTEXT('EBUD_CTX','SUPERADMIN') = 'Y' THEN o_predicate := ''; ELSIF SYS_CONTEXT('EBUD_CTX','FINANCE') = 'Y' THEN o_predicate := q'[ budget_publicity IN ('PUBLIC','FINANCE') OR budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER') ]'; ELSE o_predicate := q'[ budget_publicity = 'PUBLIC' OR budget_owner = SYS_CONTEXT('EBUD_CTX','APP_USER') ]'; END IF; RETURN o_predicate; END budgets_policy;

lots of different queries in shared pool

Page 30: Apex and Virtual Private Database

Hierarch

y Division

Directorate

Branch

Cost Centre

Cost Centre

Branch

Cost Centre

Directorate

Branch

Cost Centre

Cost Centre

"Co

st C

entr

e G

rou

ps"

Page 31: Apex and Virtual Private Database

eBud Data Model BUDGETS budget_id budget_owner budget_publicity

COST_CENTRES cost_centre branch_code

USER_COST_CENTRES USERS username role_list

USER_COST_CENTRE_GROUPS group_code

COST_CENTRE_GROUPS parent_group_code

hierarchy

Page 32: Apex and Virtual Private Database

Cost Centre Policy

Function

FUNCTION cost_centre_policy (object_schema IN VARCHAR2, object_name IN VARCHAR2) RETURN VARCHAR2 IS

BEGIN

IF SYS_CONTEXT('EBUD_CTX','FINANCE') = 'Y' THEN

RETURN '';

ELSE

RETURN q'[

EXISTS (

SELECT null

FROM user_cost_centres ucc

WHERE ucc.username = SYS_CONTEXT('EBUD_CTX','APP_USER')

AND ucc.cost_centre = cost_centres.cost_centre

)

OR EXISTS (

SELECT null

FROM all_budget_branches_vw b

JOIN user_cost_centre_groups uccg

ON uccg.group_code IN

(b.branch_code, b.directorate_code, b.division_code)

WHERE uccg.username = SYS_CONTEXT('EBUD_CTX','APP_USER')

AND b.budget_id = cost_centres.budget_id

AND b.branch_code = cost_centres.branch_code

)

]';

END IF;

END cost_centre_policy;

we can refer to the table via its alias

Page 33: Apex and Virtual Private Database

Warning

Predicate MUST NOT query the table to which it is meant to be applied - not even via a view

Image source: http://en.wikipedia.org/wiki/Drawing_Hands

Page 34: Apex and Virtual Private Database

But…

The predicate may query another table that itself has an RLS policy.

Page 35: Apex and Virtual Private Database

Budget Entry Policy Function FUNCTION budget_entry_policy (object_schema IN VARCHAR2, object_name IN VARCHAR2) RETURN VARCHAR2 IS

BEGIN

IF SYS_CONTEXT('EBUD_CTX','FINANCE') = 'Y' THEN

RETURN '';

ELSE

RETURN q'[

EXISTS (

SELECT null

FROM cost_centres cc

WHERE cc.cost_centre = budget_entries.cost_centre

AND cc.budget_id = budget_entries.budget_id

)

]';

END IF;

END budget_entry_policy;

Page 36: Apex and Virtual Private Database

Policy Type parameter (10g+)

Re-Executed for each for all

statement DYNAMIC (default)

object STATIC SHARED_STATIC

context CONTEXT_SENSITIVE SHARED_CONTEXT_SENSITIVE

If in doubt, always start with the default - DYNAMIC

consider SHARED_... if your policy function is shared amongs multiple tables

The policy type parameter is just for performance optimisation.

Page 37: Apex and Virtual Private Database

Improved in 12c

Fine-grained Context Sensitive policies

– new parameters for DBMS_RLS.add_policy: namespace and attribute

– new procedure DBMS_RLS.add_policy_context

– improved performance

Page 38: Apex and Virtual Private Database

Bypassing VPD

• Not enforced for DIRECT path export

• Grant EXEMPT ACCESS POLICY

• Return NULL for object owner: IF object_schema = USER THEN RETURN ''; END IF;

Page 39: Apex and Virtual Private Database

Errors

• ORA-28112: failed to execute policy function – the policy function raised an exception

• "Invalid SQL statement" – may be a syntax error in the generated SQL

• ORA-28115: policy with check option violation – policy has been applied to Insert, Update or Delete operations

• ORA-28133: full table access is restricted by fine-grained security – policy has been applied to Index operation

Page 40: Apex and Virtual Private Database

Tuning

• Set client_identifier to APP_USER:SESSION then call the policy function

• or, query v$vpd_policy to get the predicate(s) applied to the query

• or, get the final exact SQL statement from the trace file ALTER SESSION SET EVENTS '10730 trace name context forever, level 12';

Page 41: Apex and Virtual Private Database

Recommendations

• Use q'{ syntax for predicates }'

• Understand how Apex Sessions work

• Use context for variables

– avoid injecting literals

– avoid calls to v() etc.

• Keep predicates simple

Page 42: Apex and Virtual Private Database

More Information

Read the Oracle Docs for:

– using policy groups

– automated policy creation in DDL triggers

– integration with Oracle Label Security

– data dictionary views

– Oracle Data Redaction

Page 43: Apex and Virtual Private Database

Oracle Docs

Oracle Database Security Guide:

Using Oracle Virtual Private Database to Control Data Access http://bit.ly/16Iq5EQ

Oracle Database PL/SQL Packages and Types Reference:

DBMS_RLS http://bit.ly/1abI46V

Page 44: Apex and Virtual Private Database

Thank you jeffkemponoracle.com

Image source: http://www.toothpastefordinner.com/index.php?date=082609