10 votes

Python Flask cross site HTTP POST - ne fonctionne pas pour certaines origines autorisées

J'essaie de faire en sorte que Flask gère correctement les scripts intersites. J'ai pris le snippet du décorateur crossdomain ici : http://flask.pocoo.org/snippets/56/

Dans le code ci-dessous, j'ai mis le snippet du décorateur et le serveur flask de base.

J'appelle le décorateur avec headers='Content-Type' parce que sinon j'obtenais "Request header field Content-Type is not allowed by Access-Control-Allow-Headers." dans le navigateur.

Voici donc ma question : Tel quel, le code ci-dessous fonctionne. Mais lorsque je veux restreindre à un serveur spécifique comme ceci :

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

Je reçois l'erreur du navigateur

"Origine http://myserver.com n'est pas autorisé par Access-Control-Allow-Origin".

Je n'arrive pas à le faire fonctionner pour autre chose que origin='*'.

Quelqu'un a-t-il une idée ?

Voici le code complet :

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)

Pour référence ma version de python est 2.7.2 La version de Flask est 0.7.2

2voto

Nate Points 1106

Je viens d'essayer le même code avec la version 2.7.3 de python et la version 0.8 de Flask.

Avec ces versions, il échoue avec

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

mais il fonctionne avec

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

Peut-être ne fonctionne-t-il pas avec Flask 0.7.2 (malgré ce qui est dit sur la page des extraits).


EDIT : Après avoir joué avec cela beaucoup plus (et mis à jour vers Flask 0.9) il semble que le vrai problème (ou encore un autre problème) pourrait être lié à avoir plusieurs origines autorisées dans une liste. En d'autres termes, en utilisant le code ci-dessus comme ceci :

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

ne fonctionne pas.

Pour résoudre ce problème, j'ai modifié le décorateur. Voir le code ici : http://chopapp.com/#351l7gc3

Ce code renvoie uniquement le domaine du site demandeur s'il est dans la liste. C'est un peu bizarre, mais au moins pour moi, le problème est résolu :)

1voto

Eric Leschinski Points 14289

Python fait tout son possible pour vous empêcher de vous exposer à des attaques de type cross site scripting.

Une solution consiste à céder, et à faire en sorte que vos requêtes touchent le même serveur que celui sur lequel le script de flask est exécuté. Récupérer du JSON à partir de serveurs éloignés définis dans des chaînes de caractères est une entreprise risquée de toute façon.

J'ai pu le résoudre en laissant le navigateur se maintenir sur le même serveur, comme ceci :

$('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;
});

Remarquez comment la méthode getJSON est passée un /_add_numbers . Cela indique au navigateur qu'il doit rester sur le même hôte et rechercher cette page. Le navigateur est alors heureux et sûr que nous restons sur le même hôte, et vous n'obtenez jamais l'erreur :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X