[Esapi-user] Attempt to Fix a CSRF

Chris Barlock barlock at us.ibm.com
Wed Apr 3 23:03:38 UTC 2013


Thanks, Jeff.  Why is it that the sessions look so much alike -- same 
session ID and context values?  That surprised me.

Chris

IBM Tivoli Systems
Research Triangle Park, NC
(919) 224-2240
Internet:  barlock at us.ibm.com



From:   Jeff Williams <jeff.williams at aspectsecurity.com>
To:     Chris Barlock/Raleigh/IBM at IBMUS, "esapi-user at lists.owasp.org" 
<esapi-user at lists.owasp.org>, 
Date:   04/03/2013 06:50 PM
Subject:        RE: [Esapi-user] Attempt to Fix a CSRF



JavaEE Sessions aren’t shared across WAR files.  If you’re using Servlet 
3.0 you might try something like this in web.xml…
 
<session-config>
    <cookie-config>
        <path>/</path>
    </cookie-config>
</session-config>
 
I’m pretty sure that this will share the session cookie, but I haven’t 
tried this to verify that the sessions are shared across contexts.  Let us 
know if you figure out a workaround.
 
You might check out some of the methods in HTTPUtilities that support CSRF 
tokens.  They might make some of your code a tiny bit simpler.
 
Thanks,
 
--Jeff
 
 
From: esapi-user-bounces at lists.owasp.org [
mailto:esapi-user-bounces at lists.owasp.org] On Behalf Of Chris Barlock
Sent: Wednesday, April 03, 2013 5:10 PM
To: esapi-user at lists.owasp.org
Subject: [Esapi-user] Attempt to Fix a CSRF
 
I am attempting to fix a Cross-Site Request Forgery using the technique 
that John Melton describes in the link on the OWASP web site: 

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29 

When we log in, add a token to the HTTP session object: 

<% 
String token = ESAPI.randomizer().getRandomString(8, EncoderConstants.
CHAR_ALPHANUMERICS); 
HttpSession reqSession = request.getSession(); 
reqSession.setAttribute(HTTPUtilities.CSRF_TOKEN_NAME, token); 
%> 

When a page that needs to be protected is requested, get the token from 
the session so that it is available to the form that will be POSTed back 
to the server: 

<%-- Get the Cross-Site Request Forgery token. --%> 
<% 
HttpSession requestSession = request.getSession(); 
String csrfToken = (String) 
requestSession.getAttribute(HTTPUtilities.CSRF_TOKEN_NAME); 
%> 

Include it in the form as a hidden input field. 

<input id="csrfToken" name="<%=HTTPUtilities.CSRF_TOKEN_NAME%>" 
    type="hidden" value="<%=csrfToken%>"/>

When the form is processed, check that the two tokens match: 

    /** 
     * Tests whether a Cross-Site Request Forgery is in progress by 
comparing a 
     * token in the HTTP session object with one placed in the form that 
is 
     * being POSTed. 
     * 
     * @param formParams 
     *        The input fields of the form. 
     * @param request 
     *        The HTTP request. 
     *         
     * @return <code>true</code> if the tokens do not match and an attack 
is in 
     *         progress; <code>false</code> otherwise. 
     */ 
        public static boolean isCSRFAttack(MultivaluedMap<String, String> 
formParams, HttpServletRequest request) { 
            boolean attack = false; 
 
        HttpSession session = request.getSession(); 
        String sessionCSRFToken = (String) 
session.getAttribute(HTTPUtilities.CSRF_TOKEN_NAME); 
 
        String formCSRFToken = formParams.getFirst(HTTPUtilities.
CSRF_TOKEN_NAME); 
 
        // sessionCSRFToken should never be null, but just in case, no 
NPE... 
        if (sessionCSRFToken != null && 
!sessionCSRFToken.equals(formCSRFToken)) { 
            attack = true; 
        } 

        // Remove the CSRF token in the form, so it is not processed as a 
valid input field. 
        if (formCSRFToken != null) { 
            formParams.remove(HTTPUtilities.CSRF_TOKEN_NAME); 
        } 

        return attack; 
        } 

The problem I have is that the page is requested from one WAR file and the 
form is POSTed to a different WAR and the CSRF token is missing from the 
HTTPSession object when the form is posted.  Here's the session object 
when the GET occurs: 

 { 
 _iSession=# com.ibm.ws.session.store.memory.MemorySession # 
 { 
 _sessionId=twDANh7-Qj8Zj0i_aUpFj9n 
hashCode : 321008117 
create time : Wed Apr 03 16:48:21 EDT 2013 
last access : Wed Apr 03 16:48:21 EDT 2013 
max inactive interval : 1800 
user name : user:customRealm/smadmin 
valid session : true 
new session : true 
overflowed : false 
app name : default_host/ccmDashboard 

Attribute Names=[ctoken] 
 _refCount=1 
 } 

 
_httpSessionContext=com.ibm.ws.session.http.HttpSessionContextImpl at cb67e2b 

 } 

and here is the one from the POST: 

 { 
 _iSession=# com.ibm.ws.session.store.memory.MemorySession # 
 { 
 _sessionId=twDANh7-Qj8Zj0i_aUpFj9n 
hashCode : 2007005455 
create time : Wed Apr 03 16:49:44 EDT 2013 
last access : Wed Apr 03 16:49:44 EDT 2013 
max inactive interval : 1800 
user name : user:customRealm/smadmin 
valid session : true 
new session : true 
overflowed : false 
app name : default_host/ccm 

Attribute Names=[] 
 _refCount=1 
 } 

 
_httpSessionContext=com.ibm.ws.session.http.HttpSessionContextImpl at cb67e2b 

 } 

They are almost the same:  matchine sessionId and httpSessionContext 
values -- but the timestamps, hashCodes and, most importantly, Attribute 
Names array are different.  If the session objects really are different, 
why do they share the same session ID and context values?  Any thoughts on 
whether I'm doing this wrong or on how I might fix this? 

Thank you! 

Chris

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.owasp.org/pipermail/esapi-user/attachments/20130403/f855fe1c/attachment.html>


More information about the Esapi-user mailing list