Verifying that Play's CSRF protection is working

161 Views Asked by At

I'm adding CSRF protection to my web site (implemented with Scala Play 2.8). It appears to be working from the browser. That is, I can display a protected page in a browser, change the value of the CSRF token using the browser's tools, submit, and I then get the error message generated by Play at line 131 in CSRFActions.scala.

The problem is how to set up a test to verify this in my test suite.

Here's my test:

class AsisCtrlUpdatePrefsSpec extends OatSpec with OneAppPerSuite {

  implicit override lazy val app: FakeApplication = FakeApplication(
    withGlobal = Some(oat.wapp.GlobalWApp))

  "Getting a preferences form should" - {

    "detect CSRF tampering" in new DefaultTestContext("myUser") {
      // Get a valid token; create a tampered version
      val token = CSRF.SignedTokenProvider.generateToken
      val tamperedToken = token.replace("1", "2").replace("3", "4")
      assert(token != tamperedToken)

      // Build a request object with info to get it authenticated,
      // valid data for the posted form, and the tampered csrfToken.
      // Include a session with the correct csrfToken
      implicit val postRequest = FakeRequest(POST,
        routes.AsisCtrl.updatePreferences("advisor").url)
        .withAuthenticator(this.identity.loginInfo)
        .withFormUrlEncodedBody("col1Prefixes" → "CS", "col2Prefixes" → "MATH",
          "acadRecordSort" → "Ascending", "notesSort" → "Ascending",
          "csrfToken" → tamperedToken)
        .withSession("csrfToken" → token)

      // This is what the application should do when a VALID
      // token is presented.  But with the invalid token, this 
      // should fail.  It doesn't.
      val result2 = this.asisCtrl.updatePreferences("advisor")(postRequest)
      assertResult(SEE_OTHER) {status(result2)}
      assertResult(routes.AsisCtrl.getPreferences.url) {
        await(result2).header.headers.get("Location").get
      }
    }
  }
}

OatSpec is a trait the contains frequently used stuff from ScalaTest + Play. GlobalWApp is the global settings object for this sub-module. withAuthenticator provides the needed information to authenticate the user "myUser"

I'm satisfied that it works correctly from the browser. I'd appreciate help in figuring out what's wrong with my test.

Thanks!

PS: Related question -- I have a form set up for the data input on this page. Haven't been able to figure out how to use it to avoid the hand-crafted Map in the call to withFormUrlEncodedBody. I think the call to withFormUrlEncodedBody is legitimate. The Play code appears to check headers first for a token with the correct name.

0

There are 0 best solutions below