Preventing CSRF Using Double Submit Cookie Pattern
In previous blog post, what CSRF is and how to preventing CSRF using Synchronizer Token Pattern are discussed. This blog post will discuss how to use Double Submit Cookie Pattern to prevent from CSRF attacks.
In previous blog post, what CSRF is and how to preventing CSRF using Synchronizer Token Pattern are discussed. This blog post will discuss how to use Double Submit Cookie Pattern to prevent from CSRF attacks.
How Double Submit Cookie Pattern works?
When a user authenticates to a site, the site should generate a (cryptographically strong) pseudo-random value and set it as a cookie on the user’s machine separate from the session ID. The server does not have to save this value in any way, that's why this pattern is also called Stateless CSRF Defense.
When a user authenticates to a site, the site should generate a (cryptographically strong) pseudo-random value and set it as a cookie on the user’s machine separate from the session ID. The server does not have to save this value in any way, that's why this pattern is also called Stateless CSRF Defense.
Let's modify the previously built NodeJS application to demonstrate this
1. Handle /login POST Request.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* POST login. */ | |
router.post('/login', function (req, res, next) { | |
if (req.body.username == "admin" && req.body.password == "admin") { | |
res.cookie('csrf_token', generateCSRFToken(), { httpOnly: false }); | |
res.cookie('username', req.body.username); | |
res.redirect('home'); | |
} else { | |
res.render('../public/views/login', {message: 'Invalid username or password!'}); | |
} | |
}); |
Once the user is authenticated, a cookie containing csrf token will be create in the user's browser and it is set to httpOnly to make it readable by javascript.
2. Create submission form page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Message Form</title> | |
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css'> | |
<link rel="stylesheet" href="styles/main.css"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script> | |
</head> | |
<body> | |
<div class="login-form"> | |
<h1>Double Submit Cookie Pattern Demo</h1> | |
<form action="/message" method="post"> | |
<div class="form-group "> | |
<input type="text" class="form-control" name="message" placeholder="message" | |
required="required"> | |
<i class="fa fa-envelope"></i> | |
</div> | |
<button type="submit" class="submit-btn" id="submit-btn">Submit</button> | |
</form> | |
</div> | |
<script type="text/javascript"> | |
setCSRFToken() | |
function setCSRFToken() { | |
$("#submit-btn").before('<input type="hidden" name="csrf" value="'+getCookie('csrf_token')+'">'); | |
} | |
function getCookie(cname) { | |
var name = cname + "="; | |
var decodedCookie = decodeURIComponent(document.cookie); | |
var ca = decodedCookie.split(';'); | |
for(var i = 0; i <ca.length; i++) { | |
var c = ca[i]; | |
while (c.charAt(0) == ' ') { | |
c = c.substring(1); | |
} | |
if (c.indexOf(name) == 0) { | |
return c.substring(name.length, c.length); | |
} | |
} | |
return ""; | |
} | |
</script> | |
</body> | |
</html> |
A client-side script will retrieve csrf token value and inject it to a hidden field on the form loading to be submitted along with the form.
3. Handle /message POST request.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* POST message. */ | |
router.post('/message', function (req, res, next) { | |
var message = ''; | |
var className = ''; | |
if (/* validate req.cookies.sessionID && */ req.body.csrf == req.cookies.csrf_token) { | |
message = 'CSRF token is valid.'; | |
className = 'message-success'; | |
} else { | |
message = 'Invalid CSRF token!'; | |
className = 'message-fail'; | |
} | |
res.render('../public/views/response', {message: message, className: className}); | |
}); |
Upon receiving the request, the cookie's CSRF token and the request's CSRF token are compared to validate the request and the message submission will be completed if the provided values are correct.
GitHub URL: https://github.com/TharinduMPerera/DoubleSubmitCookiesPattern
______________________________________________________________
Tip: Understand the concept well. Then you will be able to develop better applications.
Comments
Post a Comment