Weird bug: Session not found when visiting website from other URL (Actix-web)

Hello

I have a very weird Session bug in Actix-Web I can't explain. I have a route mysite.com/my-account/orders which triggers the following function:

pub async fn my_orders(session: Session, mysql: web::Data<MySQL>) -> Result<HttpResponse> {
    // Check if logged in   
    let user = match session.get::<User>("user").unwrap() {
        Some(u) => u,
        None => {
            return Ok(HttpResponse::Found()
                .append_header(("Location", "/login-or-register"))
                .finish());
        }
    };

    // (Removed irrelevant code)...

    let s = templates::user_account::MyOrders {
        user: Some(user),
        orders: order_with_orderlines,
    }
    .render()
    .unwrap();
    Ok(HttpResponse::Ok().content_type("text/html").body(s))
}

Basically the function first checks if the user is logged in. If the user is not logged in he gets redirected to the login-page. Otherwise the page is just displayed.

This works perfectly fine in my browser. I am currently logged in, so when I visit mysite.com/my-account/orders directly in my browser I see the required page. If I logout and try the same I get redirected to the login-page as intended.

However, I just noticed, if I have have a page outside my website (e.g mail HTML or a simpel HTML page) with the following anchor tag

<a href="https://mysite.com/my-account/orders">test</a>

and I click it, no matter if I am logged in or not, I always get redirected to the login-page. If I refresh the page (when logged in) I finally see the required page.

This does not seem to be a bug in my code, because: The authentication logic works perfectly fine when visiting or testing the URL's in my browser. But the session somehow is not found directly when the URL is visited by clicking on it from another page.

How to fix this?

Edit:
So to be clear: If I make a HTML file with this code <a href="https://mysite.com/my-account/orders">test</a>. And I click the link, the session is not found and I get redirected to the login-page until I refresh the page. BUT, if I right-click the URL and select "Open URL in new tab", it works perfectly like intended. So the session is only not found if I click on the URL. Copy/pasting the URL in my browser also makes it work perfectly as intended. Seems like Actix-web is somehow not fetching the session when the previous resource was from somewhere outside my own website?

Edit 2:
This only seems to be the case in Safari.

This has nothing to do with Actix. This is a privacy/security feature of the browser, and it will happen to any server in any programming language.

It is meant to protect against cross-site tracking and CSRF and click-jacking. A different domain may be controlled by an attacker, and that site could be trying to perform operations on your site using your users' session. This is a very old weakness in the web platform, and it's a trade-off between compatibility and security.

3 Likes

But why does it work in Chrome and Firefox and only not in Safari?
Also for example: To do a password-reset I do the following:

  1. Generate a password-token and set it in session
  2. Mail a URL like mysite.com/password-reset/{password-token}
  3. When user clicks url check if password-token from the url matches with the token in the session. If not, someone else is doing something malicious.

But now, when the user clicks on the URL in the mail, the password-token in the session is not found...

Because Safari comes preinstalled, ultimately. And because Apple doesn't care all that much about whether Ads work or not. This is old change, Chrome and Firefox were supposed to replicate it, too, but they couldn't make Ad-publishers too angry.

1 Like

Then how would one implement the password-reset functionality that I described above?

Yeah, this is a browser feature to stop clickjacking.

Make sure your session cookies are being set with SameSite=Lax to make this work (block cookie on subresources but allow on top-level navigations).

@khimru Safari has made an intentional choice to make SameSite=Strict be the default, this is not due to update cadence.

4 Likes

And does using SameSite=Lax give big security issues?

It has exactly the effects described on the MDN article.

The only relevant security issue can be prevented as follows:

Create a second cookie, actok=1; SameSite=Strict. Reject all POST/PUT/PATCH/DELETE requests that carry a session cookie but not that cookie.

4 Likes

I have implemented SameSite=Lax this way:

SessionMiddleware::builder(CookieSessionStore::default(), Key::from(&[0; 64]))
                    .cookie_secure(false)
                    .cookie_same_site(SameSite::Lax)
                    .session_lifecycle(
                        PersistentSession::default()
                            .session_ttl(actix_web::cookie::time::Duration::weeks(2)),
                    )
                    .build(),

But it still does not work on Safari!

1 Like

Anyone an idea on how to fix this? It's the last impediment for me to be able to launch.

I'm working on an actix_web commercial project and I ran into the same problem. Session cookies were being applied by my login code in every browser except safari. I got it working. In the working version, the only thing I'm doing in the SessionMiddleware builder that's different than you is my session_ttl is set to 5 days. If you're still having problems, please reply asap and I'll do my best to figure it out with you.

1 Like

This issue did also not occur in the released version of my project. Seems like Safari has some extra checks built in.

This is solved.