Search code examples
pythonsecuritygoogle-app-enginewebapp2google-app-engine-python

Is it safe to do case insensitive comparison on AppEngine headers?


AppEngine makes some guarantees about specific headers in tasks. Specifically, a set of headers such as X-AppEngine-QueueName are not able to be set by a user.

These headers are set internally by App Engine: If an external user request attempts to set these headers, they are removed. Therefore, if your request handler finds any of these headers in a request, that task queue request is guaranteed to be valid.

It appears that both web frameworks provided by GAE (webapp and webapp2) have Request objects that inherit from webob.Request. Unfortunately, as far as I can tell, webob.Request only exposes the headers in a case-insensitive way. Does AppEngine guarantee that a user is unable to provide those request headers in a case-insensitive way?

In other words, can a user set a X-AppEngine-Queuename header on the request and have it passed through (at which point, webob would not be able to distinguish between that header and the real one provided by GAE)?

In the webapp2 framework, how do I guarantee that my request originated as a task and not as the result of a malicious user?


Solution

  • So I've done some experimentation and it appears that GAE does indeed sanitize the headers in a case-insensitive manner (as would be expected based on RFC 7230 3.2).

    For anyone interested in my testing methodology, I first added the following logging statements to my webapp2 request handler:

    logging.info('request headers: %s', self.request.headers.items())
    logging.info('request env: %s', list(self.request.headers.environ)
    

    I then navigated to my site in chrome with the development tools open. I found the request for the handler that I had modified in the "Network" tab. Right clicking this gives the option to copy the request as cURL so I did that and pasted it into a shell script. I then modified the copied request to remove all cookies (I don't want GAE identifying me as an app-admin) and to add a x-appengine-queuename: boom header. I submitted the curl request and took a look in my logs. There was no x-appengine-queuename header. I also added a x-appengine-mgilson: test header and that header was passed through.

    From the implementation side, the environment has all uppercase headers (e.g. self.request.headers.environ has all upper-case keys with a few exceptions that all start with wsgi. -- like wsgi.run_once). The headers are all prefixed by HTTP_ -- I'm guessing that this is part of some standard somewhere... webob strips the HTTP_ and title-cases the rest of the header name when it looks up a value in self.request.headers (presumably to comply with the previously mentioned RFC, and to provide a nicer human-readible version).