Selenium WebDriver. CSRF test

CSRF - Cross-site Request Forgery. The explanation how it works is out of scope of our post, there is a lot of information on the internet. The purpose of the post is to give you the idea how you can implement CSRF tests for your site using Selenium WebDriver in Java. The exact implementation depends on your project.

How to test CSRF

To test CSRF we should follow these steps

  • Log in to the site we need to test
  • Try to send a request (in our case a change password request) to the site from another site (or from a local html page)

CSRF test. CSRF attack from local html page

This is how our regular test (without CSRF attack) could look:

...
LoginPage loginPage = new LoginPage(driver);
MyAccountPage myAccountPage = loginPage.login("MyUserName", "MyPassword");
ChangePasswordPage changePasswordPage = myAccountPage.toChangePasswordPage();
changePasswordPage.changePassword("newPassword", "newPassword");

assertTrue(changePasswordPage.isSuccessMessageShown());

Now let’s try to implement a CSRF test - after logging in we should open the local html file that contains the same form as ChangePasswordPage has. That form sends attacker’s new password to the site.

<html>
<body>
    <form method="post" action="yoursite.com/change-password" id="form">
        <input type="password" name="newPassword" value="" id="password" />
        <input type="password" name="verifyPassword" value="" id="password" />
        <button type="submit" id="submit">Submit</button>
    </form>
</body>
</html>

We save that file (change-password.html) in the resources folder. When we logged in we have to open the file and submit the from with attacker’s new password. The test might look like this

...
LoginPage loginPage = new LoginPage(driver).load();
MyAccountPage myAccountPage = loginPage.login("MyUserName", "MyPassword");

loadLocalHtmlFile("change-password.html");

ChangePasswordPage attackerPage = new ChangePasswordPage(driver);
changePasswordPage.changePassword("newPassword", "newPassword");

assertFalse(changePasswordPage.isSuccessMessageShown());

// or
assertTrue(changePasswordPage.isForbiddenMessageShown())
...
private void loadLocalHtmlFile(String fileName) {
  String path = this.getClass().getResource("/" + fileName).getPath();
  driver.get("file://" + path);
}

We can also verify that the password wasn’t changed

CSRF test. CSRF attack - replacing innerHTML

Now we want to test CSRF using another approach. The difference is that instead of loading a local html file we go to another site (say www.google.com) and then just replace html of the page with the content of our file.

The test that simulate a SCRF attack would look like this

...
LoginPage loginPage = new LoginPage(driver).load();
MyAccountPage myAccountPage = loginPage.login("MyUserName", "MyPassword");

// Change the domain
getDriver().get("http://google.com");

replaceHTML(getFileAsString(page));

ChangePasswordPage attackerPage = new ChangePasswordPage(driver);
changePasswordPage.changePassword("newPassword", "newPassword");

assertFalse(changePasswordPage.isSuccessMessageShown());

// or
assertTrue(changePasswordPage.isForbiddenMessageShown())
...
private void replaceHTML(String html) throws IOException {
  WebElement e = getDriver().findElement(By.tagName("html"));
  ((JavascriptExecutor) getDriver()).executeScript("arguments[0].innerHTML='" 
            + StringEscapeUtils.escapeEcmaScript(html) + "'", e);
}

public String getFileAsString(SCRFPage page) throws IOException {
  InputStream input = this.getClass().getResourceAsStream("/csrf/" + page.url + ".html");
  return IOUtils.toString(input, StandardCharsets.UTF_8);
}

You may also find these posts interesting: