What is a captive page and how to create one?
My issue: the expected “Sign into Wi-Fi network” prompt does not always appear automatically when I try to log into a public network that requires a login, like “WiFi on ICE” in Deutsche Bahn’s intercity trains. Sometimes computers fail or refuse to display that page, making it hard to use the internet while travelling or working in a café.
Cannot reach public WIFI login page in Ubuntu, Captive Portal Issue, a post on AskUbuntu.com, has been viewed 23k times! How can so many people visit that page if they don’t have internet? 🤔
What is happening here?
Many systems like Android, ChromiumOS, Apple’s MacOS, or Microsoft Windows seem to have a built-in sign-in process that calls an external “captive page” like captive.apple.com
.
Ubuntu Linux doesn’t, which might be one of the few disadvantages of using Linux, although technically speaking, we could rather say the captive portals hijack people’s first internet requests, which is more easy to achieve on Windows and Apple devices than on Linux which mostly prioritizes security over convenience.
I tried to understand what is actually happening here and found it is not that simple and straightforward as it might seem on a first glance. Check Wikipedia and Chromium for more details.
A captive portal usually tries to capture your first website request using its DNS server, and if it doesn’t, your operating system’s connection manager will try to do so, at least on most operating system except Linux.
There is no Internet
If it doesn’t work, you might see another vintage style website telling you that there is no internet. I can tell you as I’m living in Germany. Despite the material wealth of our country, we are quite poor in other aspects of life, maybe emotionally but surely technologically, as Germany is infamous for its slow and unskillful adoption of new technology and its relatively slow and unreliable internet connections.
Understanding Captive Pages and their Limitations
Wikipedia lists different kinds of issues that might break the automatic portal process:
Captive portals often require the use of a web browser; this is usually the first application that users start after connected to the Internet, but users who first use an email client or other application that relies on the Internet may find the connection not working without explanation, and will then need to open a web browser to validate. […] A similar problem can occur if the client uses AJAX or joins the network with pages already loaded into its web browser. Similarly, as HTTPS connections cannot be redirected (at least not without triggering security warnings), a web browser that only attempts to access secure websites before being authorized by the captive portal will see those attempts fail without explanation (the usual symptom is that the intended website appears to be down or inaccessible).
Building a Captive Page of our own
Wouldn’t it be cool to have our own captive.localhost
or even a service like https://captive.open-mind-culture.org/ ready to go on any device?
While the actual captive mechanisms can do more or less complicated things like access control and intercepting network traffic, all that we want to build is a page that will trigger this mechanism.
So “creating a captive page” that we can call in our web browser to trigger the actual captive mechanism, is in fact nothing but a page that accepts an unencrypted HTTP request and returns a 204 or 200 status code without redirecting to HTTPS.
Using PHP to generate a Simple Page and Set HTTP Headers
One possible way to return a website and control its HTTP headers is by using PHP, which might be the simplest way if you already have a PHP-based application like WordPress up and running on a web server. If you haven’t, you might prefer to deploy a node server to a cloud infrastructure or configure a “serverless” service.
Let’s assume we have a PHP server ready, we can create a new directory and configure a new subdomain that points to that folder where we will then put a file called index.php
that sets the appropriate response headers, like preventing our browser or web proxies to cache the page.
Sending any Successful 2xx HTTP Status
As Apple’s implementation shows, we don’t have to send a 204 No Content
status. Many portals, like ICEportal of German long-distance trains, use their captive page to display a consent or login form, advertise their services (like entertainment content stored on a local server) or display status information (like the current train line and upcoming station). But we don’t have to bother putting too much content either, as we expect to get redirected to the real captive page once the external network intercepts our call and hijacks our first request anyway. Well, some do and some don’t, so let’s put some minimal content at least.
<?php
if (!headers_sent()) {
header('Status: 200 OK');
header('Cache-Control: no-cache');
header('Content-Type: text/html');
}
?><!DOCTYPE html>
<html lang="en">
<head>
<title>Success</title>
</head>
<body>
More content here...
Nice! Now we have to prove that it actually works and does its job, right? Here’s the page, let’s see it in action: http://captive.open-mind-culture.org
Everything seems to work as expected. No error, no redirect, no https in the first place, and an HTTP header to prevent caching.
Now all I have to do is test it “in the wild”, taking my Linux laptop outside and try to use it in a public network known to require a captive page like Deutsche Bahn’s WiFiOnICE or WiFi@DB.
Gibt es hier kein Internet?
Was ist eine Captive Page und wie kann ich selbst eine bauen?
Mein Problem: die erwartete Meldung „im Netzwerk anmelden“ erscheint nicht immer automatisch, wenn ich ein öffentliches WLAN benutzen will, das erst nach Login bzw. Akzeptieren der AGB benutzt werden kann, wie das „WiFi on ICE“ in der Deutschen Bahn. Das macht die Arbeit unterwegs auf Reisen oder im Café nicht einfacher.
Die entsprechende Diskussion auf AskUbuntu.com (Cannot reach public WIFI login page in Ubuntu, Captive Portal Issue) hat schon 23000 Views! Wie konnten sich eigentlich so viele Leute diesen Beitrag ansehen, wenn sie doch gar kein Internet haben? 🤔
Was passiert hier?
Viele Betriebssysteme wie Android, ChromiumOS, Apple MacOS oder Microsoft Windows haben anscheinend eine eingebaute Unterstützung für diese Art von Anmeldung und zeigen eine „Captive Page“ an, beispielsweise
captive.apple.com
.Nicht so Ubuntu Linux. Das ist einer der wenigen Nachteile dieses Betriebssystems, das ich ansonsten sehr schätze. Aus technischer Sicht könnten wir allerdings auch sagen, dass die Captive-Portale die Internetverbindung ihrer Nutzer*innen kidnappen, und das funktioniert eben besser auf Windows und Apple-Geräten als auf Linux, wo im Zweifel die Sicherheit Vorrang vor der Bequemlichkeit hat.
Ich wollte verstehen, was konkret passiert und musste feststellen, dass es gar nicht so einfach ist, wie es auf den ersten Blick erscheinen mag. Weiterführende technische Details sind bei Wikipedia und in der Dokumentation von Chromium nachzulesen.
Ein Captive-Portal versucht wohl üblicherweise, den ersten Seitenaufruf im neuen Netzwerk mithilfe des DNS-Servers umzuleiten, und wenn das nicht klappt, bietet der Netzwerk-Manager des Betriebssystems die Anmeldung an, außer wie gesagt bei Linux.
Es gibt kein Internet
Wenn all das nicht funktioniert, dann sehen wir eine retro-mäßig minimalistisch gestaltete Seite, die behauptet, es gäbe kein Internet. Wer, wie ich, die meiste Zeit seines Lebens in Deutschland verbracht hat, wird diese Meldung vermutlich auch schon des öfteren gesehen haben. Abgesehen vom materiellen Wohlstand unseres Landes ist Deutschland in vieler anderer Hinsicht sehr arm dran. Arm in emotionaler Hinsicht, und ganz sicher auch ziemlich arm in technologischer Hinsicht. Deutschland ist berüchtigt für langsame und ungeschickte Einführung neuer Technologien und für die relativ langsame und unzuverlässige Internetanbindung.
Captive Pages und ihre Herausforderungen verstehen
Wikipedia nennt verschiedene Arten von Problemen, an denen der automatische Anmeldeprozess scheitern kann. Wenn der erste Internetzugriff kein neuer Seitenaufruf einer HTTP-URL ist, sondern vom E-Mail-Client, per AJAX aus einer zuvor geladenen Website, oder einfach nur, wie heutzutage nicht unüblich, direkt per HTTPS stattfindet, ist das Verfahren zum Scheitern verurteilt, und es erscheint, wie im obigen Screenshot, die irreführende Fehlermeldung, dass das Internet gerade nicht verfügbar sei – außer das Betriebssystem hat heimlich, und den Datenschutz missachtend, einen Aufruf an seinen eigenen Server gestellt um zu tracken, ob wir uns gerade in ein anmeldepflichtiges Netzwerk eingewählt haben.
Aber was Apple, Google und Microsoft kann, können echte Hacker schon lange:
Wir bauen unsere eigene Captive Page
Wäre es nicht cool, eine eigene
captive.localhost
or am besten gleich eine öffentlich verfügbare Seite wie https://captive.open-mind-culture.org/ zu haben, die wir einfach selbst bei Bedarf aufrufen können?Zwar veranstalten die eigentliche Captive-Mechanismen mehr oder weniger komplizierte Dinge wie Zugriffskontrolle und Abfangen des Netzwerkverkehrs, aber alles was uns noch fehlt, ist ja nur eine Webseite, die diesen bestehende Mechanismus auslöst.
Also bedeutet „eine eigene Captive-Page bauen“ letztlich bloß eine Seite zu veröffentlichen, die eine unverschlüsselte HTTP-Verbindung akzeptiert und mit Status-Code 204 oder 200 beantwortet anstelle auf HTTPS weiterzuleiten.
Mit PHP eine einfache Webseite und HTTP-Header ausgeben
Eine Möglichkeit, eine Webseite auszugeben und die gesendeten HTTP-Header zu kontrollieren, ist das ganze in PHP zu schreiben. Das ist auch in vielen Fällen immer noch die einfachste Möglichkeit, nämlich dann, wenn ohnehin schon ein Server mit einer PHP-basierten Anwednung, wie beispielsweise WordPress, vorhanden ist. Ansonsten lässt sich alternativ genau so gut ein Node Server in die Cloud deployen oder ein „serverloser“ Servicde konfigurieren.
Aber nehmen wir mal einen PHP-Server an, dann können wir ein neues Verzeichnis erstellen und eine neue Subdomain konfigurieren, die auf das neue Verzeichnis zeigt, wo wir dann eine Datei namens
index.php
speichern, die sich um die nötigen Response-Header kümmern wird. Damit können wir dann gleich auch verhindern, dass Browser oder Proxy Server die Seite ungewollt im Cache speichern.Erfolgsmeldung mithilfe irgendeines 2xx HTTP Status
Wie wir unter anderem an Apple’s Implementation sehen können, müssen wir gar keinen
204 No Content
Status senden. Viele Portale, beispielsweise das ICEportal in deutschen Langstreckenzügen, nutzen ihre Captive Page nicht nur, um die Zustimmung zu ihren AGB bestätigen zu lassen, sondern auch zur Werbung für ihre Dienste und Unterhaltungsmedien sowie für die hilfreiche Anzeige der Zugnummer und des nächsten Bahnhofs. Aber wir sollten uns hier nicht unnötig lange mit eigenem Content aufhalten, wenn unsere Seite ja dafür gedacht ist, ein Hijacking durch das eigentliche Captive-Portal des Netzwerkbetreibers auszulösen, dessen Seite unsere ersetzen soll.<?php
if (!headers_sent()) {
header('Status: 200 OK');
header('Cache-Control: no-cache');
header('Content-Type: text/html');
}
?><!DOCTYPE html>
<html lang="en">
<head>
<title>Success</title>
</head>
<body>
More content here...
Nice! Nun wollen wir beweisen, dass sie tatsächlich funktioniert und ihre Aufgabe erfüllt, richtig? Hier ist sie: http://captive.open-mind-culture.org
Alles läuft wie geplant: kein Fehler, kein Redirect, kein https, aber ein HTTP-Header um Caching zu verhindern.
Jetzt müssen wir das ganze nur noch „in freier Wildbahn“ testen, unterwegs mit dem Laptop in einem der Netze, die in der Vergangenheit für ihre Probleme mit Linux-Clients bekannt waren, wie WiFiOnICE oder WiFi@DB.
Englischsprachiger Blogpost: http://www.open-mind-culture.org/en/2159/there-is-no-internet-creating-a-captive-page-to-sign-into-any-public-network/