1e6a02230Stracker-user<?php 2*047cf127Stracker-userif (!defined('DOKU_INC')) die(); 3*047cf127Stracker-user 4e6a02230Stracker-user/** 5e6a02230Stracker-user * Hide IP — action component. 6e6a02230Stracker-user * 7*047cf127Stracker-user * Hooks INIT_LANG_LOAD and rewrites the server-side IP variables so that every 8*047cf127Stracker-user * later piece of code — clientIP(), $INPUT->server->str('REMOTE_ADDR'), 9*047cf127Stracker-user * changelog writers, page metadata, the mailer's X-Originating-IP header, 10*047cf127Stracker-user * AJAX info, etc. — sees a constant placeholder instead of the real address. 11e6a02230Stracker-user * 12e6a02230Stracker-user * Why a constant ('0.0.0.0') rather than a session-hashed pseudo-IPv6: 13e6a02230Stracker-user * - This wiki has no anonymous edits; the username field already 14e6a02230Stracker-user * differentiates editors. A pseudo-IP adds no investigative value. 15e6a02230Stracker-user * - Less surface area for re-identification attacks. The original anonip 16e6a02230Stracker-user * leaked the first IPv4 octet via auth_browseruid(); even the fork's 17e6a02230Stracker-user * session-hash variant still depends on session-stability assumptions. 18e6a02230Stracker-user * - Page locking is not affected: inc/common.php::lock() writes only the 19e6a02230Stracker-user * username when REMOTE_USER is set (which it always will be here), and 20e6a02230Stracker-user * unlock() has session_id() as a fallback even when it isn't. 21*047cf127Stracker-user * 22*047cf127Stracker-user * Note: DOKU_URL / DOKU_BASE are constants defined at init.php:103-104, 23*047cf127Stracker-user * before INIT_LANG_LOAD fires. If your wiki relies on trustedproxy-based 24*047cf127Stracker-user * SSL detection at runtime (is_ssl() called after init), set $conf['baseurl'] 25*047cf127Stracker-user * explicitly so DokuWiki does not consult REMOTE_ADDR for URL construction. 26e6a02230Stracker-user */ 27e6a02230Stracker-user 28e6a02230Stracker-useruse dokuwiki\Extension\ActionPlugin; 29e6a02230Stracker-useruse dokuwiki\Extension\EventHandler; 30e6a02230Stracker-useruse dokuwiki\Extension\Event; 31e6a02230Stracker-user 32e6a02230Stracker-userclass action_plugin_hideip extends ActionPlugin 33e6a02230Stracker-user{ 34e6a02230Stracker-user /** The placeholder all anonymised reads will return. */ 35*047cf127Stracker-user public const PLACEHOLDER_IP = '0.0.0.0'; 36e6a02230Stracker-user 37e6a02230Stracker-user /** 38*047cf127Stracker-user * Register event hooks. 39*047cf127Stracker-user * 40e6a02230Stracker-user * @param EventHandler $controller 41*047cf127Stracker-user * @return void 42e6a02230Stracker-user */ 43e6a02230Stracker-user public function register(EventHandler $controller) 44e6a02230Stracker-user { 45*047cf127Stracker-user // INIT_LANG_LOAD fires at init.php:233, after the plugin controller and 46*047cf127Stracker-user // $INPUT are ready, but before auth_setup() and any page-handling code 47*047cf127Stracker-user // reads the client IP. Clobbering here covers every downstream consumer. 48e6a02230Stracker-user $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'handleAnonymise'); 49e6a02230Stracker-user } 50e6a02230Stracker-user 51e6a02230Stracker-user /** 52*047cf127Stracker-user * Overwrite REMOTE_ADDR and all forwarding headers with the placeholder. 53*047cf127Stracker-user * 54e6a02230Stracker-user * @param Event $event unused, dispatch signature only 55e6a02230Stracker-user * @param mixed $param unused 56*047cf127Stracker-user * @return void 57e6a02230Stracker-user */ 58e6a02230Stracker-user public function handleAnonymise(Event $event, $param) 59e6a02230Stracker-user { 60e6a02230Stracker-user // Direct $_SERVER write: DokuWiki's dokuwiki\Input\Server class uses 61e6a02230Stracker-user // $access =& $_SERVER (see inc/Input/Server.php), so $INPUT->server 62e6a02230Stracker-user // reads pick up the new value without anything else to do. 63e6a02230Stracker-user $_SERVER['REMOTE_ADDR'] = self::PLACEHOLDER_IP; 64e6a02230Stracker-user 65e6a02230Stracker-user // Also clear every common forwarding header. inc/Ip.php::clientIps() 66e6a02230Stracker-user // walks HTTP_X_FORWARDED_FOR and appends every IP it finds; if we 67e6a02230Stracker-user // left these set, the real client address would still leak into 68e6a02230Stracker-user // clientIP(false) (multi-IP mode) and into header logs/UIs that 69e6a02230Stracker-user // consume those raw values. 70e6a02230Stracker-user foreach ( 71e6a02230Stracker-user [ 72e6a02230Stracker-user 'HTTP_X_FORWARDED_FOR', 73e6a02230Stracker-user 'HTTP_X_REAL_IP', 74e6a02230Stracker-user 'HTTP_CLIENT_IP', 75e6a02230Stracker-user 'HTTP_FORWARDED', 76e6a02230Stracker-user 'HTTP_CF_CONNECTING_IP', // Cloudflare 77e6a02230Stracker-user 'HTTP_TRUE_CLIENT_IP', // Akamai / Cloudflare Enterprise 78e6a02230Stracker-user ] as $header 79e6a02230Stracker-user ) { 80e6a02230Stracker-user if (isset($_SERVER[$header])) unset($_SERVER[$header]); 81e6a02230Stracker-user } 82e6a02230Stracker-user } 83e6a02230Stracker-user} 84