javascriptcoldfusioncfctwo-factor-authentication

Integrating Duo Web 2 Factor Authentication with ColdFusion


I am trying integrate Duo Web 2FA with my ColdFusion application. My Coldfusion Server is Windows Server 2012 running Lucee 4.5.2.018 final. I am following the GitHub method as described here. I am receiving the following error message and I have no idea what it means or where to begin troubleshooting this:

invalid call of the function listGetAt, second Argument (posNumber) is 

invalid, invalid string list index [2]

The error occurred in C:\inetpub\wwwroot\serviceticket\test.cfm: line 82

80: <cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, AKEY, USER)>
81: 
82: <cfset valid_app_sig = ListGetAt(request_sig, 2, ":")>
83: 
84: 

Here is my test page code:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>2FA</title>

<script src="Scripts/Duo-Web-v2.js"></script>
<script src="Scripts/Duo-Web.js"></script>


</head>

<body>

<cfset IKEY = "DIGF6DVQMHS39JKMHTR3">
<cfset WRONG_IKEY = "DIXXXXXXXXXXXXXXXXXY">
<cfset SKEY = "HIDDEN">
<cfset AKEY = "8mIeduVeTBLqY0zcAKaCaZzhEXTZIPTQRstb0PYd">

<cfset USER = "testuser">

<!--- Dummy response signatures --->
<cfset INVALID_RESPONSE = "AUTH|INVALID|SIG">
<cfset EXPIRED_RESPONSE = "AUTH|dGVzdHVzZXJ8RElYWFhYWFhYWFhYWFhYWFhYWFh8MTMwMDE1Nzg3NA==|cb8f4d60ec7c261394cd5ee5a17e46ca7440d702">
<cfset FUTURE_RESPONSE = "AUTH|dGVzdHVzZXJ8RElYWFhYWFhYWFhYWFhYWFhYWFh8MTYxNTcyNzI0Mw==|d20ad0d1e62d84b00a3e74ec201a5917e77b6aef">
<cfset WRONG_PARAMS_RESPONSE = "AUTH|dGVzdHVzZXJ8RElYWFhYWFhYWFhYWFhYWFhYWFh8MTYxNTcyNzI0M3xpbnZhbGlkZXh0cmFkYXRh|6cdbec0fbfa0d3f335c76b0786a4a18eac6cdca7">
<cfset WRONG_PARAMS_APP = "APP|dGVzdHVzZXJ8RElYWFhYWFhYWFhYWFhYWFhYWFh8MTYxNTcyNzI0M3xpbnZhbGlkZXh0cmFkYXRh|7c2065ea122d028b03ef0295a4b4c5521823b9b5">

<h2>Test signRequest()</h2>
<cfset DuoWeb = CreateObject("component", "cfcs.DuoWeb")>

<br>
<cfdump var="#DuoWeb#" ><hr>

<cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, AKEY, USER)>
<cfdump var="#request_sig#" label="request_sig" /><hr>

<cfif NOT Len(request_sig)>
    <p>FAIL request_sig was NULL</p>
<cfelse>
    <p>PASS request_sig was not NULL</p>
</cfif>

<cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, AKEY, "")>
<cfif request_sig IS DuoWeb.ERR_USER>
    <p>PASS request_sig is ERR_USER</p>
<cfelse>
    <p>FAIL request_sig is not ERR_USER it is: <cfoutput>#request_sig#</cfoutput></p>
</cfif>

<cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, AKEY, "in|valid")>
<cfif request_sig IS DuoWeb.ERR_USER>
    <p>PASS request_sig is ERR_USER</p>
<cfelse>
    <p>FAIL request_sig is not ERR_USER it is: <cfoutput>#request_sig#</cfoutput></p>
</cfif>

<cfset request_sig = DuoWeb.signRequest("invalid", SKEY, AKEY, USER)>
<cfif request_sig IS DuoWEb.ERR_IKEY>
    <p>PASS request_sig is ERR_IKEY</p>
<cfelse>
    <p>FAIL request_sig is not ERR_IKEY it is:<cfoutput>#request_sig#</cfoutput></p>
</cfif>


<cfset request_sig = DuoWeb.signRequest(IKEY, "invalid", AKEY, USER)>
<cfif request_sig IS DuoWeb.ERR_SKEY>
    <p>PASS request_sig is ERR_SKEY</p>
<cfelsE>
    <p>FAIL request_sig is not ERR_SKEY it is: <cfoutput>#request_sig#</cfoutput></p>
</cfif>


<cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, "invalid", USER)>
<cfif request_sig IS DuoWeb.ERR_AKEY>
    <p>PASS request_sig is ERR_AKEY</p>
<cfelse>
    <p>FAIL request_sig is not ERR_AKEY</p>
</cfif>

<h2>Test verifyResponse()</h2>



<cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, AKEY, USER)>

<cfset valid_app_sig = ListGetAt(request_sig, 2, ":")>


<cfset future_user = DuoWeb.verifyResponse(IKEY, SKEY, AKEY, FUTURE_RESPONSE & ":" & valid_app_sig)>
<cfif future_user IS USER>
    <p>PASS future_user</p>
<cfelse>
    <p>FAIL future_user is: <cfoutput>#future_user#</cfoutput></p>
</cfif>

<cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, "1234567890123456789012345678901234567890", USER)>
<cfset invalid_app_sig = ListGetAt(request_sig, 2, ":")>

<cfset invalid_user = DuoWeb.verifyResponse(IKEY, SKEY, AKEY, INVALID_RESPONSE & ":" & valid_app_sig)>
<cfif NOT Len(invalid_user)>
    <p>PASS invalid_user</p>
<cfelse>
    <p>FAIL invalid_user</p>
</cfif>


<cfset expired_user = DuoWeb.verifyResponse(IKEY, SKEY, AKEY, EXPIRED_RESPONSE & ":" & valid_app_sig)>
<cfif NOT Len(expired_user)>
    <p>PASS expired_user</p>
<cfelse>
    <p>FAIL expired_user</p>
</cfif>


<cfset future_user = DuoWeb.verifyResponse(IKEY, SKEY, AKEY, FUTURE_RESPONSE & ":" & invalid_app_sig)>
<cfif NOT Len(future_user)>
    <p>PASS future_user invalid_app_sig</p>
<cfelse>
    <p>FAIL future_user invalid_app_sig</p>
</cfif>

<cfset future_user = DuoWeb.verifyResponse(IKEY, SKEY, AKEY, WRONG_PARAMS_RESPONSE & ":" & valid_app_sig)>
<cfif NOT Len(future_user)>
    <p>PASS future_user invalid_response_format</p>
<cfelse>
    <p>FAIL future_user invalid_response_format</p>
</cfif>

<cfset future_user = DuoWeb.verifyResponse(IKEY, SKEY, AKEY, FUTURE_RESPONSE & ":" & WRONG_PARAMS_APP)>
<cfif NOT Len(future_user)>
    <p>PASS future_user invalid_app_format</p>
<cfelse>
    <p>FAIL future_user invalid_app_format</p>
</cfif>

<cfset future_user = DuoWeb.verifyResponse(WRONG_IKEY, SKEY, AKEY, FUTURE_RESPONSE & ":" & valid_app_sig)>
<cfif NOT Len(future_user)>
    <p>PASS future_user wrong_ikey</p>
<cfelse>
    <p>FAIL future_user wrong_ikey</p>
</cfif>


<h2>Test hmacSign</h2>
<!--- test from rfc 2202 --->
<cfset result = DuoWeb.hmacSign("Jefe", "what do ya want for nothing?")>
<cfif result IS NOT "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79">
    <p>FAIL hmac result was <cfoutput>#result#</cfoutput></p>
<cfelse>
    <p>PASS hmac working properly</p>
</cfif>

</body>
</html>

There is not much documentation for this implementation, but it seems pretty straight forward. At this point I am just trying to get this test page working and then integrate it full.

Can anyone help me with detecting what am I doing wrong? HEre is screenshot of my error:

enter image description here


After replacing the cftry/cfcatch in my CFC with:

 <cfset duo_sig = signVals(arguments.sKey, arguments.username, arguments.iKey, variables.DUO_PREFIX, variables.DUO_EXPIRE)>
 <cfset app_sig = signVals(arguments.aKey, arguments.username, arguments.iKey, variables.APP_PREFIX, variables.APP_EXPIRE)>

this is the error it throws: enter image description here


Solution

  • Way too many cfifs, you don't need them (I understand you took this from their test page, but it doesn't help in this case).

    Just do a <cfdump var="#DuoWeb#" > right after you create the object and hopefully that will reveal the signature for signRequest method.

    If that works, assign request_sig one at a time and do <cfdump var="#request_sig#" >

    <h2>Test signRequest()</h2>
    
    <cfset DuoWeb = CreateObject("component", "cfcs.DuoWeb")>
    <cfdump var="#DuoWeb#" label="DuoWeb Object" />
    <hr />
    
    <cfset request_sig = DuoWeb.signRequest(IKEY, SKEY, AKEY, USER)>
    <cfdump var="#request_sig#" label="request_sig" />
    

    If request_sig does not look like a string which contains : in it, then ListGetAt(request_sig, 2, ":") will throw an error.


    I hope this helps.

    Update

    There is a cftry/cfcatch section in DuoWeb.cfc and because of it you can't see the real problem.

    Open DuoWeb.cfc and temporarily remove the cftry/cfcatch then run the test page again. It should throw an error with more info where the problem is.

    In DuoWeb.cfc replace this:

        <cftry>
            <cfset duo_sig = signVals(arguments.sKey, arguments.username, arguments.iKey, variables.DUO_PREFIX, variables.DUO_EXPIRE)>
            <cfset app_sig = signVals(arguments.aKey, arguments.username, arguments.iKey, variables.APP_PREFIX, variables.APP_EXPIRE)>
            <cfcatch>
                <cfreturn this.ERR_UNKNOWN>
            </cfcatch>
        </cftry>
    

    with this:

            <cfset duo_sig = signVals(arguments.sKey, arguments.username, arguments.iKey, variables.DUO_PREFIX, variables.DUO_EXPIRE)>
            <cfset app_sig = signVals(arguments.aKey, arguments.username, arguments.iKey, variables.APP_PREFIX, variables.APP_EXPIRE)>
    

    Update 2

    I've tried with Lucee and it does not work as is. It looks like Lucee does not like the word "cookie" from this section of DuoWeb.cfc , signVals method.

    In DuoWeb.cfc change this:

        <cfset var cookie = arguments.prefix & "|" & ToBase64(value)>
        <cfset var sig = hmacSign(arguments.key, cookie)>
        <cfreturn cookie & "|" & sig>
    

    to this (my suggestion is cookie__) :

        <cfset var cookie__ = arguments.prefix & "|" & ToBase64(value)>
        <cfset var sig = hmacSign(arguments.key, cookie__)>
        <cfreturn cookie__ & "|" & sig>
    

    It should do the trick.