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.
Related
I have a Cordova app that uses WKWebView.
I'm using permanent cookies to authenticate with my (rails) server.
The cookies are stored and sent with subsequent requests, and even continue to be sent after app restarts.
However after some time of not using the app, usually a few days, no cookies are sent with any requests, and I need to re-authenticate to get new cookies.
The cookies are set to expire in 20 years, so I think there must be some other mechanism that is clearing the cookies.
Has anyone else experienced this?
Apple says this about cookies (on MacOS, but it’s equally valid on iOS)
(It’s a pop up on this page)
So it’s a bit arbitrary and hard to predict which cookies get ejected after a week, but a test/dev setup (raw iPs etc.) probably looks rather sketchy to the AI. If users regularly return, and the cookies are first-party, this shouldn’t happen.
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.
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
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
I am using .Net core for backend, and authenticating users using cookies. My ionic application is working perfectly fine on android, where cookies are returned on login and are set automatically and being used for every request.
Trying it out on iOS, the cookies work fine as long as the app has not been closed (meaning both cookies sent from the server are being returned with every request).
If the app is closed and then restarted, I notice only one cookie being sent with the requests. This behavior is very weird since one cookie is being saved and is persistent throughout app restarts, in opposition to the other cookie that is no longer available after the restart. Does anyone have the slightest idea of what might be causing this?
Again, the same code was used for both platforms, and no manual handling for the cookies is being done.