AAIT#35643 OTP/2 factor authentication

svn path=/Website/trunk/; revision=28519
This commit is contained in:
Jos Groot Lipman
2016-03-17 14:05:20 +00:00
parent d3f7ba5e56
commit 2976bebc79
11 changed files with 310 additions and 110 deletions

View File

@@ -26,17 +26,19 @@ var itsme = (prs_key == user_key);
var no_ins = getQParamInt("no_ins", 0) == 1; // onderdruk objecten, die zitten al in een ander frame
var sql = "SELECT prs_srtperslid_key"
+ ", prs_perslid_otpsecret"
+ ", prs_perslid_verwijder"
+ " , prs_perslid_otpsecret"
+ " , prs_perslid_verwijder"
+ " , prs_perslid_wachtwoord_hash"
+ " FROM prs_perslid"
+ " WHERE prs_perslid_key = " + prs_key;
var oRs = Oracle.Execute(sql);
if (oRs.Eof)
shared.record_not_found();
shared.record_not_found(); // Kan gebeuren met heel oude gepinde tabbladen
var prs_deleted = oRs("prs_perslid_verwijder").value != null;
var prs_srtkey = oRs("prs_srtperslid_key").value;
var otpsecret = oRs("prs_perslid_otpsecret").Value;
var haspass = oRs("prs_perslid_wachtwoord_hash").Value != null;
oRs.Close();
// TODO autorisaties checken
@@ -137,7 +139,11 @@ oRs.Close();
if (S("qrc_enable") == 1) {
buttons.push({ title: L("lcl_qrc_authenticatie"), icon: "qr_code.png", action: "prs_connectmobile()", id: "bconnect_perslid" });
}
if (otpsecret) {
if (otpsecret // zonder pass nog maar even niet || S("prs_password_otp_mode")==3
|| S("prs_password_otp_mode")==2 && haspass
|| S("prs_password_otp_mode")==1 && user.checkAutorisation("WEB_PRSSYS", true)
)
{
buttons.push({ title: L("lcl_otp_authenticatie"), icon: "otp_code.png", action: "prs_showotp()", id: "otp_perslid" });
}
buttons.push({ title: L("lcl_password_title"), icon: "wall_brick.png", action: "prs_changepwd()", id: "bchange_pwd" });

View File

@@ -85,6 +85,12 @@ function FcltSyncgetJSON(url, data)
return globalData;
}
// Handig, komt heel veel voor
function gen_cancel()
{
FcltMgr.closeDetail(window, { cancel: true } );
}
// TODO Alleen als Logging aan?
$.ajaxSetup(
{"error":function(XMLHttpRequest,textStatus, errorThrown)

View File

@@ -4,7 +4,7 @@
$Id$
File: prs_perslid_otp.asp
Description: Genereert en toont de persoonlijke QR voor de Google Authenticator
Description: Genereert en toont de *bestaande* QRcode voor de Google Authenticator
Parameters:
Context:
Note: Vooralsnog niet bedoeld voor het grote publiek
@@ -13,14 +13,14 @@
<!--#include file="../Shared/common.inc"-->
<!--#include file="../Shared/iface.inc"-->
<!--#include file="../Shared/login.inc"-->
<!--#include file="../Shared/encoding.inc"-->
<%
var sql = "SELECT prs_perslid_otpsecret"
+ " FROM prs_perslid"
+ " WHERE prs_perslid_key = " + user_key;
var oRs = Oracle.Execute(sql);
var otpsecret = oRs("prs_perslid_otpsecret").Value;
oRs.Close();
user.auth_required_or_abort(otpsecret != null);
var otpsecret = user.otpsecret();
if (!otpsecret)
{
Response.Redirect("prs_perslid_otp_new.asp");
Response.End;
}
var otpresult = otpcodes(otpsecret);
%>
<html>
@@ -41,6 +41,10 @@ var otpresult = otpcodes(otpsecret);
pp.width(pw - ww);
setTimeout("updateProgress("+(ppos+1)+")", 1000 * <%=otpresult.otpstep%> / pw);
}
function new_otp()
{
window.location.href = "prs_perslid_otp_new.asp";
}
</script>
<style>
geenhekjeaanbeginregel,#progress {
@@ -56,16 +60,16 @@ var otpresult = otpcodes(otpsecret);
<body class="modal" id="mod_authQR">
<%
BLOCK_START("", L("lcl_otp_authenticatie"));
if (0) { // Vooralsnog uitgeschakeld
BLOCK_START("", L("lcl_otp_authenticatie_activate"));
var secret32 = binary_to_base32(hex_to_binary(otpresult.otpseed));
%>
<div id="myQR">Installeer de Google Authenticator op uw telefoon en scan daarin onderstaande QR code.</div>
<div id="myQR"><%=L("lcl_otp_auth_header").format(secret32)%></div>
<center><img class="QRC" src='./qrcodeotp.asp?size=4'><br></center>
<div id="footer"><%=L("lcl_otp_auth_footer")%></div>
<%
}
Response.Write("<table><tr><td>");
if (user.checkAutorisation("WEB_FACFAC", true))
{
Response.Write("Omdat je WEB_FACFAC hebt zie je onderstaande:<table><tr><td>");
for (var i = 0; i < otpresult.codes.length; i++)
{
var otpshould = otpresult.codes[i].otpshould;
@@ -82,7 +86,10 @@ if (0) { // Vooralsnog uitgeschakeld
Response.Write("</td><td style='vertical-align:middle'><div id='progress'>&nbsp;</div></td></tr></table>");
BLOCK_END();
var buttons = [ { icon: "page_refresh.png", title: L("lcl_refresh"), action: "FcltMgr.reload()" }
}
var buttons = [ { icon: "page_refresh.png", title: L("lcl_new_otp"), action: "new_otp()" },
{title: L("lcl_cancel"), icon: "cancel.png", action: "gen_cancel()" }
];
CreateButtons(buttons);

View File

@@ -0,0 +1,72 @@
<%@language = "javascript" %>
<% /*
$Revision$
$Id$
File: prs_perslid_otp_new.asp
Description: Genereert en toont de persoonlijke QR voor de Google Authenticator
Parameters:
Context:
Note: Vooralsnog niet bedoeld voor het grote publiek
*/ %>
<!--#include file="../Shared/common.inc"-->
<!--#include file="../Shared/iface.inc"-->
<!--#include file="../Shared/login.inc"-->
<!--#include file="../Shared/encoding.inc" -->
<%
var raw = shared.random(20);
var newsecret = '';
for (var i = 0; i < raw.length; i++)
{
var chr = raw.charCodeAt(i);
if (i < 16)
newsecret += "0";
newsecret += i.toString(16);
}
Session("otp_secret_temp") = '1$30$6$0$' + newsecret;
//__Log("newsecret: " + newsecret);
%>
<html>
<head>
<% FCLTHeader.Generate();
%>
<script type='text/javascript'>
function otp_accept()
{
var otp_code = $("#otpcode").val();
if (otp_code)
{
var data = { otp_code: otp_code };
<% protectRequest.dataToken("data"); %>
$.post("prs_perslid_otp_new_save.asp", data, FcltCallbackClose, "json");
}
}
</script>
</head>
<body class="modal" id="mod_authQR">
<%
BLOCK_START("", L("lcl_otp_authenticatie_activate"));
var secret32 = binary_to_base32(hex_to_binary(newsecret));
%>
<div id="myQRheader"><%=L("lcl_otp_auth_header").format(secret32)%></div>
<center><img class="QRC" src='./qrcodeotp.asp?size=4&temp=1'></center>
<div id="myQRfooter"><%=L("lcl_otp_auth_footer")%></div>
<br>
<label for='otpcode'><%=L("lcl_otp_auth_verification")%></label>
<br><input id='otpcode' name='otpcode' type='text'>
<%
BLOCK_END();
var buttons = [ {title: L("lcl_submit"), icon: "opslaan.png", action: "otp_accept()", singlepress: true, id: "btn_accept_submit" },
{title: L("lcl_cancel"), icon: "cancel.png", action: "gen_cancel()" } ];
CreateButtons(buttons);
IFACE.FORM_END();
%>
</body>
</html>

View File

@@ -11,81 +11,38 @@
*/ %>
<%
DOCTYPE_Disable = true;
DOCTYPE_Disable = true; // We doen een image/png
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../Shared/login.inc" -->
<!-- #include file="../Shared/json2.js" -->
<!-- #include file="../Shared/encoding.inc" -->
<%
var sql = "SELECT prs_perslid_oslogin,"
+ " prs_perslid_otpsecret"
+ " FROM prs_perslid"
+ " WHERE prs_perslid_key = " + user_key;
var oRs = Oracle.Execute(sql);
var otpsecret = oRs("prs_perslid_otpsecret").Value;
var oslogin = oRs("prs_perslid_oslogin").Value;
oRs.Close();
user.auth_required_or_abort(otpsecret != null);
var otpsecret = user.otpsecret();
var oslogin = user.oslogin();
if (getQParamInt("temp", 0))
{
var otpsecret = Session("otp_secret_temp");
user.auth_required_or_abort(otpsecret);
}
var otpresult = otpcodes(otpsecret);
if (otpresult.otpstep != 30)
{
oWhip2png = Server.CreateObject("SLNKDWF.Whip2PNG"); // Hopen dat dit wel lukt
var oWhip2png = Server.CreateObject("SLNKDWF.Whip2PNG"); // Hopen dat dit wel lukt
Response.clear;
Response.contenttype = "image/png";
Response.BinaryWrite(oWhip2png.TextAsPNG("Sorry, Google Authenticator cannot handle {0} second stepsize".format(otpresult.otpstep)));
Response.End();
}
var base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
function binary_to_base32(input) {
var ret = new Array();
var ret_len = 0;
var i = 0;
var unpadded_length = input.length;
while (input.length % 5) {
input[input.length] = 0;
}
for(i=0; i<input.length; i+=5) {
ret += base32_chars.charAt((input[i] >> 3));
ret += base32_chars.charAt(((input[i] & 0x07) << 2) | ((input[i+1] & 0xc0) >> 6));
if (i+1 >= unpadded_length) {
ret += "======"
break;
}
ret += base32_chars.charAt(((input[i+1] & 0x3e) >> 1));
ret += base32_chars.charAt(((input[i+1] & 0x01) << 4) | ((input[i+2] & 0xf0) >> 4));
if (i+2 >= unpadded_length) {
ret += "===="
break;
}
ret += base32_chars.charAt(((input[i+2] & 0x0f) << 1) | ((input[i+3] & 0x80) >> 7));
if (i+3 >= unpadded_length) {
ret += "==="
break;
}
ret += base32_chars.charAt(((input[i+3] & 0x7c) >> 2));
ret += base32_chars.charAt(((input[i+3] & 0x03) << 3) | ((input[i+4] & 0xe0) >> 5));
if (i+4 >= unpadded_length) {
ret += "="
break;
}
ret += base32_chars.charAt(((input[i+4] & 0x1f)));
}
return ret;
}
var binary = new Array();
for (var i=0; i<otpresult.otpseed.length/2; i++)
{
var h = otpresult.otpseed.substr(i*2, 2);
binary[i] = parseInt(h,16);
}
var binary = hex_to_binary(otpresult.otpseed);
var secret32 = binary_to_base32(binary); // 3132333435363738393031323334353637383930 ==> GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
var issuer = "FACILITOR";
var txt = "otpauth://totp/{0}:{1}/{2}?secret={3}&issuer={0}".format(issuer, customerId, oslogin, secret32)
var issuer = L("lcl_otp_issuer").format(HTTP.urlzelf());
var friendly = L("lcl_otp_friendly").format(customerId, oslogin, user.naam());
var txt = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}".format(issuer, friendly, secret32)
__Log("QR Code: " + txt);
var xc = Server.CreateObject("SLNKDWF.QRCode");

View File

@@ -416,48 +416,50 @@ function setpassword(prs_key, wachtwoord)
}
}
function testotp (user_key, otprequest)
function testotp (prs_key, otprequest)
{
var sql = " SELECT prs_perslid_otpsecret"
+ " , prs_perslid_otpcounter"
+ " FROM prs_perslid"
+ " WHERE prs_perslid_key = " + user_key;
+ " WHERE prs_perslid_key = " + prs_key;
var oRs = Oracle.Execute(sql);
var otpsecret = oRs("prs_perslid_otpsecret").Value;
var otpcounter = oRs("prs_perslid_otpcounter").Value || -1;
oRs.Close()
if (otpsecret) // Die eerst maar eens controleren
{
var otpresult = otpcodes(otpsecret);
oRs.Close();
__Log("Otprequest: " + otprequest);
var otp_oke = false;
if (otprequest.length == otpresult.otpsize && otprequest.match(/^[0-9]*$/)) // quick check exact 6 cijfers
return verify_otp(otprequest, otpsecret, otpcounter);
}
function verify_otp (otprequest, otpsecret, otpcounter)
{
var otpresult = otpcodes(otpsecret);
__Log("Otprequest: " + otprequest);
var otp_oke = false;
if (otprequest.length == otpresult.otpsize && otprequest.match(/^[0-9]*$/)) // quick check exact 6 cijfers
{
for (var i = 0; i < otpresult.codes.length && !otp_oke; i++)
{
for (var i = 0; i < otpresult.codes.length && !otp_oke; i++)
var code = otpresult.codes[i];
__Log(code);
if (code.counter > otpcounter) // Hij mag niet eerder toegepast zijn
{
var code = otpresult.codes[i];
__Log(code);
if (code.counter > otpcounter) // Hij mag niet eerder toegepast zijn
var otpshould = code.otpshould;
if (otprequest == code.otpshould)
{
var otpshould = code.otpshould;
if (otprequest == code.otpshould)
{
otp_oke = true;
otpcounter = code.counter;
}
otp_oke = true;
otpcounter = code.counter; // TODO: We zouden moeten bijwerken
}
}
}
if (!otp_oke)
{
__Log("OTP check failed");
__Log(otpresult);
return false;
}
// else zorgen dat straks bij succesvolle login otpcounter wordt gezet
}
if (!otp_oke)
{
__Log("OTP check failed");
__Log(otpresult);
return false;
}
return otp_oke;
}

View File

@@ -53,6 +53,7 @@ Perslid.prototype._require_prs_perslid = function __require_prs_perslid()
+ " , p.prs_perslid_oslogin"
+ " , p.prs_perslid_oslogin2"
+ " , p.prs_perslid_wachtwoord_hash"
+ " , p.prs_perslid_otpsecret"
+ " , p.alg_onroerendgoed_keys"
+ " , p.alg_onroerendgoed_type"
+ " , p.prs_perslid_bezetdatum"
@@ -86,6 +87,7 @@ Perslid.prototype._require_prs_perslid = function __require_prs_perslid()
this._prs_perslid_oslogin = oRs("prs_perslid_oslogin").value;
this._prs_perslid_oslogin2 = oRs("prs_perslid_oslogin2").value;
this._prs_perslid_haspw = (oRs("prs_perslid_wachtwoord_hash").value != null);
this._prs_perslid_otpsecret = oRs("prs_perslid_otpsecret").value;
this._alg_onroerendgoed_keys = oRs("alg_onroerendgoed_keys").value;
this._alg_onroerendgoed_type = oRs("alg_onroerendgoed_type").value;
this._prs_perslid_bezetdatum = oRs("prs_perslid_bezetdatum").value;
@@ -342,6 +344,12 @@ Perslid.prototype.haspw = function()
return this._prs_perslid_haspw;
}
Perslid.prototype.otpsecret = function()
{
this._require_prs_perslid();
return this._prs_perslid_otpsecret;
}
Perslid.prototype.alg_onroerendgoed_keys = function()
{
this._require_prs_perslid();

63
APPL/Shared/encoding.inc Normal file
View File

@@ -0,0 +1,63 @@
<%
/*
$Revision$
$Id$
File: encoding.inc
Description: Handige functie
Parameters:
Context:
Note:
*/
function hex_to_binary(seed)
{
var binary = new Array();
for (var i=0; i<seed.length/2; i++)
{
var h = seed.substr(i*2, 2);
binary[i] = parseInt(h,16);
}
return binary;
}
var base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
function binary_to_base32(input) {
var ret = new Array();
var ret_len = 0;
var i = 0;
var unpadded_length = input.length;
while (input.length % 5) {
input[input.length] = 0;
}
for(i=0; i<input.length; i+=5) {
ret += base32_chars.charAt((input[i] >> 3));
ret += base32_chars.charAt(((input[i] & 0x07) << 2) | ((input[i+1] & 0xc0) >> 6));
if (i+1 >= unpadded_length) {
ret += "======"
break;
}
ret += base32_chars.charAt(((input[i+1] & 0x3e) >> 1));
ret += base32_chars.charAt(((input[i+1] & 0x01) << 4) | ((input[i+2] & 0xf0) >> 4));
if (i+2 >= unpadded_length) {
ret += "===="
break;
}
ret += base32_chars.charAt(((input[i+2] & 0x0f) << 1) | ((input[i+3] & 0x80) >> 7));
if (i+3 >= unpadded_length) {
ret += "==="
break;
}
ret += base32_chars.charAt(((input[i+3] & 0x7c) >> 2));
ret += base32_chars.charAt(((input[i+3] & 0x03) << 3) | ((input[i+4] & 0xe0) >> 5));
if (i+4 >= unpadded_length) {
ret += "="
break;
}
ret += base32_chars.charAt(((input[i+4] & 0x1f)));
}
return ret;
}
%>

View File

@@ -3,8 +3,12 @@
$Revision$
$Id$
We weten echt niet wie de gebruiker is.
Toon maar een inlogscherm
File: login.asp
Description: We weten echt niet wie de gebruiker is.
Toon maar een inlogscherm
Parameters:
Context:
Note:
Uiteindelijk: redirect terug naar de 'aanroepende' pagina die is meegegeven.
*/
@@ -28,7 +32,6 @@ if (Request.QueryString.Count > 0)
var ret_page = getQParam("ret_page", rooturl + "/");
var querystring = getQParam("querystring", "");
// Echt niet automatisch gelukt. loginscherm
FCLTHeader.Requires({plugins: ["jQuery"], js:["jQuery-ui.js"]});
checkUserAgent(); // heeft device capability bits gezet
@@ -43,7 +46,21 @@ if (device.test(device.isMobile))
<script type="text/javascript">
function login_callback(json)
{
if (json.success)
if (json.success && json.otp_hmac)
{
var otp_code = prompt(json.otp_prompt);
if (otp_code)
{
var data = { otp_user_key: json.otp_user_key,
otp_hmac: json.otp_hmac,
otp_code: otp_code,
remember: $("remember").is(":checked")?"on":""
};
<% protectRequest.dataToken("data"); %>
$.post("login_otp_save.asp", data, FcltCallbackAndThen(login_callback), "json");
}
}
else if (json.success)
{
window.location.href = "<%=safe.jsstring(ret_page + (querystring?"?" + querystring:""))%>";
}

View File

@@ -0,0 +1,47 @@
<%@language = "javascript" %>
<% /*
$Revision$
$Id$
File: login_otp_save.asp
Description: Van in ingevulde OTP-code op en controleer deze
Parameters:
Context: Vanuit inlogscherm nadat gebruiker username/wachtwoord
al heeft opgegeven.
Note:
*/
%>
<%
var JSON_Result = true;
var ANONYMOUS_Allowed = 1;
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../Shared/login.inc" -->
<!-- #include file="../Shared/json2.js" -->
<%
var otp_user_key = getFParamInt("otp_user_key");
var otp_hmac = getFParam("otp_hmac");
var otp_code = getFParam("otp_code");
var remember = getFParam("remember", "off")=="on";
protectHMAC.verify(String(otp_user_key), otp_hmac, { expire: 600 }); // 10 minuten proberen is al veel te lang
if (testotp(otp_user_key, otp_code))
doLogin(otp_user_key);
result = { success: user_key > 0 };
if (user_key > 0)
{
if (S("login_remember_days") > 0 && remember && user_key > 0)
makeSessionCookie("Remember Login");
}
else
{
result.message = login_fail_reason;
}
Response.Write(JSON.stringify(result));
Response.End;
%>

View File

@@ -26,10 +26,25 @@ if (user_key > 0)
{
if (S("login_remember_days") > 0 && remember && user_key > 0)
makeSessionCookie("Remember Login");
result.success = true;
}
else
{
result.message = login_fail_reason;
if (Session("otp_user_key") > 0)
{
var otp_user = new Perslid(Session("otp_user_key"));
var issuer = L("lcl_otp_issuer").format(HTTP.urlzelf());
var friendly = L("lcl_otp_friendly").format(customerId, otp_user.oslogin(), otp_user.naam());
result.otp_user_key = Session("otp_user_key");
result.otp_hmac = protectHMAC.create(String(result.otp_user_key));
result.otp_prompt = L("lcl_otp_enter").format(friendly);
result.success = true;
}
else
{
result.message = login_fail_reason;
}
}
Response.Write(JSON.stringify(result));
Response.End;