Skip to content

πŸ” CSRF Tutorial – Flask Lab + WebGoat + Docker

πŸ“Œ What is CSRF?

CSRF (Cross-Site Request Forgery) tricks a user’s browser into performing unwanted actions on a web app where they're authenticated.

🧠 Example:

You're logged into your bank β†’ a malicious site sends a hidden request to transfer money β†’ browser sends the request with your session.


⚠️ Real-World Scenario

Victim logs into:

https://yourbank.com

Attacker makes victim visit:

<img src="https://yourbank.com/transfer?to=hacker&amount=1000">

Browser sends this request with victim’s cookies/session, completing the transaction silently.


πŸ§ͺ 1. Build Your Own CSRF Vulnerable Flask App

πŸ“ Project Structure:

csrf-flask-app/
β”œβ”€β”€ app.py
β”œβ”€β”€ Dockerfile
└── requirements.txt

πŸ“„ app.py

from flask import Flask, request, render_template_string, redirect, make_response

app = Flask(__name__)
user_balance = {"yuva": 1000}

@app.route("/", methods=["GET", "POST"])
def home():
    msg = ""
    if request.method == "POST":
        to = request.form.get("to")
        amount = int(request.form.get("amount"))
        user_balance["yuva"] -= amount
        msg = f"πŸ’Έ Transferred {amount} to {to}. Remaining: {user_balance['yuva']}"

    html = '''
    <h2>Welcome Yuva</h2>
    <p>Balance: {{ balance }}</p>
    <form method="POST" action="/">
        <input type="text" name="to" placeholder="Send to">
        <input type="number" name="amount" value="100">
        <button type="submit">Send</button>
    </form>
    <p style="color:green">{{ msg }}</p>
    '''
    return render_template_string(html, balance=user_balance["yuva"], msg=msg)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

πŸ“„ requirements.txt

flask

πŸ“„ Dockerfile

FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

▢️ Build & Run

docker build -t csrf-flask .
docker run -d -p 5000:5000 --name csrf-lab csrf-flask

Visit β†’ http://localhost:5000


πŸ§ͺ Simulate CSRF Attack

Create an attacker.html file with this code:

<html>
  <body>
    <h1>😈 Hacked</h1>
    <form action="http://localhost:5000/" method="POST" style="display:none">
      <input name="to" value="hacker">
      <input name="amount" value="500">
      <input type="submit">
    </form>
    <script>document.forms[0].submit();</script>
  </body>
</html>

Open attacker.html while you're logged into localhost:5000.
Boom πŸ’₯ β€” money sent without confirmation.


πŸ›‘οΈ 2. Preventing CSRF

βœ… Add CSRF Tokens

Use Flask-WTF for token-based protection:

pip install flask-wtf

Update app.py:

from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, SubmitField
from wtforms.validators import InputRequired
from flask import Flask, render_template_string
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.secret_key = "secret"
csrf = CSRFProtect(app)

class TransferForm(FlaskForm):
    to = StringField("To", validators=[InputRequired()])
    amount = IntegerField("Amount", validators=[InputRequired()])
    submit = SubmitField("Send")

Use {{ form.csrf_token }} in the HTML to embed a hidden token input. Now CSRF won't work.


πŸ§ͺ 3. Practice CSRF in WebGoat

🐳 WebGoat + WebWolf Docker

docker run -d -p 8080:8080 -p 9090:9090 --name webgoat-csrf webgoat/webgoat-8.2

Access lessons:

πŸ‘‰ Go to A1 - Injection β†’ CSRF β†’ Practice real-world examples with step-by-step guidance.


🧠 Summary

Concept Details
CSRF Trick user’s browser into sending unwanted requests
Fix 1 CSRF tokens in forms
Fix 2 SameSite cookies (Lax or Strict)
Fix 3 Check Referer / Origin headers
Fix 4 Use secure frameworks like Flask-WTF, Django, etc.