To evaluate the source code of the application and find the cause of the vulnerability, we need to find the appropriate file where vulnerability exists. In our vulnerable application, the endpoint which is vulnerable to CSRF attack is /update_bio
POST
request. Let’s try to find the file in which the function for this POST
request is defined.
To interactively attach the shell inside docker, use the following command:
docker exec -it csrf /bin/bash
You may notice that your shell has changed and you are inside the container runtime at /app
directory:
root@9c7fa193e734:/app#
After searching the /app
directory, we encountered a file named app.py
whose truncated contents are as follows:
from flask import Flask, render_template, request, redirect, url_for, session
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(16) # Needed for session
@app.route('/update_bio', methods=['GET', 'POST'])
def update_bio():
if 'user' not in session:
return redirect(url_for('login'))
user = session['user']
if request.method == 'POST':
new_bio = request.form.get('bio')
if new_bio:
users[user]['bio'] = new_bio
return redirect(url_for('profile', username=user))
return render_template('update_bio.html', user=user, current_bio=users[user]['bio'])
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Bug
The above
flask
based application code accepts all thePOST
requests, but it does not verify whether the request originated from a trusted source.
To protect from these type of CSRF attacks a common approach is to use CSRF token mechanism.
Note
A CSRF token mechanism is required to ensure that each request is intentionally initiated by the user and not forged by a third-party website. This tokens are randomised for each user session request or form submissions to prevent attackers from predicting or reusing them in forged requests.
Now let’s add the CSRF token mechanism in our sample python application. flask
library natively does not provide CSRF protection mechanism, but an alternative flask-wtf
library provides this functionality over flask
. Let’s install the library using pip
pip install flask-wtf
Add CSRF protection to our app (in app.py
file)
from flask import Flask, render_template, request, redirect, url_for, session
from flask_wtf import CSRFProtect
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(16) # Needed for session
csrf = CSRFProtect(app) # Enables CSRF protection
Now, we need to edit the .html
files to incorporate the CSRF tokens in forms. The login.html
and update_bio.html
under templates
directory uses HTML forms. Add the following line to add CSRF token as a hidden field within forms.
<form method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<!-- other form fields -->
<input type="submit" value="Submit">
</form>
Once both of the .html
files are changed. Let’s try our malicious HTML file once again, and check whether it stills works or not?
Voila! We have successfully mitigated the CSRF vulnerability by adding the CSRF tokens in all the forms.