Check if IP is in CIDR subnet
Sometimes, you need to check if an IP address is in a specific subnet. E.g., when writing a GitHub webhook endpoint, you want to check that the originating IP is one of GitHub’s. There’s an API call you can make to get the list of subnets hook calls can originate from:
So, the two subnets are, in CIDR format:
But how do you check these in a Django view? First, let me show you a trick how you can get the IP address in Django:
This will not only check the
REMOTE_ADDR, but also parse
HTTP_X_FORWARDED_FOR headers. You will get something like
To check this against a subnet, you first have to understand how the CIDR format works. IPs are 32 bits, i.e. 4 bytes. Above you see the normal human-readable notation. If you express the 4 bytes in binary, you get
I wrote two Python functions to easily convert IP(v4)s from the human-readable format to bits. First a function to convert a byte to bits (and pad it with 0s to get 8 bits):
and another function that splits an IP by the dots and converts the single bytes:
Back to how CIDR works: You might have noticed the / in the GitHub IPs. This
tells us the size of the subnet in bits. If you have a /8 subnet, that means
that the first 8 bits define the subnet, e.g.
18.104.22.168/8 could be anything
22.214.171.124 (this was once called a class-a subnet and
is owned by Apple).
In result, in order to check if an IP is inside GitHub’s
we have to check if the first 27 bits of the originating IP address match
Since we already have the functions to convert IPs to bits, all we have to do
now is to plug everything together:
And don’t forget to set
GITHUB_WEBHOOK_URLS in your
This is probably far from perfect and lacks support for IPv6, but it works and gives you an idea of how the internet protocol works.