simplesamlphp / simplesamlphp

SimpleSAMLphp is an application written in native PHP that deals with authentication.
https://simplesamlphp.org
GNU Lesser General Public License v2.1
1.07k stars 677 forks source link

SimpleSAML_Error_NoState: NOSTATE on multiple concurrent connections from same domain/host #597

Closed gitmsb closed 7 years ago

gitmsb commented 7 years ago

simplesaml does not work with multiple concurrent connections from one domain/host. When you e.g. have multiple iframes on one page with the same src (saml authenticated) then simplesaml throws this error. Most of the time one iframe gets authenticated correctly but all the others show an error page. In my case I authenticate against Microsoft ADFS. Simplesaml obviously expects a connection to be finished before it can handle new connections from the same domain/host. That's a bug other systems like lotus notes or sap don't show. Could you please fix that as soon as possible?

SimpleSAMLphp Error Report: Exception: SimpleSAML_Error_NoState: NOSTATE Backtrace: 2 /var/simplesamlphp/lib/SimpleSAML/Auth/State.php:263 (SimpleSAML_Auth_State::loadState) 1 /var/simplesamlphp/modules/saml/www/sp/saml2-acs.php:78 (require) 0 /var/simplesamlphp/www/module.php:137 (N/A)

jaimeperez commented 7 years ago

Hi @gitmsb!

I'm afraid this is a bug in the application using SimpleSAMLphp, not in SimpleSAMLphp itself.

First of all, SimpleSAMLphp should never be embedded in an iframe, as that opens up for serious security issues.

Second, SimpleSAMLphp doesn't know anything about connections or concurrency. This is HTTP, and the application running on top of it must be unaware of that (HTTP is stateless). All that SimpleSAMLphp expects is that certain steps are followed in order. This is achieved by tracking the user by means of the session and the query parameters (among which the state identifier is crucial), effectively keeping a state for a given user. If, at some point, a request does not provide a state identifier or that state identifier does not correspond with a state saved in the session then there's nothing SimpleSAMLphp can do but to show an error.

This can happen if you start two authentication processes at the same time, concurrently, while sharing the same session. Let's say you have requests A and B, both using the same session ID. When A is done, it saves its state information to the session. The session, however, was already loaded for request B (and therefore the information added by A was not there, as both requests started simultaneously), which subsequently saves its state information to the same session after A, removing the information added previously by A. When another request is performed with the information provided by the response to request A, that request will fail because the information stored during A is gone.

Again, there is nothing we can do to avoid that, because HTTP is stateless, and there's no way an application in an HTTP server can coordinate the concurrent requests so that are processed sequentially (and that's a feature, not a bug). The only way to avoid such behaviour would be to change the session ID on every request, because then the same session would split into as many session as concurrent requests are there. However, that introduces a whole lot of problems and issues.

An alternative could be to save the state information to an independent storage, but that has two disadvantages:

  1. It's an overkill and imposes a big burden to all those who use SimpleSAMLphp with regular PHP sessions and no external storage, just to solve a corner case caused by a misbehaving application.
  2. Even if that would solve the issue for the state information, all other data stored in the session would still be affected by this issue.

So, to summarize, there's nothing we can do. This is a problem in the application. If you give us more details on how the application works we might be able to suggest a way to solve the issue in the application itself.

gitmsb commented 7 years ago

Hi Jaime,

many thanks for your reply.

Can I describe my configuration to you?

We have a javascript application with a lot of iframes with statistics. 4 of the iframes use the same url like dashboard.domain.xyz and differ only by the page. (eg. dashboard.domain.xyz/page1.php, dashboard.domain.xyz/page2.php and so on). Since these frames all load at the same time the nostate error appears. One of our users doesn't have access to the complete dashboard but only to certain frames. So he told the browser to automatically open them in different tabs on browser start but also there we have the same issue. All the tabs are loaded at the same time...

So it's not a specific iframe issue, it's an issue of concurrent requests. Even if iframes are considered insecure different tabs are not. I wonder why nobody else encountered this issue by now.

How could we solve that problem?

many thanks,

David

gitmsb commented 7 years ago

Hi Jaime,

here I have additional information for you:

Every page contains the following saml code:

    require_once ('/var/simplesamlphp/lib/_autoload.php');
    $as = new SimpleSAML_Auth_Simple('default-sp');

    if (!$as->isAuthenticated()) $as->requireAuth();

    $attributes = $as->getAttributes();

    $user=strtolower($attributes["

http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"][0]);

Thanks a lot,

David

jaimeperez commented 7 years ago

Hi David!

The use case of a user setting his/her browser to open more than one page requiring authentication on startup should be ok if the cookie is a session cookie (which should always be unless you have logout working for every service and under any circumstances). In that case, the session cookie will be gone when you close the browser, and then there'll be no shared session on startup, and therefore no issue. Set the session.cookie.lifetime configuration option to 0 to achieve this.

In any case, if you are using iframes and / or your cookie is not a session cookie and you want to support the use case you are mentioning, then your pages shouldn't actively trigger authentication. Instead, passive authentication could be used to check if there is an existing session, and if not, display a link or a button that the user can click on to start authentication.

gitmsb commented 7 years ago

Hi Jaime,

thanks for your support.

In my case the cookie lifetime is just set for the session. Even if I delete all cookies and open the dashboard page with the iframes the same error occurs. Passive authentication is not an option. Imagine a dashboard where the user expects 10 frames with statistics and other kind of information an the user had to click "login" ten times in ten different frames... But I'm asking myself how other tools like lotus notes solve that issue. I created ten iframes with the same lotus notes webmail url and there's no problem at all.

David

jaimeperez commented 7 years ago

Having a session cookie does not solve the issue for the iframe use case, but it does for the other one.

Imagine a dashboard where the user expects 10 frames with statistics and other kind of information an the user had to click "login" ten times in ten different frames...

True. That's why authentication should be done on the main page that displays the iframes, so that if the user is not authenticated, you never get to see the iframes. If you don't want to force authentication (e.g. some iframes require auth, some don't), check for authentication (check, not enforce) on the main page, and add the iframes that require auth only in case the user was authenticated.

But I'm asking myself how other tools like lotus notes solve that issue. I created ten iframes with the same lotus notes webmail url and there's no problem at all.

I don't know. Does it support SAML? What URL is it, specifically? If it's the canonical URL of your webmail and you are already authenticated, why shouldn't it work? SimpleSAMLphp would also work perfectly fine in the same situation. What you can't do is to start several authentication flows at the same time, with the same session.

gitmsb commented 7 years ago

I have never set my cookie to anything else so it has always been a session cookie. Nevertheless we had the "multi-tab" issue.

Yes, the lotus notes webmail works with saml and adfs as my simplesaml does. It's a normal url like "https://webmail.domain.xyz/mail/D/David.nsf?OpenDatabase". And I'm not authenticated before opening the iframe page.

But if I get it right there's nothing I can do about my issue?

David

jaimeperez commented 7 years ago

I have never set my cookie to anything else so it has always been a session cookie. Nevertheless we had the "multi-tab" issue.

Is session.cookie.lifetime set to 0? If so, your SimpleSAMLphp session cookie is indeed an HTTP session cookie, meaning it is removed when you close your browser. In that case, the "multi-tab" issue is not an issue, because each loading tab would create a new session.

Yes, the lotus notes webmail works with saml and adfs as my simplesaml does. It's a normal url like "https://webmail.domain.xyz/mail/D/David.nsf?OpenDatabase". And I'm not authenticated before opening the iframe page.

If you are not authenticated before opening the iframe page, how do you get authenticated to the webmail? Furthermore, when you are pointing your iframes to your different PHP scripts, how do you authenticate on each of them if you are not authenticated previously?

But if I get it right there's nothing I can do about my issue?

On the contrary. There's nothing we can do about your issue. You could easily fix it by forcing your application to authenticate the user before showing the iframes, or avoid displaying iframes that require authentication if the user is not yet authenticated, as I described in my previous message.

gitmsb commented 7 years ago

Yes, session.cookie.lifetime is set to 0.

webmail runs in each iframe, there authentication takes place. With iframe page I mean the page which holds the iframes.

Authentication before showing the iframes is not an option. The dashboard page knows nothing about the content / pages in the iframes, it just handles creating, moving, displaying and removing iframes and so on. It does not even come from the same server / domain. The contents in the iframes come from different locations. The dashboard acts like an OS and is not aware of any content.

I have to think of a workaround.

One idea is to have a different subdomain for each page that needs saml authentication (would that help), the other is to have something like a queue class that handels request in a way that only one request at a time can be done and freezes the script of subsequent pages until the previous request is finished. Both not very nice solutions.

gitmsb commented 7 years ago

Hi Vikram,

could you please talk to Thomas Höfler regarding this issue?

Many thanks

David

From: Jaime Pérez Crespo notifications@github.com To: simplesamlphp/simplesamlphp simplesamlphp@noreply.github.com Cc: gitmsb github@msb.at, Mention mention@noreply.github.com Date: 18.04.2017 12:07 Subject: Re: [simplesamlphp/simplesamlphp] SimpleSAML_Error_NoState: NOSTATE on multiple concurrent connections from same domain/host (#597)

Hi @gitmsb! I'm afraid this is a bug in the application using SimpleSAMLphp, not in SimpleSAMLphp itself. First of all, SimpleSAMLphp should never be embedded in an iframe, as that opens up for serious security issues. Second, SimpleSAMLphp doesn't know anything about connections or concurrency. This is HTTP, and the application running on top of it must be unaware of that (HTTP is stateless). All that SimpleSAMLphp expects is that certain steps are followed in order. This is achieved by tracking the user by means of the session and the query parameters (among which the state identifier is crucial), effectively keeping a state for a given user. If, at some point, a request does not provide a state identifier or that state identifier does not correspond with a state saved in the session then there's nothing SimpleSAMLphp can do but to show an error. This can happen if you start two authentication processes at the same time, concurrently, while sharing the same session. Let's say you have requests A and B, both using the same session ID. When A is done, it saves its state information to the session. The session, however, was already loaded for request B (and therefore the information added by A was not there, as both requests started simultaneously), which subsequently saves its state information to the same session after A, removing the information added previously by A. When another request is performed with the information provided by the response to request A, that request will fail because the information stored during A is gone. Again, there is nothing we can do to avoid that, because HTTP is stateless, and there's no way an application in an HTTP server can coordinate the concurrent requests so that are processed sequentially (and that's a feature, not a bug). The only way to avoid such behaviour would be to change the session ID on every request, because then the same session would split into as many session as concurrent requests are there. However, that introduces a whole lot of problems and issues. An alternative could be to save the state information to an independent storage, but that has two disadvantages:

  1. It's an overkill and imposes a big burden to all those who use SimpleSAMLphp with regular PHP sessions and no external storage, just to solve a corner case caused by a misbehaving application.
  2. Even if that would solve the issue for the state information, all other data stored in the session would still be affected by this issue. So, to summarize, there's nothing we can do. This is a problem in the application. If you give us more details on how the application works we might be able to suggest a way to solve the issue in the application itself. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.