Python Flask and Graphene: Incorrect request causes security issue









up vote
0
down vote

favorite
1












I'm working on a simple messaging system where two different users can send messages to each other. Right now each user polls the server requesting the newest messages using graphQL.



The issue is that when one user, user A, sends a ton of messages (graphQL mutation queries) while the other user, user B, is polling, the messages sometimes appear to have been sent by user B.



I looked deeper and found that, in the cases where the messages were misattributed, the mutation query was hit with the incorrect request (as populated in the context), and the current_user from flask_login is also wrong. The current_user seems to depend on the request. The request was one of user B's polling requests (which are not mutations.)



I traced it back a bit and found the point where the context is being populated (https://github.com/graphql-python/flask-graphql/blob/master/flask_graphql/graphqlview.py#L42) but it was difficult to figure out why it was getting the incorrect request from Flask.



My best guess is that there's something going on with Flask's request context stack, but it's tough to track down.



I should mention that authentication is done using an Authorization header and that I tried to find a case where a user was being mis-authenticated but couldn't.



This only seems to happen when I'm running flask directly using FLASK_ENV=production flask run and seems to disappear when I run it using gunicorn. Even so, it's concerning that the authentication could be so fragile.



Has anyone seen this issue before? Is there something in the Flask documentation explaining this issue, why it happens, and more importantly, why it won't happen when I use gunicorn?



Relevant code versions:



  • Flask 1.0.2

  • Flask-GraphQL 2.0.0

  • Flask-Login 0.4.1

  • Flask-SQLAlchemy 2.3.2

  • graphene 2.1.3

  • graphene-sqlalchemy 2.1.0

  • graphql-core 2.1

  • SQLAlchemy 1.2.12

Example code:



import graphene as gr
from graphql import GraphQLError
from flask_login import current_user

from ...models import Message
from .shared import MessageNode

class CreateMessage(gr.Mutation):
""" Send a message to a channel account """

message = gr.Field(MessageNode)

class Arguments:
body = gr.JSONString(required=True)

@login_required
def mutate(self, info, body):
# Make sure that the body is populated with something
if not (body.get("text") and not body.get("image")) and not (
not body.get("text") and body.get("image")
):
raise GraphQLError("Body must contain either text or an image")

new_message = Message(
body=body,
sender_account=current_user, # CURRENT USER IS INCORRECT HERE
)
db.session.add(new_message)
db.session.commit()
return CreateMessage(message=new_message)









share|improve this question



























    up vote
    0
    down vote

    favorite
    1












    I'm working on a simple messaging system where two different users can send messages to each other. Right now each user polls the server requesting the newest messages using graphQL.



    The issue is that when one user, user A, sends a ton of messages (graphQL mutation queries) while the other user, user B, is polling, the messages sometimes appear to have been sent by user B.



    I looked deeper and found that, in the cases where the messages were misattributed, the mutation query was hit with the incorrect request (as populated in the context), and the current_user from flask_login is also wrong. The current_user seems to depend on the request. The request was one of user B's polling requests (which are not mutations.)



    I traced it back a bit and found the point where the context is being populated (https://github.com/graphql-python/flask-graphql/blob/master/flask_graphql/graphqlview.py#L42) but it was difficult to figure out why it was getting the incorrect request from Flask.



    My best guess is that there's something going on with Flask's request context stack, but it's tough to track down.



    I should mention that authentication is done using an Authorization header and that I tried to find a case where a user was being mis-authenticated but couldn't.



    This only seems to happen when I'm running flask directly using FLASK_ENV=production flask run and seems to disappear when I run it using gunicorn. Even so, it's concerning that the authentication could be so fragile.



    Has anyone seen this issue before? Is there something in the Flask documentation explaining this issue, why it happens, and more importantly, why it won't happen when I use gunicorn?



    Relevant code versions:



    • Flask 1.0.2

    • Flask-GraphQL 2.0.0

    • Flask-Login 0.4.1

    • Flask-SQLAlchemy 2.3.2

    • graphene 2.1.3

    • graphene-sqlalchemy 2.1.0

    • graphql-core 2.1

    • SQLAlchemy 1.2.12

    Example code:



    import graphene as gr
    from graphql import GraphQLError
    from flask_login import current_user

    from ...models import Message
    from .shared import MessageNode

    class CreateMessage(gr.Mutation):
    """ Send a message to a channel account """

    message = gr.Field(MessageNode)

    class Arguments:
    body = gr.JSONString(required=True)

    @login_required
    def mutate(self, info, body):
    # Make sure that the body is populated with something
    if not (body.get("text") and not body.get("image")) and not (
    not body.get("text") and body.get("image")
    ):
    raise GraphQLError("Body must contain either text or an image")

    new_message = Message(
    body=body,
    sender_account=current_user, # CURRENT USER IS INCORRECT HERE
    )
    db.session.add(new_message)
    db.session.commit()
    return CreateMessage(message=new_message)









    share|improve this question

























      up vote
      0
      down vote

      favorite
      1









      up vote
      0
      down vote

      favorite
      1






      1





      I'm working on a simple messaging system where two different users can send messages to each other. Right now each user polls the server requesting the newest messages using graphQL.



      The issue is that when one user, user A, sends a ton of messages (graphQL mutation queries) while the other user, user B, is polling, the messages sometimes appear to have been sent by user B.



      I looked deeper and found that, in the cases where the messages were misattributed, the mutation query was hit with the incorrect request (as populated in the context), and the current_user from flask_login is also wrong. The current_user seems to depend on the request. The request was one of user B's polling requests (which are not mutations.)



      I traced it back a bit and found the point where the context is being populated (https://github.com/graphql-python/flask-graphql/blob/master/flask_graphql/graphqlview.py#L42) but it was difficult to figure out why it was getting the incorrect request from Flask.



      My best guess is that there's something going on with Flask's request context stack, but it's tough to track down.



      I should mention that authentication is done using an Authorization header and that I tried to find a case where a user was being mis-authenticated but couldn't.



      This only seems to happen when I'm running flask directly using FLASK_ENV=production flask run and seems to disappear when I run it using gunicorn. Even so, it's concerning that the authentication could be so fragile.



      Has anyone seen this issue before? Is there something in the Flask documentation explaining this issue, why it happens, and more importantly, why it won't happen when I use gunicorn?



      Relevant code versions:



      • Flask 1.0.2

      • Flask-GraphQL 2.0.0

      • Flask-Login 0.4.1

      • Flask-SQLAlchemy 2.3.2

      • graphene 2.1.3

      • graphene-sqlalchemy 2.1.0

      • graphql-core 2.1

      • SQLAlchemy 1.2.12

      Example code:



      import graphene as gr
      from graphql import GraphQLError
      from flask_login import current_user

      from ...models import Message
      from .shared import MessageNode

      class CreateMessage(gr.Mutation):
      """ Send a message to a channel account """

      message = gr.Field(MessageNode)

      class Arguments:
      body = gr.JSONString(required=True)

      @login_required
      def mutate(self, info, body):
      # Make sure that the body is populated with something
      if not (body.get("text") and not body.get("image")) and not (
      not body.get("text") and body.get("image")
      ):
      raise GraphQLError("Body must contain either text or an image")

      new_message = Message(
      body=body,
      sender_account=current_user, # CURRENT USER IS INCORRECT HERE
      )
      db.session.add(new_message)
      db.session.commit()
      return CreateMessage(message=new_message)









      share|improve this question















      I'm working on a simple messaging system where two different users can send messages to each other. Right now each user polls the server requesting the newest messages using graphQL.



      The issue is that when one user, user A, sends a ton of messages (graphQL mutation queries) while the other user, user B, is polling, the messages sometimes appear to have been sent by user B.



      I looked deeper and found that, in the cases where the messages were misattributed, the mutation query was hit with the incorrect request (as populated in the context), and the current_user from flask_login is also wrong. The current_user seems to depend on the request. The request was one of user B's polling requests (which are not mutations.)



      I traced it back a bit and found the point where the context is being populated (https://github.com/graphql-python/flask-graphql/blob/master/flask_graphql/graphqlview.py#L42) but it was difficult to figure out why it was getting the incorrect request from Flask.



      My best guess is that there's something going on with Flask's request context stack, but it's tough to track down.



      I should mention that authentication is done using an Authorization header and that I tried to find a case where a user was being mis-authenticated but couldn't.



      This only seems to happen when I'm running flask directly using FLASK_ENV=production flask run and seems to disappear when I run it using gunicorn. Even so, it's concerning that the authentication could be so fragile.



      Has anyone seen this issue before? Is there something in the Flask documentation explaining this issue, why it happens, and more importantly, why it won't happen when I use gunicorn?



      Relevant code versions:



      • Flask 1.0.2

      • Flask-GraphQL 2.0.0

      • Flask-Login 0.4.1

      • Flask-SQLAlchemy 2.3.2

      • graphene 2.1.3

      • graphene-sqlalchemy 2.1.0

      • graphql-core 2.1

      • SQLAlchemy 1.2.12

      Example code:



      import graphene as gr
      from graphql import GraphQLError
      from flask_login import current_user

      from ...models import Message
      from .shared import MessageNode

      class CreateMessage(gr.Mutation):
      """ Send a message to a channel account """

      message = gr.Field(MessageNode)

      class Arguments:
      body = gr.JSONString(required=True)

      @login_required
      def mutate(self, info, body):
      # Make sure that the body is populated with something
      if not (body.get("text") and not body.get("image")) and not (
      not body.get("text") and body.get("image")
      ):
      raise GraphQLError("Body must contain either text or an image")

      new_message = Message(
      body=body,
      sender_account=current_user, # CURRENT USER IS INCORRECT HERE
      )
      db.session.add(new_message)
      db.session.commit()
      return CreateMessage(message=new_message)






      flask graphql flask-login graphene-python flask-graphql






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 12 at 15:36

























      asked Nov 9 at 21:08









      maxlang

      13




      13



























          active

          oldest

          votes











          Your Answer






          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "1"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );













           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53233291%2fpython-flask-and-graphene-incorrect-request-causes-security-issue%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes















           

          draft saved


          draft discarded















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53233291%2fpython-flask-and-graphene-incorrect-request-causes-security-issue%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

          Syphilis

          Darth Vader #20