Getting client remote port number from django

Hi, I am writing an app that needs to send a remote port to an API after a user goes to a Django-coded page, the reason for doing so is for security measures.

(NOTE: remote port is different from server port where server port is port 80 or so but turns out a client (remote) can send data through any port, it is up to the local server to assign it to port 80 so they are two different things as I understand.)

According to https://stackoverflow.com/questions/66892747/how-do-i-get-the-client-remote-port-number-in-a-django

There is one way to do it which, I assume, is to look at the socket

sock = request._stream.stream.stream.raw._sock
client_ip, port = sock.getpeername()

However, this method is not reliable because occasionally it raises io.BytesIO error.

AttributeError: '_io.BytesIO' object has no attribute 'stream'

I have no idea why but I was wondering is there any other ways I could obtain this information server side?

Or is it possible to obtain this value from vanilla javascript on client side? I see people do it with node.js but I don’t use it sadly.

Thanks

This reads to me like an X-Y Problem.

I think you need to more closely understand what exactly you’re trying to accomplish by trying to do this.

Any attempt to consider this a “security measure” is likely due to someone’s misunderstanding of the TCP protocols and what ports actually mean.

When the sender opens a port to initiate a transmission, that port is usually assigned at random by the sender’s operating system. This has absolutely nothing to do with the port that the receiver (“server”) is listening to. Its only significance is that it uses the senders port to know where to send the response.

The sender is only going to receive the response to that request from the originating server. That means you cannot pass that port to a different system and have it open a transmission to the originating system on that port.

Not only is that originating port assigned at random, it’s also likely to be changed between requests. You cannot guarantee that any two requests from the same system are going to originate from the same port.

Addendum: When you factor in routers, bridges, NAT boxes, firewalls, proxies, load balancers, VPNs, etc; there’s a very high probability that the “originators port” as seen by the server is not the port opened by the sender to initiate the transmission.

Hi Ken, thanks for your detailed response. In fact it is my first time knowing the client remote port (originating device) can be anything.

The API is actually a UK government API that requires sending client remote port to them, see point 8 here

When you say the connection goes through a few layer until it reaches the originating server, i think point 13 is exactly for that.

Is there anyway to achieve that in django?

Thank.

Ok, so this is an audit issue and not a security issue.

I’ve read through those requirements. Professional courtesy prevents me from expressing a detailed opinion of what I’ve read - “silly” is about the nicest adjective I can find. Items 6, 8, 9, and 12 are unverifiable and/or unretrieveable from the server.

They can’t care what values you provide - there’s no way for them to validate it.

See Request and response objects | Django documentation | Django for getting a port number. You’ll have no idea whether or not it’s the actual port number opened by the originating device, but it’s the only information you’ll have.

Point 13 may cover what you have in your infrastructure, but it cannot address what the end-user’s infrastructure may look like.

Thanks, Ken, indeed they probably cannot validate it so it is solely on the developer’s end to make sure it is correct. I am going to read the link you provided! Thanks again.

I think request.get_port() method is still server port not originating device’s port (remote port)…

Yep, you’re right, I was reading that backwards.

Try looking at request.META.REMOTE_PORT. It’s not defined as being required, but both Werkzeug and nginx do pass it as part of the wsgi call.

1 Like

Sorry, that should be request.META[‘REMOTE_PORT’]

1 Like