How Captive Portal Network Authentication Works: Technical Deep Dive for Developers


2 views

Captive portals operate through multiple layers of network interception and redirection. When a device connects to such a network, the following sequence occurs:


// Example of DNS interception (Python pseudocode)
def captive_portal_intercept(request):
    if not request.client.has_authenticated:
        redirect_to_portal(request.ip)
    else:
        allow_regular_traffic(request)

The system typically involves these technical elements:

  • DNS hijacking (resolving all domains to portal IP)
  • HTTP/HTTPS interception
  • MAC address tracking
  • Session cookies or tokens

Here's how modern OSes detect captive portals by attempting to access a known endpoint:


// JavaScript example for portal detection
fetch('http://connectivitycheck.gstatic.com/generate_204')
  .then(response => {
    if (response.status !== 204) {
      // Portal detected
      window.location = response.url;
    }
  });

For developers creating apps that need to handle captive networks:


// Python example using requests with portal handling
import requests

def internet_connectivity_check():
    try:
        response = requests.get('http://example.com', timeout=5)
        if len(response.content) < 1000:  # Likely portal page
            return False
        return True
    except:
        return False

For automated testing scenarios where you need to authenticate through the portal:


// Automating form submission (Python + Selenium example)
from selenium import webdriver

def portal_authenticate(username, password):
    driver = webdriver.Chrome()
    driver.get('http://google.com')  # Will redirect to portal
    driver.find_element_by_name('username').send_keys(username)
    driver.find_element_by_name('password').send_keys(password)
    driver.find_element_by_tag_name('form').submit()

Important security aspects developers should know:

  • HTTPS breaks the traditional captive portal flow (hence HTTP interception)
  • Certificate pinning can prevent portal redirection
  • Always validate portal pages before submitting credentials

Basic captive portal implementation using Linux and iptables:


# Basic iptables rules for captive portal
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j DNAT \
--to-destination 192.168.1.1:80
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 443 -j DNAT \
--to-destination 192.168.1.1:443

When you connect to a public WiFi network, the gateway (usually a router or firewall) intercepts all HTTP traffic through these key steps:

  1. Initial DNS Query: Your device resolves domains through the captive portal's DNS server
  2. HTTP Redirect: All HTTP requests get 302-redirected to the portal's authentication page
  3. Traffic Whitelist: Only specific domains (like payment processors) are allowed through
// Example of how a router might implement this in iptables:
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 \
         -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 443 \
         -j DNAT --to-destination 10.0.0.1:443

Modern operating systems actively check for captive portals by attempting to access specific test URLs:

  • Apple uses http://captive.apple.com/hotspot-detect.html
  • Microsoft uses http://www.msftconnecttest.com/connecttest.txt
  • Android pings http://connectivitycheck.gstatic.com/generate_204
# Python example to detect captive portals
import requests

def check_captive_portal():
    try:
        r = requests.get("http://connectivitycheck.gstatic.com/generate_204", timeout=3)
        return r.status_code != 204
    except:
        return True

Different networks implement captive portals in distinct ways:

Method Implementation Bypass Difficulty
DNS Redirection All DNS queries resolve to portal IP Easy (use DoH/DoT)
HTTP Transparent Proxy TCP 80 traffic gets intercepted Medium (use HTTPS)
Full SSL Inspection MitM with custom CA cert Hard (requires cert trust)

For automation scenarios, you can script the authentication process:

// Node.js example for hotel WiFi authentication
const puppeteer = require('puppeteer');

async function autoAuth(credentials) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  try {
    await page.goto('http://neverssl.com', {waitUntil: 'networkidle0'});
    const redirectUrl = page.url();
    
    if(redirectUrl.includes('hotelwifi.com/auth')) {
      await page.type('#username', credentials.user);
      await page.type('#password', credentials.pass);
      await page.click('#accept_terms');
      await page.click('#submit_button');
    }
  } finally {
    await browser.close();
  }
}

Captive portals create unique security challenges:

  • HTTPS interception requires installing custom CA certificates
  • MAC address tracking persists even after network disconnection
  • Cookie-based sessions can be hijacked if not properly secured
# Bash command to monitor portal interactions
tcpdump -i wlan0 -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' | 
grep -E 'Host:|GET /|POST /|HTTP/'