3

When my application retrieves index.html and other js and css files from the server, I do not see an csrf token in headers or cookies. How does play sends csrf token?

My UI is an Angular application being served from play. From the documents, I read about csrf token that

This token gets placed either in the query string or body of every form submitted, and also gets placed in the user’s session

The documentation also says that

To ensure that a CSRF token is available to be rendered in forms, and sent back to the client, the global filter will generate a new token for allGETrequests that accept HTML, if a token isn’t already available in the incoming request. - But I don't see this token in response to my initial GET request.

As my UI (and thus form) is not a play UI, I cannot use play's annotation to put csrf token in the form. I would like that when the homepage is delivered, play sends the csrf token which Angular application can store and use later.

Following are the headers I see on browser's developer console.

Response headers

Content-Length      1421
Content-Type        text/html; charset=UTF-8
Date        Sun, 11 Mar 2018 21:23:52 GMT
Referrer-Policy     origin-when-cross-origin, strict-origin-when-cross-origin
X-Content-Type-Options      nosniff
X-Frame-Options     DENY
X-Permitted-Cross-Domain-Policies       master-only
X-XSS-Protection        1; mode=block

Request headers (600 B)

Accept      text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8
    Accept-Encoding     gzip, deflate
    Accept-Language     en-US,en;q=0.5
    Connection      keep-alive
    Cookie      PLAY_SESSION=eyJhbGciOiJIUzI1N…AR2uh5KwKBhqKxQQT1wWPWC2yPyCM
    Host        localhost:9000
    Upgrade-Insecure-Requests       1
    User-Agent      Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/58.0

The Action in play which servers the homepage is

def index =
    Action { implicit request =>
      val Token(name, value) = CSRF.getToken.get
      println(s"Token name ${name}, value ${value}")
      Ok(views.html.index("Your new application is ready."))
    }

I can see (print) the token name and value but I am not sure if it is being sent in the Ok response.

Manu Chadha
  • 15,555
  • 19
  • 91
  • 184
  • your code `val Token(name, value) = CSRF.getToken.get` seems to apply to play forms, not ajax responses. Did you find a way to implement CSRF in play/angular? – ps0604 Aug 04 '18 at 15:26

1 Answers1

2

This is a partial answer. The 3 csrf configurations of interest in play are token, cookie and header names

if none of the token, cookie and header of csrf properties are configured then the default values are csrfToken for token name), nothing gets configured for cookie and Csrf-Token for header

When token name is configured then play seem to send a PLAY_SESSION cookie. Eg token.name = "CJCsrfToken". In this case, the name of the token is CJCsrfToken instead of csrfToken. However, I couldn't find how csrfToken gets sent and how to retrieve it in the client. I have an Angular5 client and I couldn't get it to pass csrf when only token.name was configured in play.

If cookie name is configured, Play will store the csrf token in a cookie with the given name, instead of in the session. I suppose we should configure either token.name or cookie.name. Eg cookie.name = "CJCsrfCookie" means you should see a cookie with name CJCsrfCookie

Now if only cookie.name is configured but no header name is configured then Play expects that requests from client will contain the csrf token in header Csrf-Token (the default header name)

The code in Angular to accept the cookie and return header was

HttpClientXsrfModule.withOptions({ cookieName: 'CJCsrfCookie', headerName: 'Csrf-Token' }),

If you do not want to use default header name, configure the new name in header.name. This would be the name of the header to accept CSRF tokens from. eg header.name = "CJCsrfHeader"

The code in Angular to accept the cookie and return header was

HttpClientXsrfModule.withOptions({ cookieName: 'CJCsrfCookie', headerName: 'CJCsrfHeader' }),

Note that for the Angular part, the url has to be relative. See this angular4 httpclient csrf does not send x-xsrf-token has

Manu Chadha
  • 15,555
  • 19
  • 91
  • 184