What is Cross
Site Request Forgery?
Cross Site Request Forgery (CSRF) is an attack that
forces an end user to execute unwanted actions on a web application in which
they are currently authenticated. CSRF attacks specifically target state-
changing requests. With a little help of social engineering like sending a link
through an email or a chat, an attacker can trick a user of a web application
into executing actions of the attacker’s choosing. Depend on the user type that
the attacker is targeting the damage that can be caused by CSRF may differ.
CSRF is an attacker tricks a user into submitting a
malicious request which inherits the identity and privileges of the victim to
perform an undesired function on the victim’s behalf. Most of the sites and
browser requests automatically include any credentials associated with the site
such as the user’s session cookie, IP address, windows domain credentials and
so on.
When you click on a link a web page, your browser
sends a request to the web server. Such requests can broadly be categorized
into two types: GET and POST. A GET
request is simply a request for a page and a POST request is sent when you need
to send data to the sever.
Objectives
of CSRF attacks
An application that allows a user to send or update
data is a potential target for an attacker. The following is a list of
potential uses for CSRF:
- Transfer money from one bank account to another bank account.
- Use a content management system to add or delete content from a website
- Change a user’s password
- Add items to a use’s shopping cart list
A CSRF
attack example
Imagine a website allows their uses to login using
their emails and the site contains a page called “my account” that will store
all the information of the user and often allows a user to change to his/her
email address. This email address change could either be made as a POST request
or as a GET request. It is possible for an attacker to forge this request once
he or she knows the structure of it, and discovering the structure is simple,
especially if users can register their own accounts.
According to the example below, the data to be changed
is contained in a parameter called “EmailAdress”. If the user can be tricked into visiting a website which is under
the attacker’s control, the following code can be used to change the email
address stored as a login credential on that particular site:
<html>
<body>
<H1> Hello</'H1>
<img src="http://examplesite.cpm/MyAccount?EmailAddress=emailaddress@site.com" width ="1" height="1"/>
</body>
</html>
<body>
<H1> Hello</'H1>
<img src="http://examplesite.cpm/MyAccount?EmailAddress=emailaddress@site.com" width ="1" height="1"/>
</body>
</html>
The page can
be presented as anything or either it could be a blank page or else it could be
a replica of the site that is under the attack. All it needs is the above code
which displays the image and the image does not need to exist. As soon as the
user’s browser starts to load the page the code will get executed and
automatically submit the request to change the user’s email address. As long as
the user is logged into the site this scenario will proceed as the victim has
clicked the link.
Even if the
website only allows updates via POST, it is possible to change the email
address in the same way and it requires some different code:
<html>
<body>
<form name="CSRF",
method="POST",
action="http://examplesite.com/MyAccount?EmailAddress=emailaddress@site.com">
<input
type='hidden' name='EmailAddress'
value='http://examplesite.cpm/MyAccount?EmailAddress=emailaddress@site.com'>
</form>
<script>
document.CSRF.submt()</script>
</body>
</html>
In both
cases, once this is submitted, the email address is automatically changed. Then
it is simple as using the built-in password-rest facility that most websites
have. If this sends the password directly to the registered email address, the
password will them be mailed to the attacker and the user account is compromised.
There are 2
types of patterns that systems can adapt in order to prevent CSRF on any
website.
In this blog
we will look at how these two types of patterns prevent CSRF and how it can be
implemented in a Java application.
Synchronizer Token Pattern
Synchronizer
Token Pattern is a very simple concept to mitigate the risk of being attacked
through CSRF. It is a technique where a token, secret and unique value for each
request is embedded by the web application in all HTML forms and verifies on
the server side.
When a
request is made to the server to change data, the server code will read and
validate the token’s origin. If the server cannot read the token, it is likely
that the token was not issued by the server and the request should not be
processed.
The token
could be generated using any method which ensures the uniqueness (either by
using hash chain of random seed) and because of this the attacker will not be
able to place a token in their requests to authenticate them.
Let’s see how
we can implement this. I have included the details of the project that I
implemented to demonstrate. You can
access the project through my GitHub account :
This
project is done using Java servlet and JSP. In here for demonstrating
synchronize token 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 texts 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 {
// get username
and password form request
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. That CSRF value
and corresponding session id will be stored that in HashMap which is available
in the Token class. Once all the
process completed it will redirected to the welcome.jsp.
Note:- HashMap has used for the simplicity
of this blog. Otherwise it need to use
database in order to store the session id and corresponding CSRF id.
3. Token.java
package Models;
import java.util.Date;
import java.util.HashMap;
public class Token {
private static String csrf;
private static String sesssion;
private static HashMap<String,String> sessionMap = new HashMap<String, String>();
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;
}
public HashMap<String, String> getSessionMap() {
return sessionMap;
}
public void setSessionMap() {
Token.sessionMap.put(this.sesssion, this.csrf);
}
}
After successfully logged in to the system,
it will redirect to the feedback page (welcome.jsp)
4. Feedback page
5. Welcome.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>Message</title>
</head>
<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="message"/>
</div>
<form action="ValidateData_syncronizeToken" 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>
<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() {
$.ajax({
type: "GET",
url: "http://localhost:8080/csrfDemo/CsrfIssuer",
contentType: "text/json",
success: function (data) {
$("#submit-btn").before('<input type="hidden"
name="csrf" value="' + data + '">');
},
error: function (err) {
console.log(err);
}
});
}
</script>
</body>
</html>
Once welcome.jsp page loaded, it will call automatically call
AJAX get request for CsrfIsssuer.java class and it will return the CSRF id
relevant to current session. Once the CSRF id has received, that value set to
the hidden filed in the form.
6. AJAX
call
$.ajax({
type:
"GET",
url:
"http://localhost:8080/csrfDemo/CsrfIssuer",
contentType:
"text/json",
success:
function (data) {
$("#submit-btn").before('<input type="hidden" name="csrf" value="' + data +
'">');
},
error:
function (err) {
console.log(err);
}
});
Once after this form submitted, feedback
values will be redirected to the ValidateData_syncronizeToken.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 is available for current session id. If it
is matched we can ensure that this request is exactly the same response which
has sent from the front end.
6. ValidateData_syncronizeToken.java
(servlet)
package servlet;
import Models.Token;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet(name = "ValidateData_syncronizeToken", urlPatterns =
{"/ValidateData_syncronizeToken"})
public class ValidateData_syncronizeToken extends HttpServlet {
Token tkn = new Token();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//get current
session id
String currentSID = tkn.getSesssion();
HttpSession session = request.getSession();
tkn.getSessionMap().entrySet().forEach((m) -> {
if (m.getKey().equals(currentSID)) {
if (request.getParameter("csrf").equals(m.getValue())) {
session.setAttribute("result_value", "Your
responce has been ensured with CSRF sysncronize token pattern!");
}
else {
session.setAttribute("result_value", "Your responce
is recoded but not ensured with CSRF");
}
}
System.out.println(m.getKey() + " - " + m.getValue());
});
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 response has been ensured with CSRF synchronize token pattern!’)
in a session variable, and show that in the result page (result.jsp)
7.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