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