Anzeige der Besucher-IP

Die Anzeige der IP-Adresse des Besuchers ist eigentlich eine Trivialität. Schließlich besteht während des Seitenabrufs eine Verbindung mit genau der IP. Interessanter ist es, parallel auch legacy IP Adressen und DNSSEC Eigenschaften zu ermitteln. Und dann soll das Ganze noch aus CMS generierten Seiten, d.h. gecachtem Content heraus funktionieren.

Die Abfrage der aktuell abfragenden IP Adresse ist mit serverseitigen Scriptsprachen eine Standardaufgabe.

<div id='your-ip'>
  <?= htmlspecialchars($_SERVER['<span class="term">REMOTE_ADDR</span>']) ?>
</div>

Schwierig wird es, wenn die Webseiten nicht ständig serverseitig bearbeitet werden, sondern aus einem Cache kommen. Dies kann der Cache eines CMS oder der eines Proxies sein. Dann ist die Scriptbearbeitung schon lange abgeschlossen, die IP steht als normaler Text drin und ist somit in der Regel falsch. Es ist unerwünscht, nur wegen dieser Anzeige, die sinnvollen Cache-Funktionen zu deaktivieren.

Stattdessen lagert man den Code der IP Ermittlung aus in ein minimales serverseitiges Script, daß nur die IP Adresse ausgibt. Und dieses Script muß vom Webbrowser clientseitig nachgeladen werden. Dazu dient Javascript. Das Nachladen von Inhalten ist unter dem Schlagwort Ajax bekannt.

<div id='your-ip'>
  <script type='text/javascript' src='http://lutz.donnerhacke.de/check-ipv6-dnssec.js'>
  </script>
</div>

Worauf kann nun dieses Script selbst zurückgreifen? Normalerweise würde man für Ajax ein Framework wie jQuery nehmen. Dies ist aber hier nicht möglich. Das einbettende System kann unbekannt sein (bei Fremdeinbindung) oder ungeeignete Frameworks schon benutzen, mit denen sich das eigene Framework beissen könnte.

Es bleibt nur, Ajax von Hand zu machen. Glücklicherweise brauche ich hier keine Rücksicht auf seltsame und alte Browser zu nehmen, ich kann mich an den Standard halten. Wenn also der Browser diesen Standard unterstützt, wird das HTML Stück mit der IP-Adresse nachgeladen:

if (window.XMLHttpRequest) {
  var xmlhttp = new XMLHttpRequest();

  xmlhttp.onreadystatechange = function() {
    if(xmlhttp.readyState == 4) {
      var mydiv = document.getElementById("your-ip");
      mydiv.innerHTML = xmlhttp.responseText;
    }
  };
  xmlhttp.open("GET","/cgi-bin/check-ipv6-dnssec.pl",true);
  xmlhttp.send(null);
}

Der serverseitige Code muß mit einem relativen Pfad geladen werden, dies verlangen die Sicherheitseinstellungen der gängigen Browser, um Cross-Site Angriffe zu vermindern. In diesem Fall wird serverseitig ein Perl-Script ausgeführt.

#! /usr/bin/perl

print q{Content-Type: text/html; charset=latin1

<html>
<body id="inhalt">};

print $ENV{'REMOTE_ADDR'};

print "</body></html>";

Damit ist das Cache-Problem gelöst.

IPv6 und IPv4 zusammen

Wenn der Client per IPv6 Adresse ankommt, dann soll auch noch seine IPv4 Adresse ermittelt werden. Dazu wird der Code leicht abgewandet. Es wird ein Marker zurückgeliefert, der dem Javascript zeigt, das noch etwas fehlt.

...
my $addr = $ENV{'REMOTE_ADDR'};
print "$addr<br/>";

if($addr =~ /:/) {
    print "<span id='your-legacy-ip'></span><br/>\n";
}
...

Und das Javascript selbst wird entsprechend erweitert.

...
      mydiv.innerHTML = xmlhttp.responseText;

      var legacy = document.getElementById("your-legacy-ip");
      if(legacy) {
        var fill_legacy  = document.createElement("script");
        fill_legacy.type = "text/javascript";
        fill_legacy.src  = "http://v4.lutz.donnerhacke.de/cgi-bin/check-v4.js"
        legacy.appendChild(fill_legacy);
      }
    }
...

Der große Trick besteht darin, dass Scripte von anderen Servern nachgeladen werden dürfen. In diesem Fall von einem Server, der kein AAAA Record im DNS anbietet. Damit ist der Client gezwungen, auf das veraltete IPv4 Protokoll zurückzuspringen. Genausogut kann man einen IPv6-only Server befragen, um das aktuelle IP Protokoll anzufordern. Dieses Javascript darf nicht mit "document.write" arbeiten, sondern muß in den bestehenden DOM des Dokuments eingreifen.

#! /usr/bin/perl

print <<END
Content-Type: text/javascript

document.getElementById("your-legacy-ip").innerHTML = "$ENV{'REMOTE_ADDR'}";
END

Mehrwerte hinzufügen

Alle weiteren Sachen sind problemlos. Das den Haupttext aufgerufene Perl-Script kann wesentlich mehr tun. Es kann auf Übergang-IPv6 Adressen testen ($addr =~ /^2001:0*:/ || $addr =~ /^2002:/) und validierende Resolver nach DNSSEC fragen dig +dnssec PTR.

Bleibt noch die Mehrsprachigkeit. Dazu muß das Script erfahren, in welchem Sprachkontext es aufgerufen wurde. Am einfachsten geht das mit einem URL Parameter beim Script-Aufruf. Aber wer soll diesen auswerten? Witzigerweise ist es auch clientseitig im Browser möglich, die Parameter von einer URL an die anderen zu hängen. Damit ergibt sich das folgende vollständige Script.

if (window.XMLHttpRequest) {
  var xmlhttp = new XMLHttpRequest();
  var urlargs = document.currentScript.src.match(/(\?.*)/);
  var args = urlargs ? urlargs[0] : "";

  xmlhttp.onreadystatechange = function() {
    if(xmlhttp.readyState == 4) {
      var mydiv = document.getElementById("your-ip");
      mydiv.innerHTML = xmlhttp.responseText;

      var legacy = document.getElementById("your-legacy-ip");
      if(legacy) {
        var fill_legacy  = document.createElement("script");
        fill_legacy.type = "text/javascript";
        fill_legacy.src  = "http://v4.lutz.donnerhacke.de/cgi-bin/check-v4.js"+args
        legacy.appendChild(fill_legacy);
      }
    }
  };
  xmlhttp.open("GET","/cgi-bin/check-ipv6-dnssec.pl"+args,true);
  xmlhttp.send(null);
}

Wer es nutzen will, soll es so einbinden, wie am Anfang geschrieben. Der relevante URL Parameter lautet locale={ger,eng}.

Erzwingen von IPv6

Wie bei RIPE festgestellt wurde, funktioniert die Erzwingung von IPv6 Verbindungen nicht, wenn als Übergangstechnik nur Teredo zum Einsatz kommt. Der Grund liegt darin, dass die aktuellen Windows-Systeme keine AAAA Anfragen stellen, wenn sie keine halbwegs brauchbaren IPv6 Adressen lokal vorliegen haben.

Das Nachschlagen von v6.lutz.donnerhacke.de ist also sinnfrei. Trägt man dagegen die IP Adresse ein, klappt es problemlos.

Viel Spaß!

Avatar
Lutz Donnerhacke 23.10.2012 22:04
Danke RIPE wird nun auch IPv6 Adressen von Teredo und Co. angezeigt.
Avatar
Lutz Donnerhacke 04.10.2012 14:26
Und nun wird auch noch die IPv6 Adresse angezeigt, falls der Client IPv4 präferiert. Geht eine Protokollfamilie nicht, steht das dann explizit da.

Und wenn ein Proxy verwendet wird, wird die IP hinter dem Proxy ebenfalls angezeigt.

Ok so?

2 Kommentare

Post a comment

Verwandter Inhalt