CSRF trusted origin with nginx and dynamic IP?

Hello.

I’m new to django and I made a simple web site for SBC, which is used e.g. for setting IP address or watching logs.

Problem is, that when I want to do POST from the site, I get error
2023-11-13 15:50:42.772 WARNING 281473023595984 log.log_response Forbidden (Origin checking failed - http://172.20.100.141:8082 does not match any trusted origins.): /networksettings

The error is valid, because http://172.20.100.141:8082 is really not in CSRF_TRUSTED_ORIGINS (and adding this address solves the problem).
But problem is, that this IP is from DHCP, so it can be always different.
I saw some solution using socket.gethostbyname, but that also desn’t help me, because the IP address can change any time, even during django run.

I’m using Gunicorn and Nginx. I tried to change the Nginx setting, so the origin would be always e.g. 127.0.0.1 but without success.
This is my Nginx settings:

server {
    listen 8082;
    server_name _;

    # Another server, not django
    location / {
        proxy_pass http://127.0.0.1:8092;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    # redirect to django
    location ~ /dj/(.*) {
        proxy_pass http://127.0.0.1:8088/$1$is_args$args;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
}

Is there any solution for this situation?
Thank you for any help!

That could be a real problem if you can’t guarantee an ip address for your nginx server.

If I had to solve this, the first thing I’d do is find out the dhcp lease time. (If you can’t keep an address for the duration of the lease, your network has other problems.) Then, I’d investigate how to identify when a dhcp-assigned address change occurs. Or, at least, how to trigger a process when a lease is renewed. Then, if the address has changed, I’d run a script to stop the Django process, update the Django settings, and restart it. (It’s easier to do this if your settings file initializes these values from environment variables, or, if the code to set it is in the settings file itself. In that way, it kinda self-configures.)

But, aside from that, if you do anything on the server side to handle this situation such as modifying the incoming headers, you’re making yourself vulnerable to exactly the kind of attack that these headers are intended to prevent.

Yeah, the dynamic IP is problem, but imagine it as RaspberryPi which you can give to people. So you have no chance to do something with network (DHCP server etc.). They connect it to their network and should be able to use this site to e.g. set new static IP.

Yea, I get it. (I’ve also done Django deployments on portable SBCs.)

Keep in mind that the settings file is a Python module. You can add a function in that file to get the current set of ip addresses of the system and dynamically construct the CSRF_TRUSTED_ORIGINS list.

What you are not going to be able to do from within Django is detect if the address has changed. You’re going to want to do something outside Django itself to trigger a restart when that occurs.

Yeah, in theory I could set the CSRF_TRUSTED_ORIGINS when page load and local IPs are different.
Thanks for the reply!

Isn’t possible to react in the Django code to this error?
Something like callback when this error happen.

This gets caught by the middleware, before your code even sees the request. You can’t do this without negating what that protection does for the people using the system.

Logically, this needs to be done outside the Django environment itself. Django should be started (or restarted) knowing what its address is. If it can’t determine that at startup, you may as well remove that protection entirely.

OK, I understand.

Am I able to get origin IP/URL and PORT in request view?
For the IP request.META.get("HTTP_HOST") looks working. Or internal variable request._current_scheme_host.
But I don’t see the origin port in the request. Only port of the django server itself (META SERVER_PORT) :confused:

That doesn’t surprise me, and I’m having a great deal of difficulty with understanding what possible value it may have. In 99.995% of all cases, it’s going to be a transient port opened for that specific request. It’s not like you’re going to be able to send data back to that port from outside the wsgi service process handling the request. (Or, if you can, it’s probably not a wsgi server I’d want to use.)

Yeah, it makes sense, I just wanted to be sure :slight_smile:
Thanks for your time!