/** * Checks if an IP matches a proxy we've configured. * @deprecated Since 1.24, use IP::isConfiguredProxy() * * @param string $ip * @return bool * @since 1.23 Supports CIDR ranges in $wgSquidServersNoPurge */ function wfIsConfiguredProxy($ip) { wfDeprecated(__METHOD__, '1.24'); return IP::isConfiguredProxy($ip); }
/** * Checks if an IP matches a proxy we've configured. * @deprecated Since 1.24, use IP::isConfiguredProxy() * * @param string $ip * @return bool * @since 1.23 Supports CIDR ranges in $wgSquidServersNoPurge */ function wfIsConfiguredProxy($ip) { return IP::isConfiguredProxy($ip); }
/** * Work out the IP address based on various globals * For trusted proxies, use the XFF client IP (first of the chain) * * @since 1.19 * * @throws MWException * @return string */ public function getIP() { global $wgUsePrivateIPs; # Return cached result if ($this->ip !== null) { return $this->ip; } # collect the originating ips $ip = $this->getRawIP(); if (!$ip) { throw new MWException('Unable to determine IP.'); } # Append XFF $forwardedFor = $this->getHeader('X-Forwarded-For'); if ($forwardedFor !== false) { $isConfigured = IP::isConfiguredProxy($ip); $ipchain = array_map('trim', explode(',', $forwardedFor)); $ipchain = array_reverse($ipchain); array_unshift($ipchain, $ip); # Step through XFF list and find the last address in the list which is a # trusted server. Set $ip to the IP address given by that trusted server, # unless the address is not sensible (e.g. private). However, prefer private # IP addresses over proxy servers controlled by this site (more sensible). # Note that some XFF values might be "unknown" with Squid/Varnish. foreach ($ipchain as $i => $curIP) { $curIP = IP::sanitizeIP(IP::canonicalize($curIP)); if (!$curIP || !isset($ipchain[$i + 1]) || $ipchain[$i + 1] === 'unknown' || !IP::isTrustedProxy($curIP)) { break; // IP is not valid/trusted or does not point to anything } if (IP::isPublic($ipchain[$i + 1]) || $wgUsePrivateIPs || IP::isConfiguredProxy($curIP)) { // Follow the next IP according to the proxy $nextIP = IP::canonicalize($ipchain[$i + 1]); if (!$nextIP && $isConfigured) { // We have not yet made it past CDN/proxy servers of this site, // so either they are misconfigured or there is some IP spoofing. throw new MWException("Invalid IP given in XFF '{$forwardedFor}'."); } $ip = $nextIP; // keep traversing the chain continue; } break; } } # Allow extensions to improve our guess Hooks::run('GetIP', array(&$ip)); if (!$ip) { throw new MWException("Unable to determine IP."); } wfDebug("IP: {$ip}\n"); $this->ip = $ip; return $ip; }
/** * Do a sanity check to make sure the session is not used from many different IP addresses * and store some data for later sanity checks. * FIXME remove this once SessionManager is considered stable * @private For use in Setup.php only * @param Session $session Defaults to the global session. */ public function checkIpLimits(Session $session = null) { $session = $session ?: self::getGlobalSession(); try { $ip = $session->getRequest()->getIP(); } catch (\MWException $e) { return; } if ($ip === '127.0.0.1' || \IP::isConfiguredProxy($ip)) { return; } $now = time(); // Record (and possibly log) that the IP is using the current session. // Don't touch the stored data unless we are adding a new IP or re-adding an expired one. // This is slightly inaccurate (when an existing IP is seen again, the expiry is not // extended) but that shouldn't make much difference and limits the session write frequency // to # of IPs / $wgSuspiciousIpExpiry. $data = $session->get('SessionManager-ip', array()); if (!isset($data[$ip]) || $data[$ip] < $now) { $data[$ip] = time() + $this->config->get('SuspiciousIpExpiry'); foreach ($data as $key => $expires) { if ($expires < $now) { unset($data[$key]); } } $session->set('SessionManager-ip', $data); $logger = \MediaWiki\Logger\LoggerFactory::getInstance('session-ip'); $logLevel = count($data) >= $this->config->get('SuspiciousIpPerSessionLimit') ? LogLevel::WARNING : (count($data) === 1 ? LogLevel::DEBUG : LogLevel::INFO); $logger->log($logLevel, 'Same session used from {count} IPs', array('count' => count($data), 'ips' => $data, 'session' => $session->getId(), 'user' => $session->getUser()->getName(), 'persistent' => $session->isPersistent())); } // Now do the same thing globally for the current user. // We are using the object cache and assume it is shared between all wikis of a farm, // and further assume that the same name belongs to the same user on all wikis. (It's either // that or a central ID lookup which would mean an extra SQL query on every request.) if ($session->getUser()->isLoggedIn()) { $userKey = 'SessionManager-ip:' . md5($session->getUser()->getName()); $data = $this->store->get($userKey) ?: array(); if (!isset($data[$ip]) || $data[$ip] < $now) { $data[$ip] = time() + $this->config->get('SuspiciousIpExpiry'); foreach ($data as $key => $expires) { if ($expires < $now) { unset($data[$key]); } } $this->store->set($userKey, $data, $this->config->get('SuspiciousIpExpiry')); $logger = \MediaWiki\Logger\LoggerFactory::getInstance('session-ip'); $logLevel = count($data) >= $this->config->get('SuspiciousIpPerUserLimit') ? LogLevel::WARNING : (count($data) === 1 ? LogLevel::DEBUG : LogLevel::INFO); $logger->log($logLevel, 'Same user had sessions from {count} IPs', array('count' => count($data), 'ips' => $data, 'session' => $session->getId(), 'user' => $session->getUser()->getName(), 'persistent' => $session->isPersistent())); } } }