1

I've read dozen of posts on this subject, most of them rely on deprecated Android APIs and I finally tried to use this but without success:

So I get the csrftoken like that:

URL url = new URL(urlString);

HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setUseCaches(false);
urlConnection.setRequestMethod("GET");

if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
    String COOKIES_HEADER = "Set-Cookie";
    site.setCookieManager(new java.net.CookieManager());

    Map<String, List<String>> headerFields = urlConnection.getHeaderFields();
    List<String> cookiesHeader = headerFields.get(COOKIES_HEADER);

    if(cookiesHeader != null)
    {
        for (String cookie : cookiesHeader)
        {
            if (cookie.startsWith("csrfToken")) {
                site.getCookieManager().getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
            }
        }
    }

    urlConnection.disconnect();
} else {
    urlConnection.disconnect();
    return null;
}

I also tried to get all informations from the header but it doesn't change the things.

And then, during posts requests I insert the token like that:

urlConnection = (HttpURLConnection) url.openConnection();

if(site.getCookieManager().getCookieStore().getCookies().size() > 0)
{
    urlConnection.setRequestProperty("Cookie",
            TextUtils.join(";", site.getCookieManager().getCookieStore().getCookies()));
}

if ((params != null) && !params.isEmpty()) {
    urlConnection.setDoOutput(true);
    urlConnection.setChunkedStreamingMode(0);
    urlConnection.setRequestProperty("Accept-Charset", "UTF-8");
    urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + "UTF-8");

    OutputStream output = urlConnection.getOutputStream();
    output.write(params.getBytes("UTF-8"));
    output.close();
}

is = urlConnection.getInputStream();

So if I look at urlconnection datas, I can see:

requestHeaders
    nameAndValues
        0 = "Cookie"
        1 = "csrkToken=5f62......973"
        2 = "Accept-Charset"
        3 = "UTF-8"
        4 = "Content-Type"
        5 = "application/x-www-form-urlencoded;charset=UTF-8"

But when I execute urlConnection.getInputStream(), I get the following exception:

java.io.FileNotFoundException: http://my.example.com/mywebservice
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:238)
    at com.ndguide.ndguide.JSONParser.getJSONFromUrl(JSONParser.java:93)
    at com.ndguide.ndguide.MainActivity.sendRegistrationIdToBackend(MainActivity.java:1562)
    at com.ndguide.ndguide.MainActivity.access$600(MainActivity.java:82)
    at com.ndguide.ndguide.MainActivity$2.doInBackground(MainActivity.java:1160)
    at com.ndguide.ndguide.MainActivity$2.doInBackground(MainActivity.java:1122)
    at android.os.AsyncTask$2.call(AsyncTask.java:295)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at java.lang.Thread.run(Thread.java:818)

May I precise that if I don't load the Csrf component on the server side, everything goes fine.

I also tried to add the following lines at the begining of my app as I read here, but it cause the same exception:

CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);

So what's wrong with my headers?

Community
  • 1
  • 1
fralbo
  • 2,534
  • 4
  • 41
  • 73
  • `csrkToken`? Also please always mention the exact CakePHP version that you are using, you are saying CSRF component, so that indicates Cake 3.x, but nobody can be really sure (by default Cake 3.x uses long living tokens, where as Cake 2.x uses one-off tokens). – ndm Dec 31 '15 at 15:36
  • @ndm sorry, ``csrfToken`` in CakePHP 3.1. In fact I have this problem since the bug in the csrf component was solved in beta version AFAIR. I suppose that my problem is on Android side because I don't have any problems with other web services. But I regularly try to solve it when I find some articles on the subject without success. Can I check something in CakePHP csrf component to have more info? – fralbo Jan 01 '16 at 17:39
  • So, the `k` in `csrkToken=5f62......973` is just a typo in this example? If so, then I'd suggest that you start with debugging what exactly the CakePHP side receives, ie log a request data/cookie dump. – ndm Jan 01 '16 at 18:02

1 Answers1

2

So, thanks to ndm advice, I explored what Csrf Component expects and here I explain what should be done to fully satisfy it as I only found partial explanations up to now, at least for a noob like me.

First we have to perform a GET request to obtian the csrf token:

URL url = new URL(urlString);

HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setUseCaches(false); // Don't use a Cached Copy
urlConnection.setRequestMethod("GET");

if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
    String COOKIES_HEADER = "Set-Cookie";
    site.setCookieManager(new java.net.CookieManager());

    Map<String, List<String>> headerFields = urlConnection.getHeaderFields();
    List<String> cookiesHeader = headerFields.get(COOKIES_HEADER);

    if(cookiesHeader != null)
    {
        for (String cookie : cookiesHeader)
        {
            if (cookie.startsWith("csrfToken")) {
                site.getCookieManager().getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
            }
        }
    }
}
urlConnection.disconnect();

Then in your post request, you have to copy back your csrf token in several ways. This is what I missed:

try {

    HttpURLConnection urlConnection = null;
    InputStream is = null;
    JSONObject jObj = null;

    URL url = new URL(urlString);

    urlConnection = (HttpURLConnection) url.openConnection();

    String csrfToken = null;

    if(site.getCookieManager().getCookieStore().getCookies().size() > 0)
    {
        //While joining the Cookies, use ',' or ';' as needed. Most of the server are using ';'
        urlConnection.setRequestProperty("Cookie",
                TextUtils.join(";", site.getCookieManager().getCookieStore().getCookies()));

        for (HttpCookie cookie : site.getCookieManager().getCookieStore().getCookies()) {
            if (cookie.getName().equals("csrfToken")) {
                csrfToken = cookie.getValue();
                urlConnection.setRequestProperty("X-CSRF-Token", csrfToken);
            }
        }
    }

    if ((params != null) && !params.isEmpty()) { // To put your posts params AND the csrf Cookie
        urlConnection.setDoOutput(true);
        urlConnection.setChunkedStreamingMode(0);
        urlConnection.setRequestProperty("Accept-Charset", "UTF-8");
        urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + "UTF-8");

        OutputStream output = urlConnection.getOutputStream();
        output.write(params.getBytes("UTF-8"));

        if (csrfToken != null) {
            String token = "&csrfToken=" + csrfToken;
            output.write(token.getBytes("UTF-8"));
        }

        output.close();
    } else {
        OutputStream output = urlConnection.getOutputStream();
        output.write(params.getBytes("UTF-8"));

        if (csrfToken != null) {
            String token = "csrfToken=" + csrfToken;
            output.write(token.getBytes("UTF-8"));
        }

        output.close();

    }

    is = urlConnection.getInputStream();

    int status = urlConnection.getResponseCode();

    if (status == HttpURLConnection.HTTP_OK) {

        /**
         * Do your job
         */

    }

} catch (IllegalArgumentException | NullPointerException | UnsupportedEncodingException | SocketTimeoutException | IOExceptione) {
    e.printStackTrace();
} finally {
    if(is != null) {
        is.close();
    }
    urlConnection.disconnect();
}

Hope this helps.

fralbo
  • 2,534
  • 4
  • 41
  • 73