Disclaimer: This conversation for now is completely theoretical in nature and may be incorrect. Take it with a pinch of salt until I am able to prove whether it works or not.
In this article a VPN detection method is suggested based on some reasonable assumptions.
We have some service we provide to users and some users use VPNs to abuse the service. We control both the code on the server and the code on the client.
Currently, blocks are implemented via IP-based bans, using some netmask to either ban a single IP or a range. With the use of VPNs, such blocking processes are almost completely ineffective.
The question we ask ourselves is: Can we detect the use of a VPN reliably?
Firstly, we make some basic assumptions about the problem space:
After some reason online, the general consensus is that it apparently cannot be done:
Unfortunately, there’s is no proper technical way to get the information you want. You might invent some tests, but those will have a very low correlation with the reality. So either you’ll not catch those you want, or you’ll have a larger number of false positives. Neither can be considered to make sense.
Generating any kind of traffic backwards from an Internet server in response to an incoming client (a port scan, or even a simple ping) is generally frowned upon. Or, in the case of a port scan, it may be even worse for you, eg when the client lives behind a central corporate firewall, the worst of which is when the client comes from behind the central government network firewall pool…
As you can see, there are some restrictions on what kinds of solutions we can even consider. This includes:
The one thing we do have on our side is time. For this particular application, it is okay to make these checks as the person begins to use the service. We can reasonably wait at least 10 seconds to get the result of the detection method.
In the setup, we consider the server, local network and client. The local network represents the client machine, a router, a local network - whichever localized networking hardware we hit first representing user’s IP. The reason for considering this will become apparent later.
In the no-VPN scenario, we see that a server connects to the local network via the internet, and then the local network connects to the client.
In the VPN scenario, we see that the server connects to the VPN first, the VPN then to the local network, and then the local network to the client.
What we want to do is detect this additional hop. The VPN is specifically designed in such a way that it cannot be so easily detected, but this should in fact be the very thing that makes it detectable.
Firstly, we assume we have some mechanism to calculate the time it takes to contact the client on their IP. One way to do this could be to ping
them, but actually some routers specifically block this capability - and so it is not a reliable way of detecting ping time.
What we can do on the other hand is perform a traceroute
, which sends out an ICMP echo request with a TTL. This will give us a list of IPs through the backbone up until the user’s network/VPN, but perhaps not the network itself. We simply pick the last one in the list that responded to our echo request (which will be relatively close to the target) and treat this as the target’s ping time, which is our round trip time (RTT). This represents the orange arrows in the diagram.
Next we request some state change from the client application, and ask them to send us the result of this state change. This represents the pink arrows in the diagram. As you can see, for the non-VPn scenario this will be the sum of:
And for the VPN scenario, this will be:
If we can make the assumption that local network time is minimal (which you would hope it is, even over WiFi!) and that the client state change request requires constant time, then you should be able to detect a VPN with reasonable accuracy. As a guestimate, if you’re seeing additional times of say 10ms or more - then there is a good chance that there are significant additional hops happening.
So, we made quite a few assumptions, not all of them I am particularly happy about:
I believe the next steps will be to build out a small scale test. I would like to build it out in Java, but unfortunately Java does not support ICMP. It will likely need to be done by wrapping some Unix command…
I believe the best method to use would be to make some HTTP server and simply ask different people to connect to it via their IP and VPNs to see how it responds and tweak it. The server could simply respond with “VPN (not) detected” and they can report their results to me.