What is Cross Site Request Forgery?
Previously we discussed about
what is CSRF and what are the potential CSRF, objectives of CSRF and an example
for a possible CSRF attack. Also, I shared some information about the project I
implemented to demonstrate the Synchronized Token Pattern.
To read more please visit my
previous blog post from here:
https://kmbloggerz.blogspot.com/2018/10/preventing-cross-site-request-forgery.html
In this blog post, I will be
discussing about the second pattern of preventing CSRF attack, which is Double
Submit Cookie Pattern.
Double
Submit Cookie Pattern
This is defined as sending a random
value in both cookie and as a request parameter, with the server verifying of
the cookie value and request value are equal. When a user authenticates to a
site, the site should generate a cryptographically strong value and set it as a
cookie on the user’s browser separated from the session id.
The site does not need to save he
session id. Then the site should require every sensitive submission to include
this random value as a hidden form of value on the request parameter and also
as a cookie value.
Because of this, an attacker will
not be able to read and modify any data that sent from the server. This means
that while an attacker can send any value he/ she wants with a malicious CSRF
request, but the attacker will not be able to read and modify the data stored
in the cookie. Since the cookie value and the request parameter value must be
the same, therefore, the attacker will not be able to successfully submit a
form unless he is able to guess the random CSRF value.
How
can we implement Double Submit Cookie Pattern in an application?
you can access the whole project
in my GitHub from here:
https://github.com/KMKasunMadusanka/CSRF_JAVA_Double_Submit_Cookie_Pattern
Let’s see how we can implement
this project:
This project is done using Java servlet and JSP. In here for
demonstrating double submit cookie pattern. It uses 3 simple interfaces (login,
form and result page).
For this demo following code structure has been used.
For this demo following code structure has been used.
In order to handle the font-end of the login page it uses
JSP page and all the functionalities of that page will handle in the separate
servlet page.
First we will look
into the login page.
1. login.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<!--Style
Sheet-->
<link rel="stylesheet" href="./resources/login.css"/>
<!--Bootstrap-->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login</title>
</head>
<body id="LoginForm">
<div class="container">
<h1 class="form-heading">SLIIT Feedback Collection Page</h1>
<h5 class="form-heading_h5">Secured with double submit cookie pattern</h5>
<div class="login-form">
<div class="main-div">
<div class="panel">
<h2>System Login</h2>
<p>Please enter user name
and password</p>
</div>
<form id="Login" action="login" method="post">
<div class="form-group">
<input type="text" name="username" class="form-control" id="username" placeholder="User Name">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" id="password" placeholder="Password">
</div>
<div class="forgot">
</div>
<button type="submit" class="btn
btn-primary">Login</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
In the login.jsp it has two text and once your submit the
username and password values, relevant data will redirected to the login.java
(servlet page) as a post request.
Note :- Do not use GET request for these kinds of secure
data transformation operations. In the GET request, it shows all the values of
each and every variable in the form as URL parameters.
Then after, we will consider about logic which has
implemented in the login.java servlet page.
2. Login.java (servlet)
package servlet;
import Models.Token;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "login", urlPatterns = {"/login"})
public class login extends HttpServlet {
Token tkn = new Token();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username.equals("admin") && password.equals("admin")) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("JSESSIONID"))
{
// set session id
tkn.setSesssion(cookie.getValue());
}
}
}
//
set session cookie
Cookie sessionCookie = new Cookie("sessionID", tkn.getSesssion());
response.addCookie(sessionCookie);
//
set cookie to username
Cookie usernameCookie = new Cookie("username", username);
response.addCookie(usernameCookie);
//
create csrf token
tkn.setCsrf();
//
set session cookie
Cookie csrfCookie = new Cookie("csrf", tkn.getCsrf());
response.addCookie(csrfCookie);
response.sendRedirect("request.jsp");
} else {
response.sendRedirect("login.jsp");
}
}
}
In the beginning it has retrieve the corresponding values of
the ‘username’ and ‘password’ from the request object. Then it checks whether username and password
values are matched with system provided username and password.
Assume: - I have assume that username and password for this
system is admin, admin.
If the entered username and password is incorrect, then page
will again redirect to the login.jsp. Otherwise it will store the current
session id in the Token class and a
separate cookie. In addition to that, username stored in a separate cookie for
farther reference. Then after CSRF token
(random token) will be generated. Then after CSRF token store in a cookie. Once
all the process completed it will redirected to the welcome.jsp.
3. cookies
4. Token.java
package Models;
import java.util.Date;
public class Token {
private static String csrf;
private static String sesssion;
public String getCsrf() {
return csrf;
}
public String getSesssion() {
return sesssion;
}
public void setCsrf() {
Date date = new Date();
long timeinMiliSeconds = date.getTime();
Token.csrf = Long.toString(timeinMiliSeconds);
}
public void setSesssion(String sesssion) {
Token.sesssion = sesssion;
}
}
After successfully logged in to the system, it will redirect
to the feedback page (request.jsp)
5. Feedback page
6. Request.jsp
<%@page contentType="text/html"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<!--Style
Sheet-->
<link rel="stylesheet" href="./resources/welcome.css"/>
<!--Bootstrap
CSS Framework-->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<!--JQuery-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Book Request</title>
</head>
<body>
<body>
<div class="container contact-form">
<div class="contact-image">
<img src="https://cdn0.iconfinder.com/data/icons/social-messaging-ui-color-shapes/128/chat-circle-blue-512.png" alt="rocket_contact"/>
</div>
<form action="ValidateData_doubleSubmitCookie" method="post">
<h3>Drop Us a Message</h3>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<input type="text" name="txtName" class="form-control" placeholder="Your Name
*" value="" />
</div>
<div class="form-group">
<input type="text" name="txtEmail" class="form-control" placeholder="Your Email
*" value="" />
</div>
<div class="form-group">
<input type="text" name="txtPhone" class="form-control" placeholder="Your Phone
Number *" value="" />
</div>
<input type="hidden" name="csrf" id="hidden_input" value="">
<div class="form-group">
<input type="submit" name="btnSubmit" class="btnContact" value="Send Message" id="submit-btn"/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<textarea name="txtMsg" class="form-control" placeholder="Your Message
*" style="width: 100%;
height: 150px;"></textarea>
</div>
</div>
</div>
</form>
</div>
</body>
<script>
window.addEventListener('load', function() {
console.log('All assets are
loaded');
retrieveToken();
});
function retrieveToken() {
let cookieArray = document.cookie.split(';');
console.log(cookieArray);
for(let i = 0; i < cookieArray.length ; i++){
if(cookieArray[i].split('=')[0] === ' csrf'){
document.getElementById("hidden_input").value = cookieArray[i].split('=')[1];
}
}
}
</script>
</body>
</html>
Once request.jsp page loaded, CSRF id which has set to
cookie in the login page will be set as the value of hidden field in this page.
Once after this form submitted, feedback values will be
redirected to the ValidateData_doubleSubmitCookie.java (servlet) class. In that
class, it check the values of the hidden field which has set is exactly similar
to the CSRF id which has set in the cookie. If it is matched we can ensure that
this request is exactly the same response which has sent from the front end.
7. ValidateData_doubleSubmitCookie.java
(servlet)
package servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet(urlPatterns = {"/ValidateData_doubleSubmitCookie"})
public class ValidateData_doubleSubmitCookie extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String csrf_form = request.getParameter("csrf");
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("csrf")) {
HttpSession session = request.getSession();
// check the responce csrf is equal with server csrf
if (cookie.getValue().equals(csrf_form)) {
session.setAttribute("result_value",
"Your responce has been ensured with CSRF
Double submit cookie pattern!");
} else {
session.setAttribute("result_value",
"Your responce is recoded but not ensured
with CSRF");
}
}
}
response.sendRedirect("result.jsp");
}
}
}
If the CSRF id which has stored in the backend is matched
with CSRF id which has send from the front end it will store a status (‘Your
responce has been ensured with CSRF Double submit cookie pattern!’) in a
session variable, and show that in the result page (result.jsp)
8. Result page
Result.jsp
<%@page contentType="text/html"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<!--Style Sheet-->
<link rel="stylesheet"
href="./resources/result.css"/>
<!--Bootstrap CSS Framework-->
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<!--JQuery-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>Result</title>
</head>
<body>
<div class="title">
Thank You For The Feedback
</div>
<%
session.getAttribute("result_value");
%>
<div class="msg_cover">
<div> Your Feedback has been successfully
recoded! </div>
<div class="msg">
${result_value}</div>
<a type="button"
class="btn btn-info" href="http://localhost:8080/csrfDemo/">Home</a>
</div>
</body>
</html>





Comments
Post a Comment