Why have I written two blog entries on Authentication?
The United States Office of Personnel Management is notifying 2.7 million people that their data were stolen. The blame lays at the feet of the people responsible for these data. While the ultimate responsibility rests with the executives who did not fund investment into data security, I shoulder that responsibility daily.
I ask myself, what am I doing to improve (and challenge) our security posture. I have been asking: is there a better way? The question resulted in 21OCT15 blog entry exploring APEX native authentication versus custom authentication. From that, I’ve adopted apex_util.strong_password_check. Great utility and it can be used with custom authentication.
The next step in improving our security posture is to be able to offer two-step, or multifactor authentication within APEX applications.
Topics
Conclusion
Not every Oracle APEX application requires MFA, but maybe having it at hand will encourage me to use it where appropriate. For example, I’ve wanted a password vault in APEX to store the dozen of passwords related to our infrastructure. One might use MFA for critical administrative functions in the same way Google does. Google asks me for a two-step process when modifying company settings, but I get access to email more easily.
With the tools and building blocks at hand, I am more likely to include it where needed.
Building Blocks
- Ability to Send SMS messages from APEX
- Means to generate and store SMS messages
- Means to validate SMS messages
Sending SMS Messages
Patrick Wolf wrote a blog entry 4MAY2007 entitled Sending an SMS message to a phone. I picked this nugget up and even used the same vendor: ESendex. It works brilliantly. The vendor took a few weeks to adopt support for my back-woods mobile carrier in the US. I dropped Patrick’s work into a package. Whether you use Patrick’s or write one of your own using APEX_WEB_SERVICE.MAKE_REST_REQUEST with another vendor, it will take time and effort to confirm you are able to send SMS messages from APEX. A quick search for “SMS rest api” gave me a fair number to pick from. You’ll need this working and fees paid before marching on.
Generate MFA Text
You’ll need a means of generating text for your message and a means of storing the texts, then comparing the text. My approach was to create a global temporary table. These data disappear and I am not passing session items around.
CREATE GLOBAL TEMPORARY TABLE VLT_SMS (
MSG_TEXT VARCHAR2(20 BYTE),
VALID_TIL TIMESTAMP (6)
) ON COMMIT PRESERVE ROWS ;
Then we need a method for dropping data into this table that limit options and establish criteria for a successful validation:
- Generate a random set of 6 digits
- to_char(round(dbms_random.value(‘100000′,’999999’),0))
- A time limit – I sent 10 minutes, easy to titrate as needed.
- current_timestamp + interval ’10’ minute
- Limit valid texts, delete old failed efforts – easily accomplished in a global temporary table.
procedure generate_mfa_text (
P_SMS_NUMBER varchar2
)
as
l_msg_text varchar2(6);
l_timestamp timestamp;
l_select_count number;
begin
if P_SMS_NUMBER is null then
raise_application_error (-20000,
'Error SMS number is null in vlt_mfa_pkg.generate_mfa_text');
end if;
-- note: there is a separate procedure that validates the SMS number
select
to_char(round(dbms_random.value('100000','999999'),0)) ,
current_timestamp + interval '10' minute
into
l_msg_text,
l_timestamp
from dual;
delete from vlt_sms;
insert into vlt_sms (
msg_text,
valid_til
) values (
l_msg_text,
l_timestamp
);
vlt_mfa_pkg.sendSMS(
P_RECIPIENT => P_SMS_NUMBER,
P_BODY => l_msg_text
);
end generate_mfa_text;
Validate SMS message
In my trial application which I call “Vault” out of some sense of irony, I validate SMS messages in two places. The first place is on the page where I edit a user’s profile. I put a button that reads: “Validate SMS” with the cute little fa-phone image next to it. Hit that, that process goes. The second place is on a new page cleverly numbered 102. Page 102 follows page 101 in the login process. You fail the MFA, APEX dumps you to the login.
I did all of my exploring and failing at the validate SMS page while editing the user.
You’ll note that I use the current time stamp in the validation process.
I just created a little function in my package.
- When SMS is valid, returns true and erases the text message from the table
- When SMS is not valid, I just get a false.
function valid_mfa (
P_MSG_TEXT IN VARCHAR2
) return boolean
as
l_timestamp timestamp;
l_select_count number;
l_return boolean := false;
BEGIN
l_timestamp := current_timestamp;
select count(*) into l_select_count
from vlt_sms
where
msg_text = P_MSG_TEXT
and valid_til > l_timestamp;
if l_select_count = 1 then
l_return := true;
delete from vlt_sms;
end if; -- l_select_count
return l_return;
END valid_mfa;
Validate SMS Number
The user profile table needs to store the SMS number as well a means of knowing if it has been validated. I opted for a field in the user profile table called “sms_valid_date”. If “sms_valid_date” not null, then the SMS number has been validated.
On the user profile page in an APEX application, there a few key elements:
- A validate SMS button
- A text item for the message the user types
- A process to validate
declare l_valid_text boolean;
begin
l_valid_text := vlt_mfa_pkg.valid_mfa(
:P92000_MSG_TEXT
);
if :P92000_USER_PK is not null then
if l_valid_text then
update vlt_user set
sms_valid_date = sysdate
where user_pk = :P92000_USER_PK;
else
update vlt_user set
sms_valid_date = NULL
where user_pk = :P92000_USER_PK;
end if; -- valid text
end if; -- user not null
return l_valid_text;
end;
Integration
Now when I am at the login process, I have all the bits and pieces I need.
- Ability to send SMS
- Generated and stored text with a time limit
- A validated SMS number
- A means of knowing the SMS number is valid
In my APEX application, I created a page 102, on that page I have three six items:
- P102_LOGIN_ATTEMPTS (hidden)
- P102_USER_PK (hidden)
- P102_SMS_NUMBER (hidden)
- P102_VALID_TIL (display only)
- P102_MSG_TEXT (text field)
- P102_MESSAGE (display only)
I have two buttons:
- Cancel (submit page as CANCEL)
- Validate (submit page as VALIDATE)
Pre-Rendering
At pre-rendering the page, you’ll need to generate and send the text message and initialize a few items. Because you’ve just come from the login page, normally 101, the username must be retrieved manually. The first steps include identifying the user, getting the SMS number and initializing the attempt counter. Because I am super nice and I wanted confirm the time process, I displayed the time on the page. This will likely disappear.
declare
l_select_count number;
l_valid_date date;
BEGIN
select count(user_pk) into l_select_count
from vlt_user
where upper(user_name) = upper(v('APP_USER'));
if l_select_count = 1 then
select
user_pk,
sms_number,
sms_valid_date
into
:P102_USER_PK,
:P102_SMS_NUMBER,
l_valid_date
from vlt_user
where upper(user_name) = upper(v('APP_USER'));
end if; -- select count = 1
if l_valid_date is null then
:P102_message := 'Mobile Number has never been validated. ';
end if;
if :P102_SMS_NUMBER is not null then
vlt_mfa_pkg.generate_mfa_text(
P_SMS_NUMBER => :P102_SMS_NUMBER
);
select to_char(localtimestamp + interval '10' minute, 'HH12:MI AM')
into :P102_VALID_TIL
from dual;
else
:P102_message := 'Mobile Number/SMS number does not exist';
end if; -- sms not null
if :P102_LOGIN_ATTEMPTS is null then
:P102_LOGIN_ATTEMPTS := 0;
end if; -- init login_attempt counter
END;
P102_MSG_TEXT
This page item has a validation process that is a PL/SQL function body that returns a boolean.
declare l_valid_text boolean;
begin
l_valid_text := vlt_mfa_pkg.valid_mfa(
:P102_MSG_TEXT
);
:P102_LOGIN_ATTEMPTS := :P102_LOGIN_ATTEMPTS + 1;
return l_valid_text;
end;
Validate Button
We want to limit the user to 3 attempts. After trying all sorts of bad ideas, the simplest approach was to disable the Validate button. Therefore, the validate button has a condition on it. It is a PL/SQL function body.
return :P102_LOGIN_ATTEMPTS < 3 and :P102_SMS_NUMBER is not null;
Branching
On a successful validation, then call your normal home page. When button Validate is pressed, branch to page 1.
On cancel, then force the user to log out. This trick I stole from the APEX application navigation bar list (application | shared components | lists). The call is a URL to &LOGOUT.
Make it work!
One last step tells APEX to hit Page 102 immediately following the login. Edit application properties, click on User Interface, then edit the Desktop user interface (the pencil icon). Change the Home URL to:
f?p=&APP_ID.:102:&SESSION.
as shown here.
Secret Hint for Oracle APEX Multifactor Authentication
Hey, once you get the SMS process working, disable the send SMS process while doing all of the testing and debugging in the application. Make it simple for yourself. Put a classic report on your apex page that displays the results of the global temporary table that holds the SMS message.
The SMS vendors may provide a few free pings, but you’ll use a few dozen implementing these processes. You don’t need to continue to pay for them. Before deployment, delete your report from Page 102 and your user profile page. And un-comment the send SMS feature.
</qed>
The post Oracle APEX Multifactor Authentication appeared first on Oracle Blog .