Python Flask cross site HTTP POST - doesn't work for specific allowed origins

左心房为你撑大大i 提交于 2019-12-08 15:42:52

问题


I'm trying to get Flask to handle cross-site scripting properly. I've taken the crossdomain decorator snippet from here: http://flask.pocoo.org/snippets/56/

In the code below, I've put the decorator snippet and the basic flask server.

I'm calling the decorator with headers='Content-Type' because otherwise I was getting "Request header field Content-Type is not allowed by Access-Control-Allow-Headers." in the browser.

So here is my question: As-is, the code below works. But when I want to restrict to only a specific server like so:

@crossdomain(origin='myserver.com', headers='Content-Type')

I get the browser error

"Origin http://myserver.com is not allowed by Access-Control-Allow-Origin."

I can't get it working for anything other than origin='*'.

Does anybody have any ideas?

Here is the complete code:

from datetime import timedelta
from flask import make_response, request, current_app, Flask, jsonify
from functools import update_wrapper

def crossdomain(origin=None, methods=None, headers=None,
            max_age=21600, attach_to_all=True,
            automatic_options=True):
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
            resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator

app = Flask(__name__)

@app.route('/my_service', methods=['POST', 'OPTIONS'])
@crossdomain(origin='*', headers='Content-Type')
def my_service():
    return jsonify(foo='cross domain ftw')

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

For reference my python version is 2.7.2 Flask version is 0.7.2


回答1:


I just tried the same code with python version 2.7.3 and Flask version 0.8.

With these versions, it fails with

@crossdomain(origin='myserver.com', headers='Content-Type')

but it works with

@crossdomain(origin='http://myserver.com', headers='Content-Type')

Perhaps it just doesn't work with Flask 0.7.2? (despite what it says on the snippet page).


EDIT: After playing with this a lot more (and upgrading to Flask 0.9) it seems that the real problem (or yet another problem) might be related to having multiple allowed origins in a list. In other words, using the above code like this:

@crossdomain(origin=['http://myserver.com', 'http://myserver2.com'], headers='Content-Type')

doesn't work.

To fix this problem I tweaked the decorator. See code here: http://chopapp.com/#351l7gc3

This code returns only the domain of the requesting site if it is in the list. Kinda quirky, but at least for me, problem solved :)




回答2:


Python is trying hard to prevent you from exposing yourself to cross site scripting attacks.

One fix is by giving in, and having your requests hit the same server the flask script is running on. Fetching JSON from far away servers defined in strings is risky business anyway.

I was able to fix it by letting the browser keep itself on the same server, like this:

$('a#calculate').bind('click', function() {
  $.getJSON('/_add_numbers', { 
    a: $('input[name="a"]').val(),
    b: $('input[name="b"]').val()
  }, function(data) {
    $("#result").text(data.request);
  });
  return false;
});

Notice how getJSON method is passed a /_add_numbers. That communicates to the browser to stay on the same host and look for that page. Then the browser is happy and secure we are staying on the same host, and you never get the error:

Origin http://myserver.com is not allowed by Access-Control-Allow-Origin


来源:https://stackoverflow.com/questions/16241588/python-flask-cross-site-http-post-doesnt-work-for-specific-allowed-origins

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!