diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index e959bf9..980de35 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1781,7 +1781,7 @@ function watchdog($type, $message, array $variables = NULL, $severity = WATCHDOG 'uid' => $user_uid, 'request_uri' => $base_root . request_uri(), 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', - 'ip' => ip_address(), + 'ip' => drupal_container()->get('request')->getClientIp(), // Request time isn't accurate for long processes, use time() instead. 'timestamp' => time(), ); @@ -3103,52 +3103,6 @@ function arg($index = NULL, $path = NULL) { } /** - * Returns the IP address of the client machine. - * - * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header - * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of - * the proxy server, and not the client's. The actual header name can be - * configured by the reverse_proxy_header variable. - * - * @return - * IP address of client machine, adjusted for reverse proxy and/or cluster - * environments. - */ -function ip_address() { - $ip_address = &drupal_static(__FUNCTION__); - - if (!isset($ip_address)) { - $ip_address = $_SERVER['REMOTE_ADDR']; - - if (settings()->get('reverse_proxy', 0)) { - $reverse_proxy_header = settings()->get('reverse_proxy_header', 'HTTP_X_FORWARDED_FOR'); - if (!empty($_SERVER[$reverse_proxy_header])) { - // If an array of known reverse proxy IPs is provided, then trust - // the XFF header if request really comes from one of them. - $reverse_proxy_addresses = settings()->get('reverse_proxy_addresses', array()); - - // Turn XFF header into an array. - $forwarded = explode(',', $_SERVER[$reverse_proxy_header]); - - // Trim the forwarded IPs; they may have been delimited by commas and spaces. - $forwarded = array_map('trim', $forwarded); - - // Tack direct client IP onto end of forwarded array. - $forwarded[] = $ip_address; - - // Eliminate all trusted IPs. - $untrusted = array_diff($forwarded, $reverse_proxy_addresses); - - // The right-most IP is the most specific we can trust. - $ip_address = array_pop($untrusted); - } - } - } - - return $ip_address; -} - -/** * Initializes and returns the class loader. * * The class loader is responsible for lazy-loading all PSR-0 compatible diff --git a/core/includes/session.inc b/core/includes/session.inc index 31e67a6..a82d252 100644 --- a/core/includes/session.inc +++ b/core/includes/session.inc @@ -173,7 +173,7 @@ function _drupal_session_write($sid, $value) { // Either ssid or sid or both will be added from $key below. $fields = array( 'uid' => $user->uid, - 'hostname' => ip_address(), + 'hostname' => drupal_container()->get('request')->getClientIp(), 'session' => $value, 'timestamp' => REQUEST_TIME, ); diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index 16faeef..394dba1 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -207,6 +207,9 @@ public function build(ContainerBuilder $container) { $container->register('mime_type_matcher', 'Drupal\Core\Routing\MimeTypeMatcher') ->addTag('route_filter'); + $container->register('reverse_proxy_subscriber', 'Drupal\Core\EventSubscriber\ReverseProxySubscriber') + ->addArgument(new Reference('settings')) + ->addTag('event_subscriber'); $container->register('router_processor_subscriber', 'Drupal\Core\EventSubscriber\RouteProcessorSubscriber') ->addArgument(new Reference('content_negotiation')) ->addTag('event_subscriber'); diff --git a/core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php new file mode 100644 index 0000000..b7b45d6 --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php @@ -0,0 +1,57 @@ +settings = $settings; + } + + /** + * Passes reverse proxy settings to current request. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event + * The Event to process. + */ + public function onKernelRequestReverseProxyCheck(GetResponseEvent $event) { + $request = $event->getRequest(); + if ($this->settings->get('reverse_proxy', 0)) { + $reverse_proxy_header = $this->settings->get('reverse_proxy_header', 'HTTP_X_FORWARDED_FOR'); + $request->setTrustedHeaderName($request::HEADER_CLIENT_IP, $reverse_proxy_header); + $reverse_proxy_addresses = $this->settings->get('reverse_proxy_addresses', array()); + $request->setTrustedProxies($reverse_proxy_addresses); + } + } + + /** + * Registers the methods in this class that should be listeners. + * + * @return array + * An array of event listener definitions. + */ + static function getSubscribedEvents() { + $events[KernelEvents::REQUEST][] = array('onKernelRequestReverseProxyCheck', 10); + return $events; + } +} diff --git a/core/lib/Drupal/Core/Flood/DatabaseBackend.php b/core/lib/Drupal/Core/Flood/DatabaseBackend.php index a7b05f4..0bc30d1 100644 --- a/core/lib/Drupal/Core/Flood/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Flood/DatabaseBackend.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Flood; +use Symfony\Component\HttpFoundation\Request; use Drupal\Core\Database\Connection; /** @@ -22,14 +23,24 @@ class DatabaseBackend implements FloodInterface { protected $connection; /** + * A request object. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; + + /** * Construct the DatabaseBackend. * * @param \Drupal\Core\Database\Connection $connection * The database connection which will be used to store the flood event * information. + * @param \Symfony\Component\HttpFoundation\Request $request + * The HttpRequest object representing the current request. */ - public function __construct(Connection $connection) { + public function __construct(Connection $connection, Request $request) { $this->connection = $connection; + $this->request = $request; } /** @@ -37,7 +48,7 @@ public function __construct(Connection $connection) { */ public function register($name, $window = 3600, $identifier = NULL) { if (!isset($identifier)) { - $identifier = ip_address(); + $identifier = $this->request->getClientIp(); } $this->connection->insert('flood') ->fields(array( @@ -54,7 +65,7 @@ public function register($name, $window = 3600, $identifier = NULL) { */ public function clear($name, $identifier = NULL) { if (!isset($identifier)) { - $identifier = ip_address(); + $identifier = $this->request->getClientIp(); } $this->connection->delete('flood') ->condition('event', $name) @@ -67,7 +78,7 @@ public function clear($name, $identifier = NULL) { */ public function isAllowed($name, $threshold, $window = 3600, $identifier = NULL) { if (!isset($identifier)) { - $identifier = ip_address(); + $identifier = $this->request->getClientIp(); } $number = $this->connection->select('flood', 'f') ->condition('event', $name) diff --git a/core/lib/Drupal/Core/Flood/MemoryBackend.php b/core/lib/Drupal/Core/Flood/MemoryBackend.php index 63b19da..1436e43 100644 --- a/core/lib/Drupal/Core/Flood/MemoryBackend.php +++ b/core/lib/Drupal/Core/Flood/MemoryBackend.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Flood; +use Symfony\Component\HttpFoundation\Request; + /** * Defines the memory flood backend. This is used for testing. */ @@ -18,11 +20,28 @@ class MemoryBackend implements FloodInterface { protected $events = array(); /** + * A request object. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; + + /** + * Construct the MemoryBackend. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The HttpRequest object representing the current request. + */ + public function __construct(Request $request) { + $this->request = $request; + } + + /** * Implements Drupal\Core\Flood\FloodInterface::register(). */ public function register($name, $window = 3600, $identifier = NULL) { if (!isset($identifier)) { - $identifier = ip_address(); + $identifier = $this->request->getClientIp(); } // We can't use REQUEST_TIME here, because that would not guarantee // uniqueness. @@ -35,7 +54,7 @@ public function register($name, $window = 3600, $identifier = NULL) { */ public function clear($name, $identifier = NULL) { if (!isset($identifier)) { - $identifier = ip_address(); + $identifier = $this->request->getClientIp(); } unset($this->events[$name][$identifier]); } @@ -45,7 +64,7 @@ public function clear($name, $identifier = NULL) { */ public function isAllowed($name, $threshold, $window = 3600, $identifier = NULL) { if (!isset($identifier)) { - $identifier = ip_address(); + $identifier = $this->request->getClientIp(); } $limit = microtime(true) - $window; $number = count(array_filter($this->events[$name][$identifier], function ($timestamp) use ($limit) { diff --git a/core/modules/action/tests/action_loop_test/action_loop_test.module b/core/modules/action/tests/action_loop_test/action_loop_test.module index 8c3b98d..c953ef4 100644 --- a/core/modules/action/tests/action_loop_test/action_loop_test.module +++ b/core/modules/action/tests/action_loop_test/action_loop_test.module @@ -69,7 +69,7 @@ function watchdog_skip_semaphore($type, $message, $variables = array(), $severit 'uid' => isset($user->uid) ? $user->uid : 0, 'request_uri' => $base_root . request_uri(), 'referer' => $_SERVER['HTTP_REFERER'], - 'ip' => ip_address(), + 'ip' => drupal_container()->get('request')->getClientIp(), 'timestamp' => REQUEST_TIME, ); diff --git a/core/modules/ban/ban.admin.inc b/core/modules/ban/ban.admin.inc index 0ea23fc..f5e699c 100644 --- a/core/modules/ban/ban.admin.inc +++ b/core/modules/ban/ban.admin.inc @@ -85,7 +85,7 @@ function ban_ip_form_validate($form, &$form_state) { if (db_query("SELECT * FROM {ban_ip} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) { form_set_error('ip', t('This IP address is already banned.')); } - elseif ($ip == ip_address()) { + elseif ($ip == drupal_container()->get('request')->getClientIp()) { form_set_error('ip', t('You may not ban your own IP address.')); } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) { diff --git a/core/modules/ban/lib/Drupal/ban/EventSubscriber/BanSubscriber.php b/core/modules/ban/lib/Drupal/ban/EventSubscriber/BanSubscriber.php index 7dc141c..8d9f2ad 100644 --- a/core/modules/ban/lib/Drupal/ban/EventSubscriber/BanSubscriber.php +++ b/core/modules/ban/lib/Drupal/ban/EventSubscriber/BanSubscriber.php @@ -43,8 +43,7 @@ public function __construct(BanIpManager $manager) { * The Event to process. */ public function onKernelRequestBannedIpCheck(GetResponseEvent $event) { - // @todo convert this to Request::getClientIP(). - $ip = ip_address(); + $ip = $event->getRequest()->getClientIp(); if ($this->manager->isDenied($ip)) { $response = new Response('Sorry, ' . check_plain($ip) . ' has been banned.', 403); $event->setResponse($response); diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php index 6851be0..99dda2d 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php @@ -153,7 +153,7 @@ protected function preSave(EntityInterface $comment) { } // Add the values which aren't passed into the function. $comment->thread->value = $thread; - $comment->hostname->value = ip_address(); + $comment->hostname->value = drupal_container()->get('request')->getClientIp(); } } diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php index 91cab48..f1f7f82 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksTest.php @@ -134,7 +134,7 @@ function setEnvironment(array $info) { 'uid' => 0, 'status' => COMMENT_PUBLISHED, 'subject' => $this->randomName(), - 'hostname' => ip_address(), + 'hostname' => drupal_container()->get('request')->getClientIp(), 'langcode' => LANGUAGE_NOT_SPECIFIED, 'comment_body' => array(LANGUAGE_NOT_SPECIFIED => array($this->randomName())), )); diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentNewIndicatorTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentNewIndicatorTest.php index 324012e..d94674c 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentNewIndicatorTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentNewIndicatorTest.php @@ -43,7 +43,7 @@ public function testCommentNewCommentsIndicator() { 'uid' => $this->loggedInUser->uid, 'status' => COMMENT_PUBLISHED, 'subject' => $this->randomName(), - 'hostname' => ip_address(), + 'hostname' => drupal_container()->get('request')->getClientIp(), 'langcode' => LANGUAGE_NOT_SPECIFIED, 'comment_body' => array(LANGUAGE_NOT_SPECIFIED => array($this->randomName())), )); diff --git a/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php b/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php index 1769605..c8ec39b 100644 --- a/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php +++ b/core/modules/dblog/lib/Drupal/dblog/Tests/DBLogTest.php @@ -137,7 +137,7 @@ private function generateLogEntries($count, $type = 'custom', $severity = WATCHD 'uid' => isset($this->big_user->uid) ? $this->big_user->uid : 0, 'request_uri' => $base_root . request_uri(), 'referer' => $_SERVER['HTTP_REFERER'], - 'ip' => ip_address(), + 'ip' => drupal_container()->get('request')->getClientIp(), 'timestamp' => REQUEST_TIME, ); $message = 'Log entry added to test the dblog row limit. Entry #'; @@ -424,7 +424,7 @@ protected function testDBLogAddAndClear() { 'uid' => isset($this->big_user->uid) ? $this->big_user->uid : 0, 'request_uri' => $base_root . request_uri(), 'referer' => $_SERVER['HTTP_REFERER'], - 'ip' => ip_address(), + 'ip' => drupal_container()->get('request')->getClientIp(), 'timestamp' => REQUEST_TIME, ); // Add a watchdog entry. diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module index 6d31124..3c0366e 100644 --- a/core/modules/openid/openid.module +++ b/core/modules/openid/openid.module @@ -1066,7 +1066,7 @@ function openid_verify_assertion_nonce($service, $response) { return TRUE; } else { - watchdog('openid', 'Nonce replay attempt blocked from @ip, nonce: @nonce.', array('@ip' => ip_address(), '@nonce' => $response['openid.response_nonce']), WATCHDOG_CRITICAL); + watchdog('openid', 'Nonce replay attempt blocked from @ip, nonce: @nonce.', array('@ip' => drupal_container()->get('request')->getClientIp(), '@nonce' => $response['openid.response_nonce']), WATCHDOG_CRITICAL); return FALSE; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/IpAddressTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/IpAddressTest.php deleted file mode 100644 index c062aa6..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/IpAddressTest.php +++ /dev/null @@ -1,111 +0,0 @@ - 'IP address and HTTP_HOST test', - 'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.', - 'group' => 'Bootstrap' - ); - } - - function setUp() { - $this->oldserver = $_SERVER; - - $this->remote_ip = '127.0.0.1'; - $this->proxy_ip = '127.0.0.2'; - $this->proxy2_ip = '127.0.0.3'; - $this->forwarded_ip = '127.0.0.4'; - $this->cluster_ip = '127.0.0.5'; - $this->untrusted_ip = '0.0.0.0'; - - drupal_static_reset('ip_address'); - - $_SERVER['REMOTE_ADDR'] = $this->remote_ip; - unset($_SERVER['HTTP_X_FORWARDED_FOR']); - unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']); - - parent::setUp(); - } - - function tearDown() { - $_SERVER = $this->oldserver; - drupal_static_reset('ip_address'); - parent::tearDown(); - } - - /** - * Tests IP address and hostname. - */ - function testIPAddressHost() { - // Test the normal IP address. - $this->assertTrue( - ip_address() == $this->remote_ip, - 'Got remote IP address.' - ); - - // Proxy forwarding on but no proxy addresses defined. - $this->settingsSet('reverse_proxy', 1); - $this->assertTrue( - ip_address() == $this->remote_ip, - 'Proxy forwarding without trusted proxies got remote IP address.' - ); - - // Proxy forwarding on and proxy address not trusted. - $this->settingsSet('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip)); - drupal_static_reset('ip_address'); - $_SERVER['REMOTE_ADDR'] = $this->untrusted_ip; - $this->assertTrue( - ip_address() == $this->untrusted_ip, - 'Proxy forwarding with untrusted proxy got remote IP address.' - ); - - // Proxy forwarding on and proxy address trusted. - $_SERVER['REMOTE_ADDR'] = $this->proxy_ip; - $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip; - drupal_static_reset('ip_address'); - $this->assertTrue( - ip_address() == $this->forwarded_ip, - 'Proxy forwarding with trusted proxy got forwarded IP address.' - ); - - // Multi-tier architecture with comma separated values in header. - $_SERVER['REMOTE_ADDR'] = $this->proxy_ip; - $_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip)); - drupal_static_reset('ip_address'); - $this->assertTrue( - ip_address() == $this->forwarded_ip, - 'Proxy forwarding with trusted 2-tier proxy got forwarded IP address.' - ); - - // Custom client-IP header. - $this->settingsSet('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP'); - $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip; - drupal_static_reset('ip_address'); - $this->assertTrue( - ip_address() == $this->cluster_ip, - 'Cluster environment got cluster client IP.' - ); - - // Verifies that drupal_valid_http_host() prevents invalid characters. - $this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), 'HTTP_HOST with / is invalid'); - $this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), 'HTTP_HOST with \\ is invalid'); - $this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), 'HTTP_HOST with < is invalid'); - $this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), 'HTTP_HOST with .. is invalid'); - // IPv6 loopback address - $this->assertTrue(drupal_valid_http_host('[::1]:80'), 'HTTP_HOST containing IPv6 loopback is valid'); - } -} diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 6a9fb78..91527b6 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1343,7 +1343,7 @@ function user_login_authenticate_validate($form, &$form_state) { // The default identifier is a combination of uid and IP address. This // is less secure but more resistant to denial-of-service attacks that // could lock out all users with public user names. - $identifier = $account->uid . '-' . ip_address(); + $identifier = $account->uid . '-' . drupal_container()->get('request')->getClientIp(); } $form_state['flood_control_user_identifier'] = $identifier;