What does your browser tell folks? - part 1 - Code

What does your browser tell folks? - part 1 - Code

My good friend Mike is a local Cyber champion and often gives talks to various organisations, generally retired folks. His next talk is to be on the subject of Internet Safety and he wanted to demonstrate the sort of information that is available to any website you visit.

For example - how do sites like this, know about you?
https://www.whatsmyip.org/more-info-about-you/

So I took up the challenge to see if I could write something to provide similar information and just how simple it might be.

The information comes from 3 separate sources

  • Your Browser header information
  • The WHOIS entry corresponding to your IP address
  • A collated database which is possibly updated from sites to whom you have given permission to access your location.

The Browser headers contain information about your Browser (obviously), Operating System, machine type and model, and screen size.

The WHOIS entry has the details of your ISP and connection type.

The database entries should be able to make a reasonable stab at your geo-location although my be well out (up to 200km!). I suspect accuracy depends on how many websites, in the past, have been granted access to your location. Also some database repositories appear more accurate than others.

For this exercise I chose WhoisXMLAPI - https://ip-geolocation.whoisxmlapi.com/api because their geolocation of my own IP was fairly accurate and they had a generous free 1000 lookups per month without the need for credit card registration. They also do the IP lookup (otherwise I would need to do a request.remote_addr), the WhoIs lookup, and even do a DNS lookup to list domains registered to this IP.

Let's look at the Python code - it really is pretty minimal - just get and parse the User-Agent and then call SimpleGeoIP for all the IP, ISP, and geo information. A little bit of code saves wasting Simple GeoIP calls when testing, by reusing a 'Blue Peter' call that I made earlier.The results from both the User-Agent and SimpleGeoIp calls (both Python dictionary structures) are then simply passed onto the Jinja2 template presenting the page on the Flask server.

Here's the ENTIRE Python code:

#! /usr/bin/python3
from flask import Flask, request, render_template
from flask_simple_geoip import SimpleGeoIP
from user_agents import parse

app= Flask(__name__)  # define the flask web server
app.config["GEOIPIFY_API_KEY"] = "xxxxxxxxxxxxxxxxxxxxxxxx" # my API key for SimpleGeoIP

@app.route('/') #Define the / (i.e. index) page of the website
def index():
# Add a "type=xxxx" parameter to the URL so that testing doesn't waste SimpleGeoIP lookups 
    production = True if request.args.get('type') == 'xxxx' else False
    
    UAHeaders = request.headers.get('User-Agent') # Get the HTTP user_agent
    UAparsed = parse(UAHeaders) # Parse it nicely
    
    if production:
        simple_geoip = SimpleGeoIP(app)  # set up the SimpleGeoIp API
        geoip_data = simple_geoip.get_geoip_data() # call SimpleGeoipto get IP info
        runtype = "Production"
    else: # if just testing use precanned information  
        geoip_data = {'ip': '81.174.xx.xx', 'location': {'country': 'GB', 'region': 'England', 'city': "xxxxxxxxxx", 'lat': xxxxxxxx, 'lng': -1.39905, 'postalCode': '', 'timezone': '+00:00', 'geonameId': 7287933}, 'domains': ['two-drifters.co.uk'], 'as': {'asn': 6871, 'name': 'Plusnet plc', 'route': '81.174.128.0/17', 'domain': 'http://www.plus.net', 'type': 'Cable/DSL/ISP'}, 'isp': 'Plusnet', 'connectionType': 'broadband'
        runtype = "Test"

    return render_template('index.html',runtype=runtype, ua=UAparsed, geoip=geoip_data) # render the webpage
   
if __name__ == '__main__':  # main program code
    app.run(host='192.168.1.15', debug=True) # simply run our flask webserver app on this machine




In the HTML a couple of Javascript functions get the screen and viewport sizes (thanks to Andy Langton for this code)

<script type="text/javascript">
      document.getElementById("screensize").innerHTML =
        screen.width + "x" + screen.height;
    </script>

    <script type="text/javascript">
      var wh = getViewportSize();
      var w = wh[0];
      var h = wh[1];

      function getViewportSize() {
        var viewportwidth;
        var viewportheight;
        if (typeof window.innerWidth != "undefined") {
          (viewportwidth = window.innerWidth),
            (viewportheight = window.innerHeight);
        } else if (
          typeof document.documentElement != "undefined" &&
          typeof document.documentElement.clientWidth != "undefined" &&
          document.documentElement.clientWidth != 0
        ) {
          (viewportwidth = document.documentElement.clientWidth),
            (viewportheight = document.documentElement.clientHeight);
        } else {
          (viewportwidth = document.getElementsByTagName("body")[0]
            .clientWidth),
            (viewportheight = document.getElementsByTagName("body")[0]
              .clientHeight);
        }
        return [viewportwidth, viewportheight];
      }

      document.getElementById("viewportsize").innerHTML = w + "x" + h;
    </script>




And there is a function directly lifted from Here's developer documentation to display the map - the only difference being that the lat/lon coordinates are passed into the function from the SimpleGeoIP call.

<html>
  <head>
  ...
     <script
      src="https://js.api.here.com/v3/3.1/mapsjs-core.js"
      type="text/javascript"
      charset="utf-8"
    ></script>
    <script
      src="https://js.api.here.com/v3/3.1/mapsjs-service.js"
      type="text/javascript"
      charset="utf-8"
    ></script>
  ...  
  </head>

  <body style="margin: 0">
  ...   
    <script>
        var platform = new H.service.Platform({
          'apikey': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
        });

      var lat = {{ geoip['location']['lat']}};
      var long = {{geoip['location']['lng']}};
      var maptypes = platform.createDefaultLayers();
       var map = new H.Map(
          document.getElementById('mapContainer'),
          maptypes.vector.normal.map,
          {
            zoom: 13,
            center: { lat: lat, lng: long }
          });
    </script>
  ...  
  </body>
</html>




The corresponding CSS is very basic and the only tricksy bit as a nice two column formatter.

And that's it! As long as things are in the correct sub-directories, (templates/ for the html and static/css/ for the CSS) it just works. Running it and browsing to 192.168.1.15:5000 gives a page similar to this:

Screenshot-from-2021-02-17-16-55-56

That was run via my VPN. My normal home location does actually include my City details, but not my Postcode.