1*e6a02230Stracker-user<?php 2*e6a02230Stracker-user/** 3*e6a02230Stracker-user * Hide IP — action component. 4*e6a02230Stracker-user * 5*e6a02230Stracker-user * Hooks INIT_LANG_LOAD (same point in DokuWiki's boot the anonip plugin uses) 6*e6a02230Stracker-user * and rewrites the server-side IP variables so that every later piece of code 7*e6a02230Stracker-user * — clientIP(), $INPUT->server->str('REMOTE_ADDR'), changelog writers, page 8*e6a02230Stracker-user * metadata, the mailer's X-Originating-IP header, AJAX info, etc. — sees a 9*e6a02230Stracker-user * constant placeholder instead of the real address. 10*e6a02230Stracker-user * 11*e6a02230Stracker-user * Why a constant ('0.0.0.0') rather than a session-hashed pseudo-IPv6: 12*e6a02230Stracker-user * - This wiki has no anonymous edits; the username field already 13*e6a02230Stracker-user * differentiates editors. A pseudo-IP adds no investigative value. 14*e6a02230Stracker-user * - Less surface area for re-identification attacks. The original anonip 15*e6a02230Stracker-user * leaked the first IPv4 octet via auth_browseruid(); even the fork's 16*e6a02230Stracker-user * session-hash variant still depends on session-stability assumptions. 17*e6a02230Stracker-user * - Page locking is not affected: inc/common.php::lock() writes only the 18*e6a02230Stracker-user * username when REMOTE_USER is set (which it always will be here), and 19*e6a02230Stracker-user * unlock() has session_id() as a fallback even when it isn't. 20*e6a02230Stracker-user */ 21*e6a02230Stracker-user 22*e6a02230Stracker-useruse dokuwiki\Extension\ActionPlugin; 23*e6a02230Stracker-useruse dokuwiki\Extension\EventHandler; 24*e6a02230Stracker-useruse dokuwiki\Extension\Event; 25*e6a02230Stracker-user 26*e6a02230Stracker-userclass action_plugin_hideip extends ActionPlugin 27*e6a02230Stracker-user{ 28*e6a02230Stracker-user /** The placeholder all anonymised reads will return. */ 29*e6a02230Stracker-user const PLACEHOLDER_IP = '0.0.0.0'; 30*e6a02230Stracker-user 31*e6a02230Stracker-user /** 32*e6a02230Stracker-user * @param EventHandler $controller 33*e6a02230Stracker-user */ 34*e6a02230Stracker-user public function register(EventHandler $controller) 35*e6a02230Stracker-user { 36*e6a02230Stracker-user // INIT_LANG_LOAD fires after init.php has applied any trusted-proxy 37*e6a02230Stracker-user // X-Forwarded-For rewriting (lines ~500/550 of init.php), so by the 38*e6a02230Stracker-user // time we run, $_SERVER['REMOTE_ADDR'] holds the resolved client IP 39*e6a02230Stracker-user // that DokuWiki would normally record. We clobber it before any 40*e6a02230Stracker-user // downstream code reads it again. 41*e6a02230Stracker-user $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'handleAnonymise'); 42*e6a02230Stracker-user } 43*e6a02230Stracker-user 44*e6a02230Stracker-user /** 45*e6a02230Stracker-user * @param Event $event unused, dispatch signature only 46*e6a02230Stracker-user * @param mixed $param unused 47*e6a02230Stracker-user */ 48*e6a02230Stracker-user public function handleAnonymise(Event $event, $param) 49*e6a02230Stracker-user { 50*e6a02230Stracker-user // Direct $_SERVER write: DokuWiki's dokuwiki\Input\Server class uses 51*e6a02230Stracker-user // $access =& $_SERVER (see inc/Input/Server.php), so $INPUT->server 52*e6a02230Stracker-user // reads pick up the new value without anything else to do. 53*e6a02230Stracker-user $_SERVER['REMOTE_ADDR'] = self::PLACEHOLDER_IP; 54*e6a02230Stracker-user 55*e6a02230Stracker-user // Also clear every common forwarding header. inc/Ip.php::clientIps() 56*e6a02230Stracker-user // walks HTTP_X_FORWARDED_FOR and appends every IP it finds; if we 57*e6a02230Stracker-user // left these set, the real client address would still leak into 58*e6a02230Stracker-user // clientIP(false) (multi-IP mode) and into header logs/UIs that 59*e6a02230Stracker-user // consume those raw values. 60*e6a02230Stracker-user foreach ( 61*e6a02230Stracker-user [ 62*e6a02230Stracker-user 'HTTP_X_FORWARDED_FOR', 63*e6a02230Stracker-user 'HTTP_X_REAL_IP', 64*e6a02230Stracker-user 'HTTP_CLIENT_IP', 65*e6a02230Stracker-user 'HTTP_FORWARDED', 66*e6a02230Stracker-user 'HTTP_CF_CONNECTING_IP', // Cloudflare 67*e6a02230Stracker-user 'HTTP_TRUE_CLIENT_IP', // Akamai / Cloudflare Enterprise 68*e6a02230Stracker-user ] as $header 69*e6a02230Stracker-user ) { 70*e6a02230Stracker-user if (isset($_SERVER[$header])) unset($_SERVER[$header]); 71*e6a02230Stracker-user } 72*e6a02230Stracker-user } 73*e6a02230Stracker-user} 74