STOP MULTIPLE SESSIONS IN COLDFUSION

curtis247
curtis247 used Ask the Experts™
on
Hi

I need to stop a user from being able to logon to my website multiple times by opening multiple browsers.  I'm running coldfusion and using sessionmangement.  I'm currently loading the session.cfid, timestamp and ip address into a table.  I thought I could distinguish between browser instances from one another with the cfid or cftoken, but they are identical coming from the same client computer.  Is there a way to distinguish between the two browser instances and kill the original? A snippet of my code is attached.

<cfapplication name="myapp" sessionmanagement="Yes" setclientcookies="No" sessiontimeout="#CreateTimeSpan(0,  0, 15,  0)#" >
 <cfcookie name="CFID" value="#session.cfid#">
 <cfcookie name="CFTOKEN" value="#session.cftoken#">
 
 
<!--- OBTAIN UNIQUE VARIABLE --->
<cflock name="#CreateUUID()#" timeout="15" type="EXCLUSIVE">
 <cfset user_cfid = Evaluate(CFID)>
 <cfset user_time = Now()>
</cflock>
 
<!--- CHECK IF USER IS ALREADY COUNTED --->
<cflock scope="APPLICATION" type="EXCLUSIVE" timeout="15">
 <cfif NOT StructKeyExists(Application.UsersInfo, user_cfid)>
  <cfset temp = StructInsert(Application.UsersInfo, user_cfid, user_time)>
 <cfquery name="logincount" datasource="mydb" dbtype="ODBC">
   insert into login_sessions (USER_CFID, SERVER, SESSION_COUNT, USER_REMOTE_ADDR, MODIFIED_DATE, USER_TIME) 
            values ('#user_cfid#', 'SERVER1', '1', '#cgi.remote_addr#', sysdate, '#TimeFormat(now(), "HH:mm:ss")#')
 </cfquery>
        
<cfset user_count = #user_count# + 1>
<cfquery name="ucount" datasource="mydb" dbtype="ODBC">
update sessionscount set session_count = '#user_count#', modified_date = sysdate where server = 'SERVER1'
</cfquery>
	</cfif>
</cflock>

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
off the top of my head  

on login you could do something like


<cfif structkeyexists(session,"userip")>
<cfset session.userIP = cgi.remote_addr>
</cfif>

<cfif cgi.remote_addr eq session.userip>
<cflocation url="alreadyloggedin.cfm">
</cfif>


You can distinguish between the same user login on different computers or if the user launches a separate browser window (by double clicking the icon).   But when the user does a FILE-> New Window or New Tab, the browser opens a second window/tab using the same session.   There is nothing you can do about this.  

I tried for a long time and then ultimately decided, why am I bothering?   This is the browsers functionality, it cannot be changed, my bank allows it, my brokerage accounts, every secure web site I access allows it because they have no choice.   (Yes, I tested them all).

So, since you can't avoid it.  The question is, why do you want to?   That's the only thing that can change :)
Acronis in Gartner 2019 MQ for datacenter backup

It is an honor to be featured in Gartner 2019 Magic Quadrant for Datacenter Backup and Recovery Solutions. Gartner’s MQ sets a high standard and earning a place on their grid is a great affirmation that Acronis is delivering on our mission to protect all data, apps, and systems.

Author

Commented:
gdemaria

I wish I did not have to implement this procedure, but I have no choice.   The demand is coming from higher up.

 What I am saying is that it is not possible.  You cannot control the behavior of the browser.

 It is the browser that chooses to reproduce the same session, you cannot detect it and if you kill one session, you will kill them both.  

 That's why I suggested examining the requirement, because this requirement cannot be met.


I can't give you specifics, but ... suggestions to try ...

Depending on how the window / tab is opened (and probably depending on the browser) .

1) if the window / tab is opened by starting a new browser altogether, then it should get a new session ID ... especially if you use JSESSIONID rather than Application/SessionID...
2) if the window / tab is opened by "File | Open Window/Tab" then you should be able to check it, using cgi.http_referer (as this will be "" rather than "http://server/...."
3) if the window / tab is opened by "right click, and open link in new window/tab" there is no way to test for it, (except possibly since there is no "back" capability for that window)

On one system I worked on, workflow control was required ... we used a table to identify session, what the current page was, and what URL's etc were allowed ... not a nice system ... but, if the URL requested by the client did not match an acceptable one based on the previous page requested, the browser recieved a "error, please start again" result, and bounced the user back to the beginning. Definitely not nice, but it worked.  

(FYI, My back used to use a similar method, and I found it EXTREMELY annoying :-)

@


>  1) if the window / tab is opened by starting a new browser altogether, then it should get a new session ID ... especially if you use JSESSIONID rather than Application/SessionID...

Correct - if you double click on the Browser Icon and launch a new application, this will give you a new session.   This would act the same as launching from a different computer.  In this case you can easily know that you have the same user with a different session and handle it with no problem.

> 2) if the window / tab is opened by "File | Open Window/Tab" then you should be able to check it, using cgi.http_referer (as this will be "" rather than "http://server/...."

Not true.  The referrer is the same as the original window.   When opening a new window or tab in this method, the browser makes an exact copy of the session.   You will not be able to detect a difference between these two identical windows/tabs.   This is the scenario that prevents the asker from meeting the requirement.

Even managing a session using hidden form fields, the URL or cookies (session or client) will not work because everything is duplicated.
> Not true.  The referrer is the same as the original window.   When opening a new window or tab in this method, the browser makes an exact copy of the session.   You will not be able to detect a difference between these two identical windows/tabs.   This is the scenario that prevents the asker from meeting the requirement.

> Even managing a session using hidden form fields, the URL or cookies (session or client) will not work because everything is duplicated

That is not entirely true ... (testing here limited exclusively to IE8/Vista/CFMX801)

Using the File menu:
* "New Tab (CTRL+T)" opens a tab using "about:tabs"
* "Duplicate Tab (CTRL+K)" opens a new tab containing the same URL, but cgi.http_referer is null.
* "New Window (CTRL+N)" opens a new window containing the same URL + cgi.http_referer.
Using the Right Click / Context Menu:
* "Open link in new tab" opens a new tab with no history (detectable by Javascript)
* "Open link in new window" opens a new window with no history (detectable by Javascript)

I honestly don't have time to test the JSESSION options

As far as "hidden form fields/URLs" etc, createUUID was unique on EVERY test even on "duplicate tab".  By using a tracking database (CFToken/CFID) combined with UUID, and permissable links, it WOULD be possible.  Not easy, but certainly possible.

@

> That is not entirely true ...

Where as some browsers under some circumstances may provided different results, the ability to use this as a reliable means-to-the-end requires consistent and predictable results.   Testing the referrer yields unpredictable results so cannot be used to reliably shutoff access.

> createUUID was unique on EVERY test even on "duplicate tab"

Yes, every time createUUID is run it does return a unique value.  But the question is, what do you then do with it.   How do you tell the page to create a new ID for a new session when you can't identify the session as 'new.'  

> By using a tracking database (CFToken/CFID) combined with UUID

The CFToken/CFIDE and JSESSIONs are duplicated when using FILE>New Tab (the only scenario under debate).   Adding UUID cannot help you because you cannot identify when to use it.  

> Where as some browsers under some circumstances may provided different results, the ability to use this as a reliable means-to-the-end requires consistent and predictable results.   Testing the referrer yields unpredictable results so cannot be used to reliably shutoff access.

if EVERY url contains a UUID, it is possible to use the referrer as an indicator of the previous page.

If the UUID in the referer already exists in the tracking table, but the current UUID is not in the tracking table, then the page has been regenerated (either through Refresh or New Page etc)

> Every time createUUID is run it does return a unique value.  But the question is, what do you then do with it.   How do you tell the page to create a new ID for a new session when you can't identify the session as 'new.'  

I did not mean, use the ID for the sessionID, but for uniqueness of Page generation ... In reference of using a Page/Status tracking table, knowing the UUID of the refering page and the UUID of the link, it would be VERY easy

> The CFToken/CFIDE and JSESSIONs are duplicated when using FILE>New Tab (the only scenario under debate).   Adding UUID cannot help you because you cannot identify when to use it

I wasn't attempting to give an exact answer, but give an indication of a workable method ...

A simple example would be:
<cfparam session.nextuuid=''>
<cfif len(cgi.querystring)>
    <cfset session.thisuuid=cgi.querystring>
    <cfif session.thisuuid neq session.nextuuid>
        <cfset session.thisuuid="">
        <cfset session.nextuuid="">
        <cflocation url="/">
    <cfelse>
        <cfset session.nextuuid=createuuid()>
    <cfif>
<cfelse>
    <cfset session.thisuuid=createuuid()>
    <cflocation url="/">
</cfif>
<a href="page1.cfm?#nextuuid#">
<a href="page2.cfm?#nextuuid#">

How Curtis can use this is upto him.  It really depends on whether or not he needs to block multiple windows, or multiple routes through the application. It also depends on whether they have control over the client software (and it's not unusual in large corporations to require specific browers)
@albrandwood, that solution does detect the new window being opened.  Perhaps it could work into a solution for Curtis although it does have some unpleasant side effects.

@curtis, The principle is that the page must be drawn with a query string that matches the saved session uuid, and that session uuid changes with every page request.   So, when the new window opens, the query string remains the same, but the session uuid has changed, so the result is a 'bad session.'

@
In order to implement, this uuid must be placed on every link and form submit.
The negative side effects are that you cannot use the Back button and you cannot do a page refresh.  Both will result in a bad session.  This would be a show-stopper for me as aborting the session because the user hits back or refresh would be a huge problem.

Here is a working version of the code for further play..

<cfparam name="session.nextuuid" default="">
 
<cfif len(cgi.QUERY_STRING)>
    <cfif cgi.QUERY_STRING is not session.nextuuid>
        <cfset session.nextuuid="">
		<h1>Error Bad Session</h1>
    <cfelse>
	    <h3>UIDs Do Match!</h3>
		This is the matched&nbsp; UUID: <cfoutput>#session.nextuuid#</cfoutput><br>
        <cfset session.nextuuid = createuuid()>
		Creating a new NextUUID: <cfoutput>#session.nextuuid#</cfoutput><br>
    </cfif>
<cfelse>
    <cfset session.nextuuid = createuuid()>
	<h1>Error 2: Bad Session</h1>
</cfif>
 
<cfoutput>
<br>
<a href="#cgi.script_name#?#session.nextuuid#">Go to this page</a> with nextID = #session.nextuuid#
<br>
</cfoutput>

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial