Django session cookie forgotten on every browser reopen - mobile Safari (iphone,ipad) - ios

I wonder if anybody encountered with this problem. I am storing some data about visitor in django session. It works as expected but only mobile safari (iphone and ipad) have strange behaviour.
When I visit my site from iphone or ipad(Safari ver. 14.3) session cookie is normally set. But when I close the browser then reopen, new session cookie is generated.
This behaviour can be seen only on mobile safari version. I was not able to reproduce it on macOS desktop safari.
To solve this problem I had to change setup for session cookie in django settings.py:
SESSION_COOKIE_SAMESITE = ‘None’
According to django doc. cookie is normally set as ‘lax’ and this introduce security risk in my app.
SESSION_COOKIE_SAMESITE
Default: 'Lax' The value of the SameSite flag on the session cookie.
This flag prevents the cookie from being sent in cross-site requests
thus preventing CSRF attacks and making some methods of stealing
session cookie impossible. Possible values for the setting are:
'Strict': prevents the cookie from being sent by the browser to the target site in all cross-site browsing context, even when following a
regular link.

For example, for a GitHub-like website this would mean that if a
logged-in user follows a link to a private GitHub project posted on a
corporate discussion forum or email, GitHub will not receive the
session cookie and the user won’t be able to access the project. A
bank website, however, most likely doesn’t want to allow any
transactional pages to be linked from external sites so the 'Strict'
flag would be appropriate.
'Lax' (default): provides a balance between security and usability for websites that want to maintain user’s logged-in session after the
user arrives from an external link.

In the GitHub scenario, the session cookie would be allowed when
following a regular link from an external website and be blocked in
CSRF-prone request methods (e.g. POST).
'None' (string): the session cookie will be sent with all same-site and cross-site requests.
False: disables the flag.
I guess that I had to cause security hole in my django app intentionally. I don’t like it but I am not sure how serious risk it is. I would love to know why only mobile safari behave like that.

Related

Can I get cookies from Safari in a SFSafariViewController?

tl;dr; see the question below
In my app, I have a login that uses SFSafariViewController and ASWebAuthenticationSession that follows the OAuth 2.0 flow (Using the AppAuth library).
The login works and the cookies are shared with Safari as expected. Thanks to the cookie sharing, users are automatically logged-in if they use the Safari app.
However, back in the app, if I launch a SFSafariViewController again, the cookies are missing. This surprises me, because I thought the cookie Store is the same for SFSafariViewController and Safari, and it clearly worked in the direction from SFSafariVC to the Safari app during login.
Is it intended not to work the other way round - from Safari to SFSafariViewController, or is it a bug?
I have not found clear statements in the documentation.
Of course I have not set ephemeral session to true, but according to the documentation it would do the opposite of what I want to achieve:
When not using an ephemeral session, all cookies except session cookies are available to the browser.
I've also found somehow related radars like http://www.openradar.me/33323462 and http://www.openradar.me/radar?id=5036182937272320 or this stackoverflow post: Why is SFSafariWebViewController not sharing cookies with Safari properly? but they do not answer my question.
According to this comment it could work if the cookies have an expiry date (set to a future date). I verified the cookies - they all have a future expiry date.
My question: Am I doing something wrong, or is this expected behaviour, that SFSafariViewController does not get cookies from an earlier SFSafariViewController instance in the same app or from Safari?
REQUIREMENTS
So it seems you want a solution to invoke secured web content from a mobile app, and to avoid an extra login. It is a common requirement and I will be adding some stuff to my blog on this topic over the next month or so.
STATE OF THE INDUSTRY
The problem with the above is that third party cookies, such as those issued by Identity Providers, are often dropped by default these days due to browser security initiatives such as Intelligent Tracking Prevention changes - which is ON by default in Safari:
COOKIE PROPERTIES
Worth checking that your cookies are issued with SameSite=None, which will give you the best options for a third party cookie based solution.
MOBILE FIRST DESIGNS
In an OAuth world, in order to meet the requirements, it is likely to be necessary to send a token from the mobile UI to the web UI, which of course has prerequisites that need to be designed for:
Web UI must use tokens
Web UI must use different strategies for token handling depending on the host
OPTION 1
One option is to use a mobile web view to show the web content - see my code below:
Web UI Code to ask the host for tokens
Mobile UI Code to service these requests
OPTION 2
Another option is to send something representing the token in a query string parameter from the mobile app to the Web UI, in which case you need to ensure that:
No usable tokens are recorded in web server logs
The token has a one time use only
A typical implementation would look like this:
Mobile UI calls an /api/token/encrypt endpoint
API stores a token hash in a database and returns an encrypted value with a short time to live
Token is sent from the Mobile App to the Web UI
Web UI calls an /api/token/decrypt endpoint to get the real token
The API's decrypt implementation deletes the database entry

Why can't I read the contents of a SameSite Lax cookie when opening a link from the Gmail iOS App?

My company's website (mercury.co) sends password reset links via email to users. We ran into some really weird behavior that we can only reproduce in the Gmail iOS app relating to the SameSite Lax attribute:
The user follows a link in their email to https://mercury.co/reset-password
The browser loads Javascript from that URL to set up the site
The client does a GET request, which returns a CSRF token in a cookie. This token has the SameSite Lax attribute set.
Expected behavior: The client can read the cookie with the CSRF token in it. Actual behavior: The client cannot read the cookie. We've determined this by doing an alert(document.cookie) and seeing the CSRF token is not there when same-site lax is set, but is there when the same-site attribute is not set.
This causes the next POST request to fail because it can't get the CSRF token to be sent to the server. Though, if you look at the cookies that are sent in the request, it includes the cookie that has the CSRF token in it.
My understanding is that the cookie should be readable, because it is not cross-site in this context. And it certainly should not be unreadable, and then sent to the server on the next request.
My understanding is that SameSite Lax cookies should not prevent the client from reading this cookie.
As a fix, we've determined we don't need the SameSite Lax attribute on this particular cookie. However, we'd still like to understand the underlying cause of this issue.
Some details our investigation so far:
We can only reproduce the issue in the iOS Gmail app. We can't reproduce the issue by creating our own UIWebview or WKWebview (I ran in the iOS simulator for iOS 12.2). We can't reproduce it on the two iPads we tested on (though those are maybe different iOS versions). I tested on my iPhone running iOS 12.2
Based on using this method: https://stackoverflow.com/a/18678703/1176156 our application is not embedded in an iframe or anything when run in Gmail. We also disallow wrapping our site in an iframe via header.
You mostly answered your own question (and pointed me in the right direction :-) ).
For the sake of completeness, I found the relevant bug: Safari (still) doesn't send Lax cookies after a cross-site redirection.
This was fixed in release 77, which explains why the bug does not occur in iOS 12.3.1.
As it turns out, this must have been a bug in iOS 12.2, because I can no longer reproduce this behavior in iOS 12.3.1. I can't find an iOS changelog detailed enough to show this fix, though, and I didn't find anything relevant in the Webkit changelog.

WKWebview not syncronizing cookies after I log out of a domain, opened on it


I am developing a WKWebview app in swift. Here One needs to login to a specific domain. For this , I am throwing a cookie with logged in information/token.
But the problem occurs when I try to logout and the check if token exists?
And the token still exists even after logout.
Note - I checked on chrome browser on mac, and here it works perfectly.
WKWebView runs all of its networking in a separate process and thus does not ‘see’ your process’s cookie store.
The problem is that the WKWebView does not write back the cookies immediately. I think it does this on its own schedule. For example when a WKWebView is closed or maybe periodically.
In iOS 11 we added WKHTTPCookieStore to give you full access to the web view’s cookie store.
Supported cookie sync with WKWebView on older platforms is tricky. There are two techniques that might work:
You can set a cookie in the headers of the request you pass to
[WKWebView loadRequest:].
You can get and set cookies from within the web view by running
JavaScript code (using -evaluateJavaScript:completionHandler:) that
accesses the JavaScript document.cookie value.
For more reference https://forums.developer.apple.com/thread/95301 additionally some workaround Getting all cookies from WKWebView

iOS sporadically sends old cookies

I have an application that rotates an auth token cookie values regularly.
Each time the server rotates the token, it will not mark it as "good" until it sees the client has the token (cause the client includes it in the request headers for a resource).
I have a very specific situation ONLY on iOS (10.3) where sporadically it will send a very old cookie when network conditions change (eg: get off the subway). When this condition hits it "forgets" about the most recent cookie value and "starts living in the past" and sends and old value.
** Security note: IP addresses are publicly allocated t-mobile in NYC and token has long been deleted from our DB
Is this a known issue?
Are there any workarounds for cookie handling that is more robust on iOS? localstorage is not ideal cause these cookies are http only.
To clarify ... this is the flow:
Client (iOS Safari) has a cookie called _t with the value old
Client (iOS Safari) makes a request to the server
We issue Set-Cookie and set _t cookie to a new value new (http only, secure cookie)
Client makes another request with the new cookie value new. We flag that the cookie value is good and client has it.
Time passes
Client makes a request with the _t cookie with the value old
Here is my theory of what happened:
From the cookies lifecycle, whenever user authentication state change (login user -> logout user || logout user -> login user), the old cookie would be invalidated and replaced with a new cookie.
But why that happened in the subway and not other places?
1. These days most subways provide free unsecured WiFi to supplement the bad wireless network connectivity while underground.
2. There were some reports on network connectivity issue in 10.3, and
this one in particular is interesting, as the issue was location dependent.
3. I think the combination of (1) and (2) above was causing the app to reauthenticate to the server. Maybe you could pull the logs to check if that is indeed the case?
Possible workaround?
Maybe none. We can't prevent iPhone user from doing iOS upgrade. And most already did.
Also, the security repercussion of not changing cookies after reauthentication is worse.
Update based on the comment as of 05/31/2017:
Given the details as in the comment. We could have better explanation.
In cookie life cycle, when user logout, server-side-invalidation should take place.
The work flow:
1. When the user logout, the authenticated sessionID is deleted from the browser.
2. But that's not enough. The server needs to invalidate that sessionID too. Else there could be security repercussion.
3. It could be that in your case the server didn't invalidate. Thus it still expecting a SessionID which has been deleted from the browser.
This is just one possible explanation. To be exact, more details log file analysis and more experiment would be required.
For example, during that period, at the server log were there any reauthentication took place? Could we test in a controlled environment, if the server-side-invalidation has been implemented properly?
My experience
I also use authentication via IDs that change with each request and are stored in cookies.
I can confirm this behavior and it is still present in iOS 11 (and iOS 11.1 beta 3). In my log files, I can see that sometimes old cookies are randomly stored in Safari. This happens in a standalone webapp when it is closed and reopened.
My Fallback method
I built a fallback method over localStorage. A request with an old cookie will normally mark all data in my database as invalid. If the user agent points to a device with iOS, I do not mark the data as invalid and give the client a second attempt to authenticate itself.
On the client side I check the localStorage and create new cookies with this data. Subsequently, a new authentication takes place. The localStorage is rewritten like the cookies with each request. If authentication fails again, I mark the data as invalid.
Safari View Controller no longer shares cookies with Safari in iOS 11 and up, this change resolved the cookie store corruption issues that plagued iOS. We have not experienced this issue ever since iOS 11 was released.
Maybe that's caused by automatic retries?
Those posts mention that can happen under bad network conditions (like you said, subway):
https://medium.com/#fagnerbrack/the-day-a-bug-was-fixed-only-because-the-ceo-called-in-f653a34079eb
https://blogs.oracle.com/ravello/beware-http-requests-automatic-retries
SQLite database, if you're willing to sacrifice a little security.

Rails 3 and iOS Architecture Review

My goal is to build a standalone RESTful Rails 3 service that communicates with a Rails 3 web application via ActiveResource JSON and an iPhone application via iOS 5 native JSON. I have each running so that a single table of data is being exposed in the service app and that can be called and rendered via both a Rails app and the iPhone app.
My question is around authentication and something that can be reusable for both the web application and the iPhone app or in the future an Android app.
From the research I have done on this site, it seems HTTP Basic would work for both, however I would be unable to properly logout a user on the web side like sessions or cookies could and I have the browser login form to deal with. If I use sessions, how would that translate to setting up authentication on the iOS side of things?
This project is a code learning exercise, so I am hoping for implementation or architectural guidance rather than simply implementing Devise or Authlogic, etc.
It sounds like you're conflating at least two problems.
The first issue is authentication: you need to determine if the user is who they say they are. For authentication, you can do basic auth. You could also use client certs, though that's probably not what you're looking for.
The second thing is session management: First, you can do basic auth on each page request and store the session state in the database, but you're right about not being able to log the user out, as the browser will cache the credentials.
You may want to consider a login page that requires basic auth and shoots back a cookie to do session management. All other pages don't require basic auth, but give a 401 unauthorized if the cookie isn're present. Or you could redirect. The iOS client code will have to know to call the login page first to get the cookie and then use it after that. Logging out is deleting the cookie.. hrmm, but the browser will still cache the basic auth credentials.
I'm thinking the only way you're going to get what you want is to have a form-based auth for your web users (to allow them to log out and log in as someone else), and a basic-auth based system for iOS users. As a result of both authentication mechanisms, return a cookie that has to be used for all other pages.

Resources