Search code examples
pythonjqueryflaskcsrf

Flask Preventing Form Injection


How can python / flask block foreign form injections?

Consider the following mwe:

app.py

from flask import Flask, request, render template

app = Flask(__name__)

@app.route('/', methods=['GET','POST'])
def helloworld():
    if request.method == 'GET':
        return render_template('index.html') 
    if request.method == 'POST':
        print(request.form['info'])

        ## do something with the info, like write to a database

        return 'nothing'

if __name__ == '__main__':
    app.run(debug=True)

templates/index.html

<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type='text/javascript' src="{{ url_for('static', filename='js/fire.js') }}"></script>
</head>

<body>
<p>Hello world!</p>
</body>
</html>

static/js/fire.js

$(document).click(function() {

    // post data to flask

    $.post('/', {'info': 'test'});

    return false;

};

My questions are:

  1. Is injection possible from a foreign website? Follow-up: how could this be done? (e.g., perhaps via a form that posts to my website url?)
  2. If injection is possible, what can I do in the app.py script to block the injection?

Edit

Here is a very basic script that can be used to test injections against the above flask application. The accepted answer blocks this script:

<!DOCTYPE html>
<html>
<body>

<h2>Malicious Form Injection</h2>

<form action='http://127.0.0.1:5000/' method='post'>
  Input 1:<br>
  <input name="info" value="mal1"><br>
  <input type="submit" value="Submit">
</form>


</body>
</html>

Solution

  • app.py

    from flask import Flask, request, render template
    from flask_wtf.csrf import CSRFProtect
    
    app = Flask(__name__)
    
    CSRFProtect(app)
    
    app.config['SECRET_KEY'] = 'somethignrandom'
    
    @app.route('/', methods=['GET','POST'])
    def helloworld():
        if request.method == 'GET':
            return render_template('index.html') 
        if request.method == 'POST': # anything post will autocheck csrf
            print(request.form['info'])
    
            ## do something with the info, like write to a database
    
            return 'nothing'
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    There is no need to pass the secret key to the html template, as CSRFProtect will automatically pass the secret key.

    templates/index.html

    <html>
    <head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <meta name='csrf-token' content="{{ csrf_token() }}">
    <script type='text/javascript' src="{{ url_for('static', filename='js/fire.js') }}"></script>
    
    </head>
    
    <body>
    <p>Hello world!</p>
    </body>
    </html>
    

    script.js

    $(document).click(function() {
    
        // post data to flask
    
        $.post('/', {'info': 'test', '_csrf_token':$('meta[name="csrf-token"]').attr('content')});
    
        return false;
    
    };