Topic List
Sage Computing Services
- browser cache
- url tampering - entry level
- session state protection
- hidden items
- xss - little cleverer
- sql injection - takes skill
- apex_dml_lock_wait_time
- transparent database encryption
- database xxim - starts at design
- remote queries
- vpd
- encryption
- authentication schemes
- ldap
- SSO
- user groups
- security plugins
- custom auth
- authorization schemes
- apex_application_files
- session timeout
- internal settings
- authorized urls
Oracle Apex 4.1 Security
Scott Wesley
Systems Consultant & Trainer
-- encryption
CREATE OR REPLACE FUNCTION sw_code(p_this IN VARCHAR2) RETURN RAW IS
v_length NUMBER;
v_sum VARCHAR2(32); -- hashed to 32
v_value VARCHAR2(100);
as0893j0f VARCHAR2(1) := '~';
f2no3ifn0a VARCHAR2(1) := 'o';
fn2oi3fm VARCHAR2(1) := '4';
s093mafm VARCHAR2(1) := 'A';
p23o02dm2 VARCHAR2(1) := '&';
n2oi3f8nc VARCHAR2(1) := 'g';
a01l2nlfi VARCHAR2(1) := 'w';
i92hf0xzz VARCHAR2(1) := '|';
q02n208n3 VARCHAR2(1) := '0';
-- This is not the actual key!
v_key_string CONSTANT VARCHAR2(8) := 'mR3s5fHq';
BEGIN
-- Get checksum of keyed value.
v_sum := dbms_obfuscation_toolkit.md5(input=>utl_raw.cast_to_raw(p_this));
v_value := -- Vary the substring length to get different amount of salt.
SUBSTR(v_sum,1,32)||
-- Second character of extra
SUBSTR(p_this, 2, 1)||
-- Last character of extra
SUBSTR(p_this, LENGTH(p_this), 1)||
-- More characters of key. Nbr used varies depending on amount of checksum used.
as0893j0f||fn2oi3fm||i92hf0xzz||n2oi3f8nc||as0893j0f||q02n208n3;
IF v_value IS NOT NULL THEN
RETURN utl_raw.cast_to_raw(v_value);
ELSE
RETURN NULL;
END IF;
END sw_code;
/
CREATE OR REPLACE FUNCTION sw_write(p_that VARCHAR2, p_this VARCHAR2) RETURN VARCHAR2 IS
v_string VARCHAR2(1024);
v_encrypted_string RAW(1024);
v_padding CONSTANT VARCHAR2(1) := CHR(0);
BEGIN
IF p_that IS NULL THEN
RETURN NULL;
ELSE
-- Pad it with up to 8 characters to ensure string to be encrypted is a multiple of 8 bytes long.
v_string := RPAD( p_that, CEIL(LENGTH(p_that)/8) *8, v_padding );
-- Encrypt and return the value.
dbms_obfuscation_toolkit.des3encrypt(input => utl_raw.cast_to_raw(v_string)
,key => sw_code(p_this)
,encrypted_data => v_encrypted_string
,which => dbms_obfuscation_toolkit.threekeymode
);
RETURN utl_raw.cast_to_varchar2(v_encrypted_string);
END IF;
END sw_write;
/
CREATE OR REPLACE FUNCTION sw_read(p_that VARCHAR2, p_this VARCHAR2) RETURN VARCHAR2 IS
v_string VARCHAR2(1024);
v_padding CONSTANT VARCHAR2(1) := CHR(0);
BEGIN
IF p_that IS NULL THEN
RETURN NULL;
ELSE
-- Ensure string to be read is multiple of 8 bytes long.
-- Determine value of string.
v_string := utl_raw.cast_to_varchar2(dbms_obfuscation_toolkit.des3decrypt
(input => utl_raw.cast_to_raw(p_that)
,key => sw_code(p_this)
,which => dbms_obfuscation_toolkit.threekeymode
));
-- Return decrypted value minus the padded characters.
RETURN RTRIM(v_string, v_padding);
END IF;
END;
/
ALTER TABLE demo_customers
DROP COLUMN credit_limit_enc;
--ALTER TABLE demo_customers
--ADD credit_limit_enc RAW(128);
ALTER TABLE demo_customers
ADD credit_limit_enc VARCHAR2(128);
CREATE OR REPLACE TRIGGER cust_enc_biur
BEFORE INSERT OR UPDATE ON demo_customers
FOR EACH ROW
WHEN (NEW.credit_limit IS NOT NULL)
BEGIN
:NEW.credit_limit_enc := sw_write(:NEW.credit_limit, :NEW.customer_id);
:NEW.credit_limit := NULL;
END;
/
UPDATE demo_customers
SET credit_limit = credit_limit;
col credit_limit format a20
col enc_limit format a20
SELECT customer_id
,credit_limit
,sw_read(credit_limit_enc, customer_id) enc_limit
,credit_limit_enc
FROM demo_customers;
-- vpd / context
http://www.talkapex.com/2010/07/poor-mans-vpd-in-apex-and-oracle-xe.html
http://www.google.com.au/url?sa=t&rct=j&q=vpd%20oracle%20apex&source=web&cd=9&ved=0CHYQFjAI&url=http%3A%2F%2Fd2850a.aderas.net%2Fpls%2Fapex%2FADERASWWW.download_my_file%3Fp_file%3D80513909248389119&ei=47irToS8AsqtiQfbz7zgDw&usg=AFQjCNHTd95ZXv5A_URxPwYwkfeVo5K6rg&sig2=rKjLciv8-zaG4QwLzmRjiQ
update demo_orders set user_name = 'SWESLEY' where rownum< 3;
select *
from demo_orders
where user_name = 'DEMO';
select count(*), user_name from demo_orders group by user_name;
-- enterprise edition
CREATE OR REPLACE PACKAGE ee_rls AS
PROCEDURE p_set_context;
FUNCTION f_ord_rls_policy
(object_schema IN VARCHAR2
,object_name IN VARCHAR2
) RETURN VARCHAR2;
END ee_rls;
/
CREATE CONTEXT ee_ctx USING ee_rls;
CREATE OR REPLACE PACKAGE BODY ee_rls AS
PROCEDURE p_set_context IS
BEGIN
dbms_session.set_context
(namespace => 'EE_CTX'
,attribute => 'USER_NAME'
,value => COALESCE(v('APP_USER')
,SYS_CONTEXT('USERENV'
,'SESSION_USER'))
);
END p_set_context;
FUNCTION f_ord_rls_policy
(object_schema IN VARCHAR2
,object_name IN VARCHAR2
) RETURN VARCHAR2 IS
BEGIN
RETURN 'user_name = '||q'[sys_context('EE_CTX','USER_NAME')]';
END f_ord_rls_policy;
END ee_rls;
/
begin
dbms_rls.drop_policy('AUSOUG','DEMO_ORDERS','ORD_RLS_POLICY');
end;
/
BEGIN
dbms_rls.add_policy
(object_schema => 'AUSOUG'
,object_name => 'DEMO_ORDERS'
,policy_name => 'ORD_RLS_POLICY'
,function_schema => 'AUSOUG'
,policy_function => 'ee_rls.f_ord_rls_policy'
,statement_types => 'SELECT');
END;
/
-- fetched from app_user
execute ee_rls.p_set_context;
select sys_context('EE_CTX','USER_NAME') from dual;
select count(*), user_name from demo_orders group by user_name;
-- cheapo
CREATE CONTEXT xe_ctx USING xe_rls;
CREATE OR REPLACE PACKAGE xe_rls AS
PROCEDURE p_set_context
(p_user_name IN VARCHAR2);
PROCEDURE p_clear_context;
END xe_rls;
/
CREATE OR REPLACE PACKAGE BODY xe_rls AS
PROCEDURE p_set_context IS
BEGIN
dbms_session.set_context
(namespace => 'XE_CTX'
,attribute => 'USER_NAME'
,value => v('APP_USER'));
END p_set_context;
PROCEDURE p_clear_context IS
BEGIN
dbms_session.clear_context('XE_CTX');
END p_clear_context;
END xe_rls;
/
exec xe_rls.p_set_context('ADMIN');
select sys_context('XE_CTX','USER_NAME') from dual;
CREATE OR REPLACE VIEW demo_orders_vw AS
SELECT *
FROM demo_orders
WHERE user_name = SYS_CONTEXT('XE_CTX', 'USER_NAME')
WITH CHECK OPTION
/
select count(*), user_name from demo_orders_vw group by user_name;
SQL> select count(*), user_name from demo_orders group by user_name;
COUNT(*) USER_NAME
---------- ------------------------------
2 ADMIN
8 DEMO
2 rows selected.
SQL> select count(*), user_name from demo_orders_vw group by user_name;
COUNT(*) USER_NAME
---------- ------------------------------
2 ADMIN
1 row selected.
-- apex_dml_lock_wait_time
BEGIN
FOR r_rec IN
(SELECT *
FROM demo_customers
WHERE customer_id = :p_id
FOR UPDATE OF credit_limit WAIT 0
) LOOP
-- do update...
null;
END LOOP;
END;
/
DECLARE
CURSOR c_sample IS
SELECT *
FROM demo_customers
WHERE customer_id = :p_id
FOR UPDATE OF credit_limit WAIT 0;
lr_sample c_sample%ROWTYPE;
BEGIN
OPEN c_sample;
FETCH c_sample
INTO lr_sample;
CLOSE c_sample;
END;
/
-- XSS
UPDATE demo_customers
SET cust_street_address1 = '<script>alert("danger");</script>'
WHERE customer_id = 1;
-- groups
DECLARE
VAL VARCHAR2(32765);
BEGIN
VAL := APEX_UTIL.GET_GROUPS_USER_BELONGS_TO(p_username => 'FRANK');
END;
DECLARE
VAL BOOLEAN;
BEGIN
VAL := APEX_UTIL.CURRENT_USER_IN_GROUP(p_group_name=>'Managers');
END;
-- custom
PROCEDURE post_authentication IS
lc_exists VARCHAR2(1);
BEGIN
SELECT 'Y'
INTO lc_exists
FROM dual
WHERE EXISTS
(SELECT null
FROM application_roles
WHERE username = :APP_USER);
apex_util.set_session_state('F_USR_TEAMLEADER', lc_exists);
END post_authentication;
-- files
INSERT INTO my_files (my_id, filename, blob_col, mime_type)
SELECT :P1_ID, filename, mime_type
FROM apex_application_files
WHERE name = :P1_FILENAME;
DELETE FROM apex_application_files
WHERE name = :P1_FILENAME;
-- sql injection
create table secrets (id number, secret varchar2(100));
insert into secrets values (1, 'Who shot JFK');
insert into secrets values (2, 'Coke''s secret ingredient');
insert into secrets values (3, 'Origin of the universe');
insert into secrets values (4, 'Which way to plug in a USB cable');
-- plugins
CREATE table app_roles (username VARCHAR2(200), role_name VARCHAR2(200));
insert into app_roles values ('SCOTT','OFFICER');
insert into app_roles values ('PENNY','MANAGER');
insert into app_roles values ('BRANKA','TEAM_LEADER');
http://chrisonoracle.wordpress.com/2011/05/09/how-to-create-an-apex-4-1-authorization-plugin/
FUNCTION is_authorised (
p_authorization IN apex_plugin.t_authorization,
p_plugin IN apex_plugin.t_plugin )
RETURN apex_plugin.t_authorization_exec_result
IS
lc_group VARCHAR2(4000) := p_authorization.attribute_01;
ln_count PLS_INTEGER;
lr_result apex_plugin.t_authorization_exec_result;
BEGIN
SELECT 1
INTO ln_count
FROM dual
WHERE EXISTS
(SELECT NULL
FROM apex_workspace_group_users
WHERE user_name = p_authorization.username
AND group_name = lc_group);
lr_result.is_authorized := ln_count > 0;
RETURN lr_result;
END is_authorised;
FUNCTION custom_auth (
p_authentication IN apex_plugin.t_authentication,
p_plugin IN apex_plugin.t_plugin,
p_password IN VARCHAR2)
RETURN apex_plugin.t_authentication_auth_result
IS
l_user demo_users.user_name%TYPE := UPPER(p_authentication.username);
l_entered_pwd demo_users.password%TYPE := p_authentication.attribute_02;
l_pwd demo_users.password%TYPE;
l_id demo_users.user_id%TYPE;
lr_result apex_plugin.t_authentication_auth_result;
BEGIN
pr_sw_dummy('l_user:'||l_user);
pr_sw_dummy('l_entered_pwd:'||l_entered_pwd);
SELECT user_id, password
INTO l_id, l_pwd
FROM demo_users
WHERE user_name = l_user;
lr_result.is_authenticated := l_pwd = custom_hash(l_user, l_entered_pwd);
IF NOT lr_result.is_authenticated THEN
lr_result.log_code := 4;
lr_result.log_text := 'Incorrect password';
END IF;
lr_result.log_code := 0;
RETURN lr_result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
lr_result.log_code := 1;
lr_result.log_text := 'Unknown user';
lr_result.is_authenticated := FALSE;
RETURN lr_result;
END;
update demo_users set password = custom_hash(user_name, 'ausoug');
"Thank you"
The road ahead
How many
you
did
see?
Systems Consultant & Trainer
sagecomputing.com.au
/*
Scott Wesley
*/
scott@sagecomputing.com.au
secure
entry level
that makes me a developer
Today's Architecture Diagram
Workstation
Medieval Castle Design
denial of service attack
apex_dml_lock_wait_time
Browser Security
Layer 8
f?p=147:8:3816416929962008::::P8_ID:24
user just modifies ID they're editing
f?p=147:8:3816416929962008::::P6_ROWID:AAAXMbAAGAAAD2lAAv
Apex now offers better security through obscurity
f?p=147:8:3816416929962008::::P8_ID:23&cs=3D4E91375C318A9AD184555771D961CB1
Checksums are easiest out of box solution to guard the gate
(Oracle) Application Server
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
"Defence in Depth" philosophy
Transparent Database Encryption
"stop the soldiers from reading data"
Web Browser
audience question!!
Authentication
Security Plug-ins
Identity Management
Custom Authentication
Schemes
Authorisation Plug-in
Authentication Plug-in
SSO
- Views on remote views
- Grants only to schemas that need it
out of the box
-- this is the documented example
better info for apex_workspace_access_log
other people's definition
Authorisation scheme definition
LDAP
Remote Queries
(apex_workspace_group_users)
User Groups
Authentication scheme definition
setup a scheme to allocate to components
talk directly with your LDAP server
or setup some PL/SQL to run queries against it
my favourite security setting
place your "schemes" in application items
allocate to your Apex users
because it solves a problem...
APIs exist to help
Authorisation
Session State Protection
Data Encryption
SQL Injection
Encrypting data in the db
/*+ DRIVING_SITE (alias_of_big_table) */
define a function to encypt
but allows mischief like this
can start at item level definition
will their roles change once logged in?
and the function to decrypt
specially privileged, of course
stop the average user from fiddling with the URL
kinda need to go out of your way
so read through the in-line documentation
automagically
INTERNAL
protects from internal viewing
- Limited access
- Contains views to tables
also evident if viewing directly
protect your hidden items
... the users see something a little nicer
unprotected - you can do this
Application
VPD
Enterprise Edition teaser
Poor man's VPD
Session
still isn't really that cheap...
Timeout
or if SSP applied just at page level
AKA: Fine Grained Access Control (FGAC)
granular settings by page
Workspace
Apex Application
honoured even if SSP is disabled
but if you must, use DBMS_ASSERT
Views Schema
Apex Application Security Attributes
"your greatest threats are typically from within"
Apex Application Files
XSS
queries
added to
all
WHERE user_name = :APP_USER
"beware the trojan horse"
Directory Service (AD/OID)
you could load as per 3.x
necessary with thanks to the session pool
clean
or load direct to your table
(Application Data)
and don't try this at home...
Remote Data Warehouse
Application Express
Oracle OLTP Database