XSRF or CSRF or sea-surf is web security vulnerability that takes advantage of website’s trust in user. In this attack, the user is forced into performing unwanted activities on the website where s/he is logged in.
For instance, consider a personalized website (like bank) where you as a user are logged in. During login, browser of your computer would typically store your login credentials with your other personalized information. Now, suppose you receive an advertisement (written with malicious intent) in an email, that has an image with a Javascript embedded. When you click on the advertisement, the javascript, taking advantage of you being authenticated on your bank website through cookie information, could deem you to transfer money form your account to the attacker’s account or can sneak into your personal details. Such attacks are called cross site request forgery (XSRF).
Tornado has the capability of preventing such attacks. Servers developed with Tornado can cookie every user with a token. When any Http PUT, POST or DELETE request (form submission) made to the server doesn’t match the token, it is deemed forged or malicious and is rejected by the Web server.
Let’s look at the sample implementation (below)
- Here, the server side has an unique application setting ‘xsrf_cookies’=True that suggests that Tornado will set _xsrf cookie for all user
- Also the POST request form includes a template block “{% module xsrf_form_html() %}” that generates the token while submitting the HTTP POST request.
This file contains 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
import tornado.ioloop | |
import tornado.web | |
class Page(tornado.web.RequestHandler): | |
def get(self): | |
self.render('xsrfform.html') | |
def post(self): | |
name = self.get_argument("name") | |
self.write('Helo ' + name) | |
application = tornado.web.Application([ | |
(r"/page", Page), | |
], | |
debug=True, | |
xsrf_cookies=True, | |
cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=" | |
) | |
if __name__ == "__main__": | |
application.listen(8888) | |
tornado.ioloop.IOLoop.instance().start() |
This file contains 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
<html> | |
<title> | |
Login | |
</title> | |
<body> | |
<form action="/page" method="post"> | |
{% module xsrf_form_html() %} | |
Username: <input type="text" name="name"/> | |
<input type="submit" name="login" value="Login"/> | |
</form> | |
</body> | |
</html> |
Once the client enters username on xsrfform.html, Tornado renders a message ‘Helo username’and _xsrf cookie is generated and stored in browser like below:
Also, the body of the POST request generated by xsrfform.html contains the_xsrf token as required by the server side.
ubuntu@ubuntu:~/tornado-2.2$ python tornadoxsrf.py _xsrf=636f8afbd29c4d72a4aa4a501f3b3436&name=Technobeans&login=Login
But what happens the server expects a token but doesn’t receive it? Well, server thinks the request is malicious and the request gets rejected.
- _xsrf.txt is the output on the command line where your server is running
- _xsrfbrowser.txt: Client receives on the browser
This file contains 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
WARNING:root:403 POST /page (127.0.0.1): '_xsrf' argument missing from POST | |
WARNING:root:403 POST /page (127.0.0.1) 1.49ms |
This file contains 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
Traceback (most recent call last): | |
File "/home/ubuntu/tornado-2.2/tornado/web.py", line 982, in _execute | |
self.check_xsrf_cookie() | |
File "/home/ubuntu/tornado-2.2/tornado/web.py", line 872, in check_xsrf_cookie | |
raise HTTPError(403, "'_xsrf' argument missing from POST") | |
HTTPError: HTTP 403: Forbidden ('_xsrf' argument missing from POST) |
Tornad_xsrf cookie generated by Tornado looks like: