XSRF errors in repo creation using custom HttpAuthenticationHandler

Matt Lesko March 20, 2014

Hi all,

I'm having a devil of time trying to figure out where this bug is coming from. In house, we (I) have written a custom HttpAuthenticationHandler implemenation designed to authenticate, or rather, pre-authenticate a user based on HTTP headers.

Specifically, we are using mod_auth_kerb to do kerberos authentication, and if successful, set an HTTP Header 'REMOTE-USER'.

When using Firefox with about:config, network.negotiate-auth.using-native-gsslib=true the process works without issue to the best of my knowledge. This includes most of the operations within stash although I can't claim complete coverage.

When using Chrome, where negotiate-auth is *not* an option, the auth_krb_auth setup does a 401 prompt (the annoying pop-up kind), accepts the username/password credential, and then permits access to the product. Most of it works as intended.

However, creating a personal repo from Profile -> Create Repository causes an "XSRF Security Token Missing" error the majority, but frustratingly, not *all* the time. It's very inconvenient.

I've looked up and down with both Firebug and Chrome Developer tools - the atl_token is being set correctly both on the page, and is identical to the POST.

Any thoughts on what might be a cause, or how to better debug? If it's possible to disable to XSRF check here that'd be nice to know, although that seems a little bit extreme.

2 answers

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

0 votes
Michael Heemskerk
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 23, 2014

Hi Matt,

Not sure if you've found this already, but have a look at https://bitbucket.org/mheemskerk/stash-auth-plugin-examplefor an example authentication plugin.

Please be careful with accepting a REMOTE_USER header. If it's possible for remote users to set this header manually, you may introduce a security hole. This would be the case if your Apache HTTP server doesn't strip the REMOTE_USER header set by clients or if the Tomcat server can be accessed directly (bypassing the proxy server).

An alternative is to set up mod_auth_kerb and use the AJP Connector to provide the authenticated user to Tomcat. You can then use request.getRemoteUser to retrieve the authenticated username. This is exactly what the example auth plugin that I linked above does.

An example configuration is described here: http://stackoverflow.com/questions/8448558/kerberos-sso-with-apache-and-tomcat-under-jdk5.

Hope this helps,

Michael

Matt Lesko March 23, 2014

That's actually the original plugin example I used, although I see now you've commited major changes last week. My plugin is still operating on the simplier, 'preauthenticate()' style. Is that deprecated, or in some way not preferred?

Matt Lesko March 23, 2014

Also, as a reference - the httpd.conf file has the same mod_auth_kerb configuration as the SO answer, but does it through regular HTTP headers before passing it to Tomcat. It removes PRIVATE_REMOTE_USER then sets if, if the kerberos auth was successful.

Matt Lesko March 23, 2014

I'm cautiously optimistic that the getSession() solution is working, as I haven't seen any XSRF errors since including it. The caveat is that they weren't 100% repeatable - some of the Chrome requests still work.

I guess my only remaining question is if the preauthenticate() method should be used in conjunction with the session creation, or not.

And if anyone has a good hypothesis for why this *did* work with Firefox, despite being, in the end, incorrectly written code.

Thanks.

Michael Heemskerk
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 24, 2014

The previous version of the example was created when Stash did not have a pluggable authentication API (pre 2.3). In 2.3 we've introduced the HttpAuthenticationHandler plugin point to make it easier to write custom SSO integrations, so that's the prefered option. HttpAuthenticationHandler requires you to return a StashUser, but you don't have to call preauthenticate. Simply looking up a user is sufficient.

Matt Lesko March 24, 2014

It looks like the same XSRF errors are back. They're just inconsistent which makes it hard to track down. When is the X-ASESSIONID created? I see that changing frequently as I browse stash through proxy.

Matt Lesko March 24, 2014

What I find particularly odd are messages like these:

2014-03-24 18:18:04,452 DEBUG [http-bio-7990-exec-10] 1098x1331x0 ylmg2e 1XX.XX.XX.XX,127.0.0.1 "GET /users/myself/repos HTTP/1.0" c.a.s.i.a.PluginHttpAuthenticationSuccessHandler onAuthenticationSuccess - none of the plugins handled authentication success

My onAuthenticationSuccess handler returns false, which is what I believe is the correct thing to do. It is called before this line.

Michael Heemskerk
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 24, 2014

Hi Matt,

You shouldn't need to provide an HttpAuthenticationSuccessHandler unless you need to do special handling when someone authenticates successfully. If you've got your sources online somewhere (e.g. Bitbucket), I'd be happy to have a look at them?

The X-ASESSIONID header is set on every request and the value is derived from the JSESSIONID. If you see that value frequently fluctuating, it means that for some reason your session is getting invalidated. One thing that comes to mind is that Stash invalidates your session and creates a new session on successful authentication to prevent session fixation attacks. Perhaps you HttpAuthenticationHandler.validateAuthentication implementation is throwing an AuthenticationException, triggering a fresh authentication?

Matt Lesko March 24, 2014

Some internal headaches mean I don't have a public bitbucket copy of this code. Are you able to see my contact info as an Atlassian employee? If so, contact me through email and I'll see what I can do.

Matt Lesko March 24, 2014

I don't see any exceptions being thrown. From debugging output, I see the Firefox instances, validateAuthentication() is called, for the Chrome instances, it is never called. Each request seems to do another authenticate().

Michael Heemskerk
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 25, 2014

If you have a private Bitbucket repo for your plugin, you can grant read access to me (my username is mheemskerk).

Matt Lesko March 25, 2014

Access has been granted. There's an example httpd.conf with the mod_auth_krb/proxypass configuration as well.

I'll also point out that we are using the same httpd.conf setup with Confluence and a custom authenticator that operates in the same manner (looking for an HTTP header). There have been no problems with the Confluence authenticator, and all my dev tool watching shows that the JSESSIONID is consistent, on both Chrome and on Firefox.

Matt Lesko April 20, 2014

Hi Michael. Were you able to access my repo? Any thoughts?

0 votes
jhinch _Atlassian_
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 20, 2014

The XSRF validation is tied to the HttpSession on the server. The session is associated with requrests via a SESSIONID cookie. It sounds like the session either isn't being created or is not being correctly associated. Are you stripping cookie headers from the requests at your proxy server?

Matt Lesko March 20, 2014

I'm not stripping any headers at the proxy, indeed, the only thing is adding a new header, REMOTE-USER.

I presume you meant the header X-ASESSION? I do indeed see that on Chrome, it changes on every request, but on Firefox it remains the same for subsequent actions.

Matt Lesko March 20, 2014

My custom authenticator isn't doing anything with the HttpSession, at least not explicitly:

@Override
public StashUser authenticate(HttpAuthenticationContext authenticationContext) {

    StashUser user;

    HttpServletRequest httpRequest = authenticationContext.getRequest();

    if( ( httpRequest.getHeader("REMOTE-USER") != null )
        && ( httpRequest.getHeader("authorization") != null ) ) {

        user = userService.getUserByName(httpRequest.getHeader("REMOTE-USER"));

        if(user != null) {

            try {
                user = userService.preauthenticate(user.getSlug());
            } catch ( AuthenticationException e ) {
                e.printStackTrace();
                throw e;
            }

            return user;

        } else {
            return null;
        }
    } else {
        return null;
    }
}

jhinch _Atlassian_
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 21, 2014

No, I was refering to the JSESSIONID cookie. Without this, many parts of Stash will not work. Looking at your code, it looks like a session is never created. I would suggest calling request.getSession() on a successful authentication and store the user ID to a known attribute. It the user ID changes, invoke request.getSession(false).invalidate() and then request.getSession() to create a new one.

Matt Lesko March 23, 2014

Thanks for the advice, I'm looking into implementing that.

What's strainge though is that Firefox, with this GSSAPI funniness, *does* get a consistent JSESSIONID and X-ASESSIONID created and used in all requests. Chrome, using the Basic Auth, does not. Wouldn't those *SESSIONID be created earlier, before the authenticate() call?

TAGS
AUG Leaders

Atlassian Community Events