Flask : CSRF verification failed

2k views Asked by At

I am sending a POST request from an iOS client

-(void)loadFavorite:(NSArray*)favorites{

    //data and url preparation

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"https://example.com" forHTTPHeaderField: @"Referer"];

    [request setValue:[NSString stringWithFormat:@"%tu", [requestData length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody: requestData];

    if ([Tools isNetWorkConnectionAvailable]) {
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
            //response handle
    }
}

Here is the response :

<div id="summary">
  <h1>Forbidden <span>(403)</span></h1>
  <p>CSRF verification failed. Request aborted.</p>
</div>

I'm using Flask framework and pythonanywhere for hosting.

It works fine when I reload the python script but after few hours/days the CSRF verification failed error reappear.

Even if I try to disable the CSRF verification in my app.py with :

app.config['WTF_CSRF_CHECK_DEFAULT'] = False

App.py script :

//some import error handlers ...

app = Flask(__name__)
app.config['WTF_CSRF_CHECK_DEFAULT'] = False

@app.route('/api/favorites', methods=['POST'])
def get_favorites_beaches():
    if not request.json or not 'favorite' in request.json:
        abort(400)
    //data process

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

How can I implement the CSRF verification correctly or how to disable it ?

2

There are 2 answers

2
conrad On

you have a line in your code that is [request setValue:@"https://example.com" forHTTPHeaderField: @"Referer"];

did you not set it to the correct url? A wrong referer is one way you would get a cross site error.

Conrad

0
Giles Thomas On

PythonAnywhere developer here, reposting what we just put on our forums. This turned out to be a fairly obscure problem on our hosting platform, and we've just pushed a system patch that fixes it.

Here's what it was: if a web app was shut down for some reason (system reboot, certain kinds of glitch, excessive resource usage, maybe hibernation) then only a GET request would wake it up. POST requests, in particular, would be rejected with a CSRF error (generated by our code that's meant to start up the web app), and the app wouldn't be woken up. So if your app is one that processes mostly POST requests, you'd see this problem. This definitely seems to fit the issue as you describe it.

Our new code wakes up the app when a POST is received. One slight issue remains -- the first POST request that wakes it up will receive a "503 Service Unavailable" response with the "retry-after" header set to "5". If you handle this and do the retry, then the next request will work. We believe that browsers do that automatically, but unfortunately the requests library doesn't by default.