Jose Juan MontielJose Juan Montiel

Previous step

To perform tests under different domains, we use xip.io In addition to lifting the same code with:

mvn spring-boot:run -Drun.jvmArguments="-Dserver.port=9000"
mvn spring-boot:run -Drun.jvmArguments="-Dserver.port=9001"

Cookies

The cookies, described in a simple way, are the mechanism invented by browsers, to store information sent by the servers, and that was maintained between petition and petition, that is, between one website served and the next. Also, it had to be reliable, therefore one use, was to keep the "session" between different requests to server, something not conceived in the HTTP that is "stateless", therefore it was also used for security or authentication.

And to avoid security problems, a server can only issue cookies from its domain and a page, you can only read (from javascript) those that belong to your domain.

Cross domain cookies

The way in which a "page" can communicate with another "page" is something that counts aqui. And aqui explain it in more detail

But as you walk, you learn by walking, we will use SpringBoot to ride a proof of concept. We create a project with the following controller.

    @RequestMapping("/cookie1")
    public String greeting(
    		@RequestParam(value = "rp", required = false) String reqparam, (1)
    		@RequestParam(value = "sp", required = false) String sesparam, (2)
    		@RequestParam(value = "cp", required = false) String cookiparam, (3)
    		HttpSession session,
    		HttpServletResponse response,
    		Model model) {

    			if (reqparam!=null) model.addAttribute("rp", reqparam); (1)
    			if (sesparam!=null) session.setAttribute("sp", sesparam); (2)
    			System.out.println("sp="+sesparam);

    			if (cookiparam!=null) {
    				Cookie newCookie = new Cookie("cp", cookiparam); (3)
    				newCookie.setMaxAge(24 * 60 * 60);
    				response.addCookie(newCookie);
    			}

    	return "cookie1";
    }

    public static String getCookieValue(HttpServletRequest req, String cookieName) {
    	return Arrays.stream(req.getCookies()).
    			filter(c -> c.getName().equals(cookieName)).
    			findFirst().
    			map(Cookie::getValue).
    			orElse(null);
    }
1 We received a paramtro for querystring, which we added to the model
2 We received a paramtro for querystring, which we keep in session
3 We receive a paramtro for querystring, which we store in a cookie

If we focus on case 2, it receives a parameter per url, which is stored in the server session, and this generates a cookie, the JSESSIONID file that allows To establish which hollow of the session of the server, corresponds to the user.

Case 3 is a generalization of it, where the server in this case generates a cookie that reaches the browser.

    <p th:text="'Request param: ' + ${rp}" /> (1)
    <p th:unless="${session == null}"
    	 th:text="'Session param: ' + ${session.sp}" /> (2)
    <p th:unless="${#ctx.httpServletRequest.getCookies()==null}"
    	 th:text="'Cookie param: ' + ${T(com.example.demo.Cookie1Controller).getCookieValue(#ctx.httpServletRequest,'cp')}" /> (3)

    <img th:if="${#strings.startsWith(#httpServletRequest.getHeader('Host'),'a.')}"
    		 src="http://b.127.0.0.1.xip.io:9001/cookie1?rp=request_from_a&amp;sp=session_from_a&amp;cp=cookie_from_a" style="display:none;" /> (4)
1 We paint the model parameter
2 We paint the session parameter
3 We paint the parameter of the cookie
4 We invoke the other domain, only if we are not in the

In this page, we see how the values stored in the session are painted, and in the cookie.

And now the good starts, when we open: http://a.127.0.0.1.xip.io:9000/setcookie?rp=a&sp=a&cp=a this page apart from setting the values of the ulr in session and in a cookie, makes a request to a url of domain B (through an img) http://b.127.0.0.1.xip.io:9001/cookie1?rp=request_from_a&sp=session_from_a&cp=cookie_from_a and we can see the results by watching: http://b.127.0.0.1.xip.io:9001/setcookie

Navigation would be something of this style.

  1. Flow of cookies navigation

Browser -> DomainA: Parameters are sent
Browser -> Domain: Parameters are sent
DomainA -> Browser: Generates cookie
DomainB -> Browser: Generates cookie

And here we see the results, which we will comment:

a domain img1

In the first request from A, we observe how the JSESSIONID cookie is generated in A, that tells us that something has been saved in session in the server, and that value, we observe in the browser when painting the value of the object in session sp, which is b. What not yet we observe in the page, it is how it receives the cookie that is generated in server, although if we would see it in the cookie inspector.

On the other hand, we observe in the network inspector, how a request is sent to domain B, generated by the JSESSIONID cookie in the browser but for domain B. In the fourth image we will see how the System.out shows us that querystring arrives and it has been stored in session, but beware of domain B.

a domain img2

In a second recharge, we would see the cookie generated in server A, based on to the previous request.

b domain img1

The curious thing begins now, in a third petition to Domain B directly, without parameters. As our controller does nothing if parameters do not arrive, we see, when entering domain B, as the browser reads the B domain cookies, generated when navigating through A, but with a request to B from an IMG, like the server we returns what you have stored in session for us, navigators of your domain. And this is, what is generated from another domain, making that request from an image.

b domain img2

Here we see the System.out

Conclusion

While browsing through domain A, we can "send" to a domain B information, that domain B, upon receiving it in that image request, can store it in its session for us users that we navigate, so that when we go to the domain B, recover it from session and show it to us.

The next steps would be to try to make a third party services (domain A), that we can integrate in our webs (domain B), via APIs JS, "send us from that JS "information, that allows us below (from the back in domain B) make a request to them (domain A) and recover what the third-party service (domain A) stores in your session for us users that we navigate, for example that we are logged in your service.

  1. Navigation flow with third-party service

Browser -> DomainB: Step1
Browser -> DomainA: Step2
Browser -> DomainA: Step3
DomainA -> Browser: Step4
Browser -> Browser: Step5
Browser -> DomainB: Step6
DomainB -> DomainA: Step7
DomainB -> Browser: Step8
  • Step1: We browse our website

  • Step2: API JS calls the third-party service and shows us login

  • Step3: We can do third-party service using JS API

  • Step4: The third party service generates session cookies

  • Step5: The browser generates an <img> pointing to B, with the information of the JSESSIONID that A generates.

  • Step6: That image, generated by the third-party API, on the page that we serve from B, calls us and stores that value in B’s session.

  • Step7: B makes a request to A, from server, using the JSESSIONID of A, to impersonate us that we navigate B, using JS API of A.

  • Step8: We can show to us that we navigate, info provided by A, via backend communication, after logarnos.