Giter VIP home page Giter VIP logo

ip-address-middleware's Introduction

Client IP address middleware

PSR-15 Middleware that determines the client IP address and stores it as an ServerRequest attribute called ip_address. It optionally checks various common proxy headers and then falls back to $_SERVER['REMOTE_ADDR'].

Build status

Configuration

The constructor takes 4 parameters which can be used to configure this middleware.

Check proxy headers

Note that the proxy headers are only checked if the first parameter to the constructor is set to true. If it is set to false, then only $_SERVER['REMOTE_ADDR'] is used.

Trusted Proxies

If you configure to check the proxy headers (first parameter is true), you have to provide an array of trusted proxies as the second parameter. When the array is empty, the proxy headers will always be evaluated which is not recommended. If the array is not empty, it must contain strings with IP addresses (wildcard * is allowed in any given part) or networks in CIDR-notation. One of them must match the $_SERVER['REMOTE_ADDR'] variable in order to allow evaluating the proxy headers - otherwise the REMOTE_ADDR itself is returned.

Attribute name

By default, the name of the attribute is 'ip_address'. This can be changed by the third constructor parameter.

Headers to inspect

By default, this middleware checks the 'Forwarded', 'X-Forwarded-For', 'X-Forwarded', 'X-Cluster-Client-Ip' and 'Client-Ip' headers. You can replace this list with your own using the fourth constructor parameter.

If you use the nginx, set_real_ip_from directive, then you should probably set this to:

$headersToInspect = [
    'X-Real-IP',
    'Forwarded',
    'X-Forwarded-For',
    'X-Forwarded',
    'X-Cluster-Client-Ip',
    'Client-Ip',
];

If you use CloudFlare, then according to the documentation you should probably set this to:

$headersToInspect = [
    'CF-Connecting-IP',
    'True-Client-IP',
    'Forwarded',
    'X-Forwarded-For',
    'X-Forwarded',
    'X-Cluster-Client-Ip',
    'Client-Ip',
];

Security considerations

A malicious client may send any header to your proxy, including any proxy headers, containing any IP address. If your proxy simply adds another IP address to the header, an attacker can send a fake IP. Make sure to setup your proxy in a way that removes any sent (and possibly faked) headers from the original request and replaces them with correct values (i.e. the currently used REMOTE_ADDR on the proxy server).

This library cannot by design ensure you get correct and trustworthy results if your network environment isn't setup properly.

Installation

composer require akrabat/ip-address-middleware

In Mezzio, copy Mezzio/config/ip_address.global.php.dist into your Mezzio Application config/autoload directory as ip_address.global.php

Usage

In Slim 3:

$checkProxyHeaders = true; // Note: Never trust the IP address for security processes!
$trustedProxies = ['10.0.0.1', '10.0.0.2']; // Note: Never trust the IP address for security processes!
$app->add(new RKA\Middleware\IpAddress($checkProxyHeaders, $trustedProxies));

$app->get('/', function ($request, $response, $args) {
    $ipAddress = $request->getAttribute('ip_address');

    return $response;
});

In Laminas or Mezzio, add to your pipeline.php config at the correct stage, usually just before the DispatchMiddleware:

# config/pipeline.php
# using default config
$app->add(RKA\Middleware\IpAddress::class);

If required, update your .env file with the environmental variables found in /config/autoload/ip_address.global.php.

Testing

  • Code style: $ vendor/bin/phpcs
  • Unit tests: $ vendor/bin/phpunit
  • Code coverage: $ vendor/bin/phpunit --coverage-html ./build

ip-address-middleware's People

Contributors

acelaya avatar aiglesiasn avatar akrabat avatar bafs avatar batumibiz avatar elisdn avatar gboily avatar geeh avatar harikt avatar homeyjd avatar kudashevs avatar pascalheidmann-check24 avatar pmous avatar satalaondrej avatar seagull-4auka avatar svenrtbg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ip-address-middleware's Issues

Capturing any IP

Maybe I am missing the point. But my goal is to capture any IP from any request made to my rest api so I can keep track of them in a database.

I am using Slim 3 and when I use composer to load the middleware I use it like this and it always returns null from my localhost.

require '../vendor/autoload.php';

$app->get('/api/v1/customer', function (Request $request, Response $response) {
    $requestIP = $request->getAttribute('ip_address');
     var_dump($requestIP);
     echo $requestIP;

    return $response;
});

From my understanding this is options if you only want to accept request from certain IP,

$checkProxyHeaders = true; // Note: Never trust the IP address for security processes!
$trustedProxies = ['10.0.0.1', '10.0.0.2']; // Note: Never trust the IP address for security processes!
$app->add(new RKA\Middleware\IpAddress($checkProxyHeaders, $trustedProxies));

When I add that into my app the output is this,

string(3) "::1" ::1

If I am trying to capture the IP from the request is that $trustedProxies optional? If it is not optional how can I accept request from all IP?

To achieve my goal what am I missing here? Or am I getting the expected result and I just do not realize it?

The middleware is not working with other middleware

I'm trying to setup a set middleware to mangle requests and response on my app and I've noticed that I don't have the ip_address set on middleware following rka-ip-address-middleware.

More specifically I'm setting up a throttling middleware to limit requests per IP address per hour like bellow

class ThrottlingMiddleware {

    const CACHE_PREFIX = 'throttling_';
    const THROTTLING_LIMIT = 1000000;
    private $enabled = false;
    protected $memcache;

    public function __construct() {
        $this->memcache = new Memcache();
        $this->enabled = true;
    }

    private function _isRateExceeded($host) {
        if (!$this->memcache->get(self::CACHE_PREFIX . $host))
            return false;

        return $this->memcache->get(self::CACHE_PREFIX . $host) > self::THROTTLING_LIMIT;
    }

    private function _register($host) {
        if (!$this->memcache->get(self::CACHE_PREFIX . $host)) {
            $this->memcache->set(self::CACHE_PREFIX . $host, 0, time() + (60 * 60));
        }

        $this->memcache->increment(self::CACHE_PREFIX . $host, 1);
    }

    function __invoke(Request $request, Response $response, callable $next) {
        if (!$this->enabled)
            return $next($request, $response);

        $ip_address = $request->getAttribute('ip_address'); // **IP WAS SUPPOSED TO BE SET HERE**

        if (is_null($ip_address))
            return $next($request, $response);

        if ($this->_isRateExceeded($ip_address)) {
            return $response->withStatus(429, "Too Many Requests");
        }

        $response = $next($request, $response);

        $this->_register($ip_address);
        return $response;
    }
}

Then middleware is added to the app like

$checkProxyHeaders = true;
$trustedProxies = ['10.0.0.1', '10.0.0.2'];
$app->add(new RKA\Middleware\IpAddress($checkProxyHeaders, $trustedProxies));
$app->add(new ThrottlingMiddleware());

Given the above I think IpAddress is supposed to run first and thus set the ip address on the request which is received by the next middleware in the queue.

Please clarify if the assumption above is correct or not, or provide advise on how I can handle this case.

AWS Application load balancer has random IP addresses

Hi Guys,

AWS doesn't specify the private IP address for my application load balancer. I think from what I've read online the number of addresses can change as the load balancer scales. So essentially I need to be able to check the proxy headers without the trusted proxies list and/or use a netmask? I can do the work to add this feature if you think this change is acceptable?

Fails with Azure Application Gateway / LB

This middleware seems to fail in an environment behind an Azure application gateway.

I can confirm that the client ip is available in the request headers under X_FORWARDED_FOR, however, this middleware always returns the REMOTE_ADDR, which is some internal IP in Azure.

I've set $checkProxyHeaders to true -- i've left the $trustedProxies array both empty, and filled with every possible combination of ips/domains that I could find in the request headers -- can't seem to get it to return the client's IP address.

Any idea what could be going on here?

Checks only the first trustedWildcard

foreach ($this->trustedWildcard as $proxy) {
if (count($proxy) !== $parts) {
continue; // IP version does not match
}
foreach ($proxy as $i => $part) {
if ($part !== '*' && $part !== $ipAddrParts[$i]) {
break 2;// IP does not match, move to next proxy
}
}
$checkProxyHeaders = true;
break;
}

If first proxy from trustedWildcard does not match, break 2; exits two cycles instead of one. Thus ignoring the rest of the wildcard list

Using middleware with "checkProxyHeaders" but without "trustedProxies" does not raise a warning

We used to use version 0.4 from the old repo, and upgraded to 0.6 and eventually 1.0 of this repo.

In the 0.6 release, there was a change that now forces the user to provide an IP list of "trustedProxies", otherwise the determined IP address would always be the "REMOTE_ADDR". Before, one could determine the supposed remote address without a list.

This effectively makes it useless to allow "checkProxyHeaders = true" with "trustedProxies = []". It will silently be switched back to "checkProxyHeaders = false" without any notice or warning.

This would force us to have a list of all trusted proxies of our DEV, TEST and PRODUCTION environment, and also reveal all these numbers in every other environment, or juggle with different configurations. Nobody should be able to sneak in from anywhere, which is guaranteed by our infrastructure setup. Effectively, we do not need to configure trusted proxies, we are fine with determining the IP from the headers as-is.

Would you consider reverting that change?

second Parameter of __invoke should be RequestHandlerInterface instance

public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)`
    {
        if (!$next) {
            return $response;
        }

        $ipAddress = $this->determineClientIpAddress($request);
        $request = $request->withAttribute($this->attributeName, $ipAddress);

        return $response = $next($request, $response);
  }

I got the error exception.

[FATAL] RKA\Middleware\IpAddress::__invoke(): Argument # 2 ($response) must be of type Psr\Http\Message\ResponseInterface, Psr\Http\Server\RequestHandlerInterface@anonymous given, called in /vendor/slim/slim/Slim/MiddlewareDispatcher.php on line 168

Plugin does not seem to work with current Slim version

Hi,

the usage example you are giving does not seem to work with current Slim 3 versions.

The line:

$app->add(new RKA\Middleware\IpAddress($checkProxyHeaders, $trustedProxies));

produces the following error:
Argument 1 passed to Slim\Slim::add() must be an instance of Slim\Middleware, instance of RKA\Middleware\IpAddress given

What can I do to use your middleware anyway?

Thank you very much
Kind regards,
Tom

PHP 8 support

Hey!

Would you consider adding PHP 8 support? If so, I'm open for the contribution.

Doesn't work on AWS using the request object to get headers

The current code uses the request object to get the headers.
On AWS Elastic Beanstalk for example the request headers are:
Host: subdomain.domain.com
HTTP_ACCEPT: application/json
HTTP_ACCEPT_ENCODING: gzip, deflate
HTTP_CACHE_CONTROL: no-cache
CONTENT_TYPE: application/json
HTTP_POSTMAN_TOKEN: f37b1651-c782-4f29-9f00-e9e1f074a257
HTTP_USER_AGENT: PostmanRuntime/3.0.11-hotfix.2
HTTP_X_FORWARDED_FOR: 72.206.90.181
HTTP_X_FORWARDED_PORT: 80
HTTP_X_FORWARDED_PROTO: http
HTTP_CONNECTION: keep-alive

The change I employed was to use the PHP function getallheaders() which provides these headers:
host: subdomain.domain .com
Accept: application/json
accept-encoding: gzip, deflate
cache-control: no-cache
Content-Type: application/json
Postman-Token: 0facf6eb-9d64-4d2f-a9d7-3eaf287bf729
User-Agent: PostmanRuntime/3.0.11-hotfix.2
X-Forwarded-For: 72.206.90.181
X-Forwarded-Port: 80
X-Forwarded-Proto: http
Connection: keep-alive

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.