I am trying to open a Web view of a site (in Android) that needs token/Cookie in the headers for authentication.
I try to open the page using code below
HashMap<String,String> headers = new HashMap<String,String>();
headers.put("Cookie","MyToken");
MyWebView.loadUrl("https://myURL.com",headers);
I am hitting the onReceivedError with following values
errorCode: -6
*description: The connection to the server was unsuccessful.*
(PS: Since this site can be access from Intranet, using emulator I don't see this error the page loads correctly. Also HttpPost works fine with same token)
This is just a quick post about adding cookies to a web view. If you’ve ever tried to do this the way most people have said that it should be done, you’ve failed miserably and found this post. :)
The way it’s supposed to work is you set the cookie on the CookieManager and then tell the CookieSyncManager to sync.
CookieManager.getInstance().setCookie(domain, value);
CookieSyncManager.getInstance().sync();
I’ve never got this to work as described. With or without async tasks waiting for the threads to catch up.
Instead, I just add the cookie in the header of all the loadUrl calls.
Map<String, String> headers = new HashMap<String, String>();
headers.put("Cookie", "cookieName=cookieValue;domain=domain.com;path=/;Expires=Thu, 2 Aug 2021 20:47:11 UTC;");
webView.loadUrl("myurl.com", headers );
Caveat: I only need to initially load the appropriate cookie for the request, if you want to cover nested calls from inside the browser, you need to override shouldOverrideUrlLoading.
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url, headers);
return false;
}
});
If you need to inject the cookie for all requests(including images, js, etc), you’re going to need to override shouldInterceptRequest,
That is not a valid cookie header. Try:
headers.put("Cookie", "foo=bar");
Related
My project for an Android app uses Checkmarx to scan the source code for security issues. Checkmarx reports an issue with the Google Volley library. Here is the error description:
Method performRequest at line 89 of
\app\libraries\volley\src\main\java\com\android\volley\toolbox\HurlStack.java
gets user input for the getHeaders element. This element’s value then
flows through the code without being properly sanitized or validated
and is eventually displayed to the user in method parseNetworkResponse
at line 61 of
\app\libraries\volley\src\main\java\com\android\volley\toolbox\JsonArrayRequest.java.
This may enable a Cross-Site-Scripting attack.
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
....
}
It says the headers of the request are cached and later displayed to the users in the JsonArrayRequest::parseNetworkResponse. However I can not find it is displayed to the user at all. Is this a false alarm? And in what condition a Cross-Site-Scripting attack may occur on an Android app? Does that only happen when you use a webview?
Let me answer your last question first. Yes, almost only WebViews are affected by it. Another (less common) scenario can be if an XSS payload is saved into a file and later opened by a browser.
HTTP headers are considered valid vector for XSS, so it doesn't seem like a false alarm. Here are two resources for further reading:
Cross-Site Scripting in HTTP Headers
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
So I've been dealing with my problems for some time now with the help of Internet, but I've only had minimal success.
The problem: I'm using Android with the Volley library to try and perform a search from two websites. The websites in question don't have an open API or anything to see how I could perform the searches. I've tried inspecting the HTTP requests in Firefox with the view opening with F12.
Website 1
Uses HTTPS protocol. I need to search from here using the Vnr number. From the resulting page I would parse the name(s) of the drugs.
Website 2
Uses HTTP protocol. From here I would search using the found drug name and I would get the drug description (in Finnish).
For Website 2 I tried the following code, which successfully returns the HTML file of the result page:
RequestQueue queue = Volley.newRequestQueue(this);
String url = "http://www.laakeinfo.fi/Medicine.aspx?m=758&d=2111337&i=ORION+PHARMA_RESILAR_RESILAR+oraaliliuos+3+mg/ml";
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("HTTPTEST", "Response len: " + response.length());
writeToFile(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("HTTPTEST", "Error: " + error.getMessage());
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("i", "Resilar");
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
};
queue.add(request);
Now this works because the URL points straight to where I want it to. If I changed the URL to "http://www.laakeinfo.fi/Search.aspx" and in getParams() I'd use instead params.put("Search1:txtSearch", "Resilar"); (the search bar's contents) it just returns the search page itself.
Now my questions is, is what I'm trying to do possible? I'm not too well in the know with web stuff, but it seems the pages hide the search logic and everything in a way that makes it difficult to perform the searches. If it is indeed possible, how could I go about it? Any hints would be appreciated in this attempt.
The quick answer is YES, you can do it! ... but it might be not that simple :)
So servers might check many many things in the request and even 'insignificant' data like Accept-Language: header might cause the service to return or not a response.
you have 2 main things you have to check and experiment with and 1 remark:
1) Headers
Host: xn--lkeinfo-5waa.fi
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://xn--lkeinfo-5waa.fi/Search.aspx
Cookie: ASP.NET_SessionId=mxj22y2oofzwcnmtcen50c45
these might change the behavior so you need to experiment there. I would try to put all that I think might be useful or do not understand. Here I would remove just the 'Cookie' and 'Accept-Encoding'.
2) Params
__EVENTTARGET:"Search1$lbSubmit"
__EVENTARGUMENT:""
__VIEWSTATE:"/wEPDwUKMTc2NzY1NjI3Mg9kFgJmD2QWBAIDD2QWAgIFDxYCHgVjbGFzcwUGYWN0aXZlZAIFD2QWBGYPFgIfAAUJZnJtc2VhcmNoZAICDw8WBh4IQ3NzQ2xhc3MFA2J0bh4EVGV4dAUDSEFFHgRfIVNCAgJkZGQ2BqYJzINvRHYkGXr7+fwtg0L8cQ=="
__VIEWSTATEGENERATOR:"BBBC20B8"
__EVENTVALIDATION:"/wEWBALa8vLKCALQkcCMBAL43fjZDALY7fiNCoNRBovCyGbCM+mhYEACTRYyyeuF"
Search1:txtSearch:"Resilar"
Search1:hdLanguageId:"1"
Although 'Search1:txtSearch' seems the obvious one to make a search the other might play part in it to so I would add all of those and remove them 1 by one and try out.
3) (remark) response 302 this is a post request which does not return a response but rather redirects to one. You have to be careful enable debug logs and check what Volley returns as this response is considered as an error in volley. It might be handled by the HTTP Stack Volley uses but it depends which one is set. If this is the case this will be transparent to volley but otherwise some customization on Volley needs to be done or use another stack. For example android has adopted and baked OkHttp in Android source as the native client actually used by URLConnection. So earlier versions like KitKat would not handle this redirect. The new releases however handles it.
I need help regarding SSL trust. I am loading a payment gateway page in WebView. It is a POST request and I am passing a payload.
This is happening successfully till now.(Shown below)
mWebView = (Android.Webkit.WebView)Control;
string payload = "myPayload";
byte[] valTest = Encoding.UTF8.GetBytes(payload.ToCharArray(0, payload.Length));
mWebView.Settings.JavaScriptEnabled = true;
mWebView.Settings.DomStorageEnabled=true;
mWebView.SetWebViewClient(new MyWebViewClient(this));
mWebView.SetWebChromeClient(new ChromeClient());
mWebView.PostUrl("https://mypage", valTest);
After filling up the form and submit I am getting a callback in the OnReceivedSslError method of the WebViewClient class. Here I ask it to proceed (as per various forums). Once this is done I am not getting any success callback. I need to be able to read javascript values once I get a response from the webview. I do not know how this is to be achieved.
Shown below is the OnSSLErrorReceived Callback method.
public override void OnReceivedSslError(Android.Webkit.WebView view, SslErrorHandler handler, Android.Net.Http.SslError error)
{
base.OnReceivedSslError(view, handler, error);
handler.Proceed();
}
}
Expectations-
1. On submitting the form it should be trusted(it is a self-signed certificate).
2. Once this above step is done we are expecting a response from the webpage. We need to read javascript values from the page. How can I achieve this?
I do not see any SuccessCallback or ResponseCallback.
I use the below to read the JS values.But this is not working.
view.EvaluateJavascript("document.getElementById('test').value", new MyCallbackClass());
The above issue has been resolved. Calling handler.Proceed in OnSSLError method was fine but the issue was with calling the base inside this callback. Once I removed base.OnSSLError my code worked fine.
After the page finishes I am able to read JS values in the OnPageFinished callback method.
Also the evaluate JS function is also working and I get callbacks.
I need to make Crosswalk on Android (in a Cordova project) use some
cookies I gathered via an Apache HttpClient.
However I'm not sure how to achieve this. I tried experimenting with
the XWalkCookieManager but I think everything I do with the
XWalkCookieManager is ignored. Where would I initialize the cookie
manager? Is this even supported yet?
//cookieStore contains the cookies I got via a request from the Apache
// HttpClient.
List<Cookie> cookies = cookieStore.getCookies();
for (int i = 0; i < cookies.size(); i++) {
Cookie cookie = cookies.get(i);
String cookieString = buildCookieStringFromCookie(cookie);
// This is a XWalkCookieManager I initialized earlier but it doesn't do anything
//as far as I can tell.
cookieManager.setCookie(cookie.getDomain(),cookieString);
}
I would really appreciate any help, this is the only thing that
currently keeps me from using Crosswalk successfully.
I have also tried using the standard Android cookie Manager via CookieManager.getInstance().setCookie(cookie.getDomain(), cookieString); but this seems to be ignored by Crosswalk as well.
Best,
Andreas
//Edit for future reference:
The issue was that Crosswalk expects the cookie url to start with https:// and the native Android webview doesn't (or the other way around, I'm not sure anymore). Now what is working fine is to set the cookie twice, once without https and once with https:
mCookieManager.setCookie(cookie.getDomain(), cookieString);
mCookieManager.setCookie("https://" + cookie.getDomain(), cookieString);
You can create an instance of XWalkCookieManager and set the differents cookies in it.
It seems to be shared accross the XwalkViews.
// Your can use this inside the onCreate() method
private XWalkCookieManager mCookieManager;
mCookieManager = new XWalkCookieManager();
mCookieManager.setAcceptCookie(true);
mCookieManager.setAcceptFileSchemeCookies(true);
// Pass it to your request executor
httpRequestExecutor.setXWalkCookieManager(mCookieManager);
//You're now able to add your cookies to this manager rather than to your cookieManager
How we extract the cookies from the HttpResponse :
responseHeaders = response.getAllHeaders();
for (int i = 0; i < responseHeaders.length; i++)
{
Header header = responseHeaders[i];
if (header.getName().equalsIgnoreCase("set-cookie"))
{
mCookieManager.setCookie(request.getUrl(), header.getValue());
}
}
The implementation of XWalkCooikeManager is a little different with Android CookieManager.
You should add schemes(http|https) before cookie.getDomain().
This is because the implementation of CookieManager in Android WebView use WebAddress(url).toString(), which will add schemes (http|https) into the url.
As the explanation in CooieManagerAdapter:
WebAddress is a private API in the android framework and a "quirk" of the Classic WebView implementation that allowed embedders to be relaxed about what URLs they passed into the CookieManager, so we do the same normalisation before entering the chromium stack.
While creating an Android app in Appcelerator's Titanium that involves both webView and background calls, I ran into a problem / bug where the cookies were getting corrupted on multiple createHTTPClient calls.
Cookies were originally obtained from the webView:
var webview = Ti.UI.createWebView();
webview.url = 'http://www.example.com';
window.add(webview);
webview.addEventListener('load', function(e) {
cookies = e.source.evalJS("document.cookie");
Titanium.App.Properties.setString('cookies',cookies);
}
window.open({modal:true});
and then later used with a background call:
var loader = Titanium.Network.createHTTPClient();
loader.open("GET",base_url + url);
loader.onload = function() {
// process response
}
loader.setRequestHeader('Cookie',Titanium.App.Properties.getString("cookies"));
loader.send();
The first time the above createHTTPClient chunk of code was called, everything worked, but subsequent runs of the above code would send corrupted cookies. In Google App Engine (gae), printing out the request headers would look like this (broken):
logging.info('Request:\n%s' % self.request)
broken response (only the cookie portion of the request header is shown)
Cookie: auth="eyJfdXNlciI6WzYsMSwiVmRoZEtnYWZRMnNxazFjaVM0U1lKdCIsMTM1NzIyMzcwOSwxMzU3MjIzNzA5XX0\075|1357223709|4f622167f477a8c82cab196af4b0029af1a966d7", auth=eyJfdXNlciI6WzYsMSwiVmRoZEtnYWZRMnNxazFjaVM0U1lKdCIsMTM1NzIyMzcwOSwxMzU3MjIzNzA5XX0\075|1357225569|7a469fab7a38a437649c25620729e07c4607f617
Cookie2: $Version=1
working response
Cookie: auth="eyJfdXNlciI6WzYsMSwiVmRoZEtnYWZRMnNxazFjaVM0U1lKdCIsMTM1NzIyMzcwOSwxMzU3MjIzNzA5XX0\075|1357223709|4f622167f477a8c82cab196af4b0029af1a966d7"
...
I suspect the issue has something to do with unicode characters or something inside createHTTPClient. Two auth= statements are shown in the corrupted cookie.
In summary, when the app first launches, the background Titanium.Network.createHTTPClient call works, and any calls after that appear to send corrupted cookies.
The HTTPClient documentation says "object is intended to be used for a single request," so I assumed everything would reset after multiple calls. But something was different after the first call.
Adding loader.clearCookies(base_url); to the code before setting the cookies seems to fix the issue.
var loader = Titanium.Network.createHTTPClient();
loader.open("GET",base_url + url);
loader.onload = function() {
// process response
}
loader.clearCookies(base_url); // THE FIX.
loader.setRequestHeader('Cookie',Titanium.App.Properties.getString("cookies"));
loader.send();