Technology may have changed in the meantime.
I’ve been a freelance web developer for almost 15 years.
One of the most brilliant web pages I ever made, was this one:
<?php header('Content-Type: text/plain'); echo $_SERVER['REMOTE_ADDR']; ?>
And if you think I’m joking, allow me to give you some background…
The above code returns a page with no other content than the visitor’s IP address. This may seem trivial and rather useless, but consider the following:
You’re developing a website. It’s a website for, obviously, someone who is not a web developer, which means, especially for people of my generation and older, that they know close to nothing about this internet-thing.
At a certain moment, you want to show them what you’ve done so far, but you do not want the rest of the internet to find the website already. But you also know that this person, having other hobbies than learning all the ins and outs of this new technology, will type the URL you send them into the Google search field, thinking that that is the location bar you were talking about. And once Google gets hold of something, you can be sure it will be out there for everybody to find.
So, you need to make sure only your customer can access this new website; this way, you can be sure that even if Google has the address, it won’t be able to index the site. And the most practical way to make sure only the client can access the site, is to limit access to the client’s IP address. But how does one explain to this lay person where to find their external IP address?
And that’s why I came up with the code above. With that in place, I could just ask my client to go to https://www.example.com/ip.php, copy all the text they find there, and mail it to me. Their reply would always be something like “It only says 203.0.113.45. Are you sure?“, to which I always replied “That’s all I need. Thank you!“.
The lesson here: When working with lay people, make sure that there is nothing that they may want to interpret or filter.
And even though I no longer develop websites for clients, I still have a use for this page that was invented about 15 years ago: to find my own IP address (I travel a lot) and, with the small enhancement I made, also to verify my TOR connection.
So, I thought I’d share.
The TOR project publishes a regularly updated list of all the current TOR exit nodes. I wrote a small script that fetches this list, extracts the IP addresses for the exit nodes, and puts them all in a PHP array; a cronjob runs this script every hour, to make sure I always have an up-to-date list of exit nodes.
#!/usr/bin/env bash ################################################################################ # # This script retrieves the current list of exit nodes from the TOR website, # and writes their IP addresses to a PHP array. # # For more info: https://www.ohreally.nl/2020/08/01/external-ip-address # ################################################################################ # # Copyright (c) 2020 Rob LA LAU < https://www.ohreally.nl/ > # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ################################################################################ # The directory where the file should be saved. # The complete path to the file will be # ${dir}/tor-exit-nodes.php dir="/srv/www/www.example.com/htdocs" ################################################################################ # Do not change anything below this line. # Applications we need. WGET=`which wget` || exit 1 GREP=`which grep` || exit 1 AWK=`which awk` || exit 1 MV=`which mv` || exit 1 # Filenames. tmp="${dir}/tor-exit-nodes.tmp" php="${tmp/tmp/php}" # Create the array and write it to a PHP file. echo '<?php' > "${tmp}" echo '$exitnodes = array(' >> "${tmp}" "${WGET}" -q -O - https://check.torproject.org/exit-addresses | "${GREP}" '^ExitAddress ' | "${AWK}" "{printf \"'%s',\", \$2}" >> "${tmp}" echo ');' >> "${tmp}" echo '?>' >> "${tmp}" # Replace the previous list with the new list. "${MV}" "${tmp}" "${php}"
The result of this is a large PHP array of IP addresses, named $exitnodes, in the file /srv/www/www.example.com/htdocs/tor-exit-nodes.php. This can now be included in the code from the beginning of this post, to indicate TOR exit nodes.
<?php include './tor-exit-nodes.php'; header('Content-Type: text/plain'); echo $_SERVER['REMOTE_ADDR']; if (in_array($_SERVER['REMOTE_ADDR'], $exitnodes)) { echo ' (TOR exit node)'; } ?>
And with that, the page in question will always display the visitor’s external IP address, and will indicate that it’s a TOR exit node if that’s the case.