Apex and Virtual Private Database

2,420 views

Published on

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.

Published in: Technology, Business
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,420
On SlideShare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
51
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Apex and Virtual Private Database

  1. 1. Apex and Virtual Private Database Jeffrey Kemp InSync Perth, Nov 2013
  2. 2. Why use VPD? • • • • Security Simplicity Flexibility No backdoors
  3. 3. Acronym Overload • Virtual Private Database • Row Level Security • Fine-Grained Access Control
  4. 4. VPD introduced; supports tables and views 9i History 8i 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
  5. 5. Requirements • Enterprise Edition • execute on DBMS_RLS
  6. 6. Disclaimer not an expert expertise
  7. 7. 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"
  8. 8. eBud Data Model BUDGETS budget_id budget_owner budget_publicity COST_CENTRES cost_centre branch_code BUDGET_ENTRIES chart amount USERS username role_list Row-level security required
  9. 9. 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');
  10. 10. Solution #2 V.P.D. Image source: http://www.executiveinvestigationandsecurity.com/security/
  11. 11. 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)
  12. 12. 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;
  13. 13. 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;
  14. 14. 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;
  15. 15. Create an Application Context CREATE CONTEXT EBUD_CTX USING VPD_PKG ACCESSED GLOBALLY;
  16. 16. Apex Setup 1. Authentication Scheme 2. (no step 2!)
  17. 17. 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;
  18. 18. (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;
  19. 19. Create a Policy begin DBMS_RLS.add_policy ( object_name => 'BUDGETS' , policy_name => 'budgets_policy' , policy_function => 'VPD_PKG.budgets_policy' ); end; /
  20. 20. Create a Policy begin DBMS_RLS.add_policy ( object_name , policy_name , policy_function , statement_types ); end; / => => => => 'BUDGETS' 'budgets_policy' 'VPD_PKG.budgets_policy' 'SELECT'
  21. 21. 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)
  22. 22. 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;
  23. 23. 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;
  24. 24. 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;
  25. 25. 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;
  26. 26. Policy Function body #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; lots of different queries in shared pool END budgets_policy;
  27. 27. Directorate Branch Cost Centre Directorate Branch Cost Centre Cost Centre Branch Cost Centre Cost Centre Hierarchy "Cost Centre Groups" Division
  28. 28. eBud Data Model BUDGETS budget_id budget_owner budget_publicity USER_COST_CENTRES COST_CENTRES cost_centre branch_code USERS username role_list COST_CENTRE_GROUPS parent_group_code USER_COST_CENTRE_GROUPS group_code hierarchy
  29. 29. 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; we can refer to the table via its alias END cost_centre_policy; Cost Centre Policy Function
  30. 30. 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
  31. 31. But… The predicate may query another table that itself has an RLS policy.
  32. 32. 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;
  33. 33. Policy Type parameter (10g+) Re-Executed statement for each for all DYNAMIC (default) object STATIC SHARED_STATIC context CONTEXT_SENSITIVE SHARED_CONTEXT_SENSITIVE consider SHARED_... if your policy function is shared amongs multiple tables If in doubt, always start with the default - DYNAMIC The policy type parameter is just for performance optimisation.
  34. 34. 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
  35. 35. 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;
  36. 36. 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
  37. 37. 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';
  38. 38. 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
  39. 39. 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
  40. 40. 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
  41. 41. Thank you jeffkemponoracle.com Image source: http://www.toothpastefordinner.com/index.php?date=082609

×