Background:
I am experiencing a very confusing behaviour with android Webviews in API 21 and up when testing in real devices.
I have a local HTML5 application (inside assets folder) with the following functionality
Login (2 steps authentication).
Show a list of items depending on the authentication.
The problem:
After doing the login requests, the server returns a cookie with the session. This cookie is not stored in the Webview when using real devices with API 21 or up. If I use emulators (Genymotion in this case), the cookies are properly stored.
More information:
The request to do the auth has the following headers:
POST http://myServer/j_spring_security_check HTTP/1.1
Proxy-Connection: keep-alive
Content-Length: 101
access-control-allow-origin: *
accept: application/json
access-control-allow-credentials: true
User-Agent: Framework/1.5.0 (Linux; U; Android 6.0.1; Nexus 5X Build/MMB29Q) App/0.1.1
Origin: file://
content-type: application/x-www-form-urlencoded
Accept-Language: en-US
X-Requested-With: app.package
Host: myServer
With the following response:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4D169E8656DBEDFFA4D17FE8D436A5BA; Expires=Fri, 19-Feb-2016 14:27:55 GMT; Path=/; HttpOnly
Content-Type: application/json;charset=UTF-8
Content-Length: 43
Date: Fri, 19 Feb 2016 14:17:55 GMT
The cookie is not stored in devices with API 21 or more. Same request/response works fine in the rest of devices + all the emulators
Clarification:
This flags are enabled inside the app:
android.webkit.CookieManager.setAcceptFileSchemeCookies(true);
(Before CookieManager or webview is instantiated, as documentation says)
if(VERSION.SDK_INT >= 21) {
CookieManager.getInstance().setAcceptThirdPartyCookies(this.nativeWebView, true);
}
If after doing the authentication, I access the cookies datastore and
check the "hasCookies" method, I get false.
The two step auth service actually calls 3 different paths from the same endpoints. None of the cookies that the response that generate this services are stored. I don't know if this is relevant or not.
When doing simple authentication (to a different server), cookies are stored properly in all the devices emulators.
I am using Angular 1.5
I am aware that the service is using http instead of https. That will be solved in the future.
I get no error message in the consoles.
Questions:
Is there any internal security measure in the webviews that blocks the storage of the cookies? Why does it work on emulators (that are rooted devices) and not in real devices? This really bugs me.
If the network request is done using window.fetch you may need to add:
fetch('/something', { credentials: 'same-origin' }) // or 'include'
On chromium, window.fetch has the credentials flag set by default to 'omit' and no cookies are stored into the cookie storage. More details of this bug here: https://bugs.chromium.org/p/chromium/issues/detail?id=477523
Related
I am trying to connect to a WebSocket server that my Android device connects to from an app. I captured the packets on my Android device, and the initial request headers look like this:
GET / HTTP/1.1
Pragma: no-cache
Cache-Control: no-cache
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ysWaBflPV9EmRaB1JpPMOQ==
Origin: http://example.com:80
Sec-WebSocket-Protocol: default-protocol
Sec-WebSocket-Extensions:
Sec-WebSocket-Version: 13
The response from the server looks like this:
HTTP/1.1 101 Switching Protocols
Date: Wed, 31 Jan 2018 02:37:20 GMT
Connection: upgrade
Set-Cookie: AWSALB=0yaRd5HOPlZSITp+bcXoZoIn/7YOOqE9o4/t/8b3kw2PTxooflm/85w+1JfudEE0Cwb1BUkWPV+t4kOnEm4FbLSWwMMFp8URbblZKj0a0kd0xB+glbLBHWxc/TPW; Expires=Wed, 07 Feb 2018 02:37:20 GMT; Path=/
Server: nginx/1.12.1
Upgrade: websocket
Sec-WebSocket-Accept: bj5wLF8vmyDrA7pqEgbHKbxqQSU=
Then, some communication begins, with lots of unrecognizable characters and some clear words in the messages. I don't have much experience with WebSockets, but I assume it is some form of compression.
I was able to send an identical request to this server using the ws module in Node.js, and I got an identical response to the one above. One notable difference was that when I set the protocol header to default-protocol, I received an error saying "Server sent no subprotocol". Without using this header, I still got the same response.
After the initial response, however, I did not receive any more messages, even though I did on my Android device. After about 30 seconds, the connection closes with code 1006 and no further information.
I tested the same request with curl and received the same headers back, but it also closed after about 30 seconds saying:
"Empty reply from server"
So my main question is obviously: What is going wrong, and how can I fix it?
More specifically, I am wondering if anyone with WebSocket experience knows if it is a problem with my client, or with the server itself.
It is possible that the server is authenticating me in some way on my Android device, but the headers that I captured are not revealing anything about that. Is it ever customary to authenticate a connection with a later message in the client-server communications? Is it possible that a separate HTTP request is authenticating me for this WebSocket server? All of these things seem unlikely to me since I found no other packets with anything related to auth requests. It seems much more likely that there is something wrong with the messages being sent.
Our Android users have started complaining that every time they kill our app, they have to log in again. I am able to reproduce this on our Android devices, but not our iOS devices, not through our mobile website on any device, and not through our desktop website.
When I attach the Chrome debugger to our Cordova app running on Android, it looks like Django is failing to set the sessionid cookie on the login response path. On every other platform (including straight Chrome on Android) when I look at the response for that endpoint, the sessionid cookie is there.
Here is what the normal login response looks like in every browser other than Cordova Android. Notice the setting of the sessionid cookie:
Response Headers
Connection:keep-alive
Content-Encoding:gzip
Content-Length:654
Content-Type:application/json; charset=utf-8
Date:Sat, 23 Apr 2016 22:33:44 GMT
ETag:"11186a50be09093d01d4e82ff4d9d3e5;gzip"
Server:nginx/1.8.1
Set-Cookie:sessionid=25a9wodafd4zh8w0lzpklf8lnc7mxwbm; expires=Sat, 07-May-2016 22:33:44 GMT; Max-Age=1209600; Path=/
Vary:Cookie, Accept-Encoding
X-Frame-Options:DENY
X-Handled-By:127.0.0.1:8000
Here is the response I'm getting in Android through our Cordova app:
Response Headers
Connection:keep-alive
Content-Encoding:gzip
Content-Length:654
Content-Type:application/json; charset=utf-8
Date:Sat, 23 Apr 2016 22:52:23 GMT
ETag:"11186a50be09093d01d4e82ff4d9d3e5;gzip"
Server:nginx/1.8.1
Vary:Cookie, Accept-Encoding
X-Frame-Options:DENY
X-Handled-By:127.0.0.1:8000
The request succeeds and the user somehow has a session and can make purchases. They can background the app and bring it up and their session is still there, but if they kill the app and bring it back up, they lose their session.
When I connect the Safari web debugger to our iOS Cordova app, the login response looks good. The sessionid cookie appears in the response header and everything works.
I'm hoping that there's something obvious about this whole process that I'm missing.
I have an app engine server that uses webapp2 extras auth mechanism. I have both an Android and an iOS client, and of course the mechanism uses cookies in order to keep the session going.
The problem is, that the when the Android client tries to make a request after logging in - even if it sends the a request with the cookie - the cookie is ignored and the session is not recovered. When I use the iOS client - the session is verified successfully.
This is very bizarre and I can't put my finger on why this happens.
I debugged the session for both cases, and here they are:
iOS session:
Accept: */*
Accept-Language: en-us
Content_Length: 0
Content_Type: application/x-www-form-urlencoded;charset=UTF-8
Cookie: auth="eyJfdXNlciI6WzQ2Nzg2OTY4NTQwOTM4MjQsMSwiV3FmUnFWUmxUME91TllsYnZsMWFxOSIsMTQyMzM1MzI3MCwxNDIzMzUzMjcwLCJMaW9yIFphdGxhdmkiXX0\075|1423353270|c2c7343dbb701f188c18f8b16c0fe06b794ad2d2"
Host: localhost:8081
User-Agent: PeersCards/1.0 CFNetwork/711.1.12 Darwin/14.0.0
X-Appengine-Country: ZZ
INFO 2015-02-08 17:38:55,123 user_api.py:591] Session was recovered
Android session:
Content_Length: 0
Content_Type: application/x-www-form-urlencoded;charset=UTF-8
Cookie: auth=eyJfdXNlciI6WzQ2Nzg2OTY4NTQwOTM4MjQsMSwiZDBCam5Sc1lucElRTjMySWxKQ0NzZyIsMTQyMzQxOTI1MSwxNDIzNDE5MjUxLCJMaW9yIFphdGxhdmkiXX0\075|1423419251|68063593b0262fdb5c6b479457c95eb9fcc7047f
Host: 10.0.0.16:8081
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-N900 Build/KOT49H)
X-Appengine-Country: ZZ
INFO 2015-02-08 18:16:11,215 user_api.py:594] Session is not saved
Any ideas?
I've figured this out.
Apparently, the Android CookieManager was storing the cookie as:
auth=XXXXXX
And AppEngine was expecting:
auth="XXXXX…"
It can also be seen in the input I've placed in the question, though I seriously didn't think that would be the issue.
I've set the "" manually into the cookie in the Android code, and that worked out the problem.
I launched burp as an emulator's proxy for debugging of http requests from my application with intercepting option switched on and at the startup I found that emulator sends a GET request to google:
GET /generate_204 HTTP/1.1
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.3; sdk Build/JWR66V)
Host: 173.194.32.129
Connection: Keep-Alive
Accept-Encoding: gzip
And gets a response like:
HTTP/1.1 204 No Content
Content-Length: 0
Content-Type: text/html; charset=UTF-8
Date: Thu, 05 Sep 2013 06:56:51 GMT
Server: GFE/2.0
So I would like to know if there is some purpose for making this request to google at the startup?
It's most likely for counting things:
active developers
emulator use
framework use
generating statistics how developers are spread over the world
...
It's Android trying to tell if the Wifi (or other network connection) connection has internet. I'm testing on real devices and it does the same thing. If you don't forward the message the connection status in Android Wifi Setting will say "Connected. No internet" until you forward and it gets a success back.
We have a website that makes use of OAM for single sign on (form-based authentication). When we submit credentials to WebGate / Access Server the authorization succeeds, however after the authentication is performed, the form action (as configured in the Authentication Scheme - with passthrough:no) returns a server error instead of redirecting to the originally requested URL.
If we use Mini Opera, we are able to get authenticated and forwarded properly.
This problem happens on numerous Android phones (versions ranging from 1.5-2.2), as well as the Emulator provided with the SDK.
This is proving to be a real problem as the default browser on Android phones is not able to get access to our sites(and this is the only browser that is having this problem).
I have created a WebView-based custom browser with the hope of seeing a client-side error and tried trapping every possible error....none show up....
I have tried to trace all of the http requests and found only a single difference in the requests... the http header for Connection:keep-alive is not sent by the Android WebView.
I have provided some tracing info below...
Has anyone run into this problem? Has anyone solved this?
Any insight to this issue would be greatly appreciated.
Thanks,
Tim
Request RAW Data-
POST
http: // MYSERVER/security/ATLAFunction HTTP/1.1 Host: MYSERVER:7777
Accept-Encoding: gzip
Accept-Language: en-US
Cookie:ObSSOCookie=loggedoutcontinue
Accept-Charset: utf-8, iso-8859-1,utf-16, ;q=0.7
Referer:http://10.84.32.71:7777/tpf/login.html
User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; en-us; sdk Build/FRF42) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Origin: http: // MYSERVER
Accept:application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,/*;q=0.5
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
uname=auser&pwd=appas
Raw Response Data -
HTTP/1.1 503 Service Temporarily
Unavailable Date: Tue, 05 Oct
2010 14:26:12 GMT Set-Cookie:
ObSSOCookie=II%2F4n5pFreT6B6hOAumv6pI6CZh6l04VhyXHrCzuRUT5hDEHMK%2FJCX659uyCkxgIyJ8ywB3BKrHxorsCwZwivpn91t9Mu%2FCKT7PrY23S518xoBeOam26tr%2B0pSfCbo%2FZXLmFIxjHFOPHPGxi5tHrOlUroXXA9Fe0GZz3SbJLMgAkCw0euuAVewOHKIjoDh8MwAdGtL4lo%2BmHhk5kB316iFJ4Aljr7cQYpAp1r%2BVGD9FbLkYl4ekY5hrlNfwYS%2BVjnR0uSIFjc0toiKkGN33z7%2FiElh2Ue2iWQrpCRcgFpxE%3D;
httponly; path=/; Cache-Control:
no-cache Pragma: no-cache
Content-Length: 312 Connection:
close Content-Type: text/html;
charset=iso-8859-1
<!DOCTYPE HTML PUBLIC
"-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>503 Service Temporarily
Unavailable</title>
</head><body>
<h1>Service Temporarily
Unavailable</h1>
<p>Sorry!The server is
currently unable to handle the
request due to a temporary
overloading or maintenance of the
server.</p>
</body></html>