2

I asked a related question here. Basically, I have an API built with Flask, which I want to be able to POST to only from the same machine as is running the Flask app itself. All other POST requests should get a response of 401 Unauthorized.

I have become a little bit confused as to how to achieve that at this point. Elsewhere on SO (I can't recall where anymore) I read that comparing the REMOTE_ADDR header/field is the proper way to do this. Here is at least one example.

I wrote some code which I thought would do this in a view function:

if request.environ.get('REMOTE_ADDR', '127.0.0.1') != '127.0.0.1':
    abort(401)
post_data = request.form
DPC().store(post_data)
return jsonify(post_data), 201

However, after implementing these changes, I was still able to POST to the API from an external machine. A test I wrote for this functionality belies what might be an underlying problem: def test_external_post_fails(self):

    my_data = {
        ...
    }
    result = self.client.post('/daily_population_changes', 
                    data=my_data, 
                    environ_overrides={'REMOTE_ADDR': '127.0.0.2'})
    assert result.status_code == 401

The test succeeds. But the question is one of accuracy, as opposed to precision: am I testing the right thing here? Does a remote request actually have a (non-null) value for REMOTE_ADDR in the Werkzeug environment that is not equal to '127.0.0.1'?

I haven't yet been able to actually inspect values as I would like to for these request objects, save to see (on a production server) that POST whitelisting is not succeeding. Perhaps someone already has insight into this, or else I will have access to more machines in a bit.

Thanks!

Brian Peterson
  • 2,800
  • 6
  • 29
  • 36
  • 1
    Are you running Flask behind some sort of Proxy (for example, using Nginx forwarding to Flask)? If so, Nginx will make a NEW request that is probably using the loopback interface and Flask is seeing that request (thus the REMOTE_ADDR will be 127.0.0.1). See [This question](http://stackoverflow.com/questions/4773969/is-it-safe-to-trust-serverremote-addr) for more info. – Mark Hildreth Nov 01 '13 at 16:11
  • Yes, I am! Thanks for the tip, I'm looking into this. – Brian Peterson Nov 02 '13 at 01:13
  • So, basically, Nginx stores the user's IP in the `X-Forwarded-For` Header? Wow, that would have taken me awhile to figure out. – Brian Peterson Nov 02 '13 at 01:19
  • Yeah, I'm pretty sure this is exactly what is happening in my case. But actually, the header is dependent on where your nginx config saves the $remote_addr value. For me, it is in the X-Real-IP header. – Brian Peterson Nov 03 '13 at 06:51
  • You should write an answer for this. I think it's the correct one, and if so the answer is valuable, and should be written down for others in its own right. – Brian Peterson Nov 03 '13 at 06:53

1 Answers1

1

One thing which may be tripping you up is using request.environ.get('REMOTE_ADDR', '127.0.0.1'). The get() method of dicts will return the second value if the first key doesn't exist. So if there is no 'REMOTE_ADDR', for whatever reason, then it will automatically fill it in with '127.0.0.1'.

Try something like:

if request.environ.get('REMOTE_ADDR', False) == '127.0.0.1':

instead. Does that help?

Daniel
  • 1,410
  • 12
  • 17
  • That was my next trail to look into after I got the will to mess with this again. The thing is, I'm not really sure if `environ['REMOTE_ADDR']` is always set. In fact, I know it isn't sometimes (got an error accessing the dict directly, which is when I added the default). Since the name of the attribute seems to suggest it's a *remote* address, I was guessing that its not being set might mean it was simply local. – Brian Peterson Nov 02 '13 at 01:14
  • I figured out that the reason `environ['REMOTE_ADDR]` wasn't always set is simply that my reverse proxy server was setting it, but I wasn't using it when testing. I added a `environ_overrides={'REMOTE_ADDR': '127.0.0.1'}` parameter in my test, and simply provided a throwaway name `foo` to compare to `'127.0.0.1'`. But the server still accepts POST requests from my local machine. – Brian Peterson Nov 03 '13 at 06:35