jasny / forwarded-middleware
处理PSR-7请求的Forwarded头部的服务器中间件
Requires
- php: >=7.2.0
- psr/http-message: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- jasny/php-code-quality: ^2.5
Suggests
- wikimedia/ip-set: Check if IP is in a trusted CIDR block
This package is auto-updated.
Last update: 2024-08-29 05:18:36 UTC
README
服务器中间件用于处理PSR-7请求的Forwarded
头部。同时作为PSR-15和双次传递中间件使用。
中间件设置服务器请求的client_ip
和original_url
属性。还支持非标准的X-Forwarded-*
和其他自定义头部。
安装
composer require jasny/forwarded-middleware
用法
use Wikimedia\IPSet; use Jasny\Forwarded; use Zend\Stratigility\MiddlewarePipe; use Zend\Diactoros\ResponseFactory; $trustIps = new IPSet(['208.80.154.0/26', '2620:0:861:1::/64', '10.64.0.0/22']); $middleware = new Forwarded\Middleware(function (string $ip, array $forward) use ($trustedIps) { return $trustedIps->match($ip); }); $app = new MiddlewarePipe(); $app->pipe($middleware);
受信任的代理
构造函数仅接受一个回调参数,该回调用于每个在Forwarded
头部中指定的转发(由逗号分隔)。每个转发应有一个for
指令,并可能包含其他指令,如port
和proto
。
回调的第一个参数是代理服务器的IP。第二个参数包含此转发的指令,作为关联数组。
IP的初始值来自REMOTE_ADDR
服务器参数。对于后续的每次调用,都给出前一个受信任转发的for
指令的值。
示例
Forwarded: for=75.84.3.2,for=92.53.34.1,for=32.5.86.102,for=10.64.0.23 Client IP (REMOTE_ADDR) is 208.80.154.8
这将导致以下调用
fn("208.80.154.8", ['for' => "10.64.0.23"]); // true fn("10.64.0.23", ['for' => "32.5.86.102"]); // true fn("32.5.86.102", ['for' => "92.53.34.1"]); // false
client_id
设置为"32.5.86.102"
。请注意,for=75.84.3.2
的转发不被考虑。
不一定要基于IP信任。作为替代,您可以检查代理是否设置了秘密。
$middleware = new Forwarded\Middleware(function (string $ip, array $forward) { return $forward['secret'] === getenv('PROXY_SECRET'); });
原始uri
除了client_ip
之外,中间件还会设置original_uri
属性。此属性是基于请求URI的PSR-7 URI对象。
proto
和host
,以及非标准的path
和port
指令被应用于创建原始uri。如果port
是proto
的标准端口(对于"http"是80,对于"https"是443),则省略。
仅使用最后一个受信任代理的指令;
示例
HTTP/1.1 GET /foo Host: x9.example.com Forwarded: for=92.53.34.1, for=32.5.86.102;proto=https;port=443;host=example.com;path=/x/foo, for=10.64.0.23;proto=http;port=8080
original_uri
属性将是"https://example.com/x/foo"
服务器请求的URI不会被修改。
非标准头部
如果您的代理设置了X-Forwarded-*
头部,请使用CompatMiddleware
。此中间件将这些头部转换为Forwarded
头部。
use Wikimedia\IPSet; use Jasny\Forwarded; use Zend\Stratigility\MiddlewarePipe; use Zend\Diactoros\ResponseFactory; $trustIps = new IPSet(['208.80.154.0/26', '2620:0:861:1::/64', '10.64.0.0/22']); $compatMiddleware = new Forwarded\CompatMiddleware(); $middleware = new Forwarded\Middleware(function (string $ip, array $forward) use ($trustedIps) { return $trustedIps->match($ip); }); $app = new MiddlewarePipe(); $app->pipe($compatMiddleware); $app->pipe($middleware);
默认情况下,兼容中间件使用以下头部
X-Forwared-For
X-Forwarded-Proto
X-Forwarded-Host
自定义头部
如果您的代理使用其他头部,您可以传递自定义映射给构造函数
$compatMiddleware = new Forwarded\CompatMiddleware([ 'X-Client-IP' => 'for, 'X-Forwarded-For' => 'for', 'X-Forwarded-Proto' => 'proto', 'X-Forwarded-Port' => 'port', ]);
如果有多个头部具有相同的指令,则使用找到的第一个头部。在上面的例子中,如果有X-Client-IP
,则不使用X-Forwarded-For
头部。
兼容中间件支持将任何映射到for
指令的头部使用多个条目。其他指令应用于第一个条目。如果X-Forwarded-For
包含受信任和不信任代理的条目,这可能不会按预期工作。
兼容中间件将始终替换或删除现有的Forwarded
头部。通常,代理要么设置Forwarded
头部,要么使用非标准头部。允许两者都可能导致安全问题。
双次传递中间件
某些PHP库支持双次传递中间件而不是PSR-15,并使用以下签名的可调用对象;
fn(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface
要获取用于Jasny Router和Relay v1的回调,请使用asDoublePass()
方法。
use Jasny\Forwarded; use Relay\RelayBuilder; $middleware = new Forwarded\Middleware(function ($ip, $forward) { /* ... */ }); $relayBuilder = new RelayBuilder($resolver); $relay = $relayBuilder->newInstance([ $middleware->asDoublePass(), ]); $response = $relay($request, $baseResponse);
CompatMiddleware
还有一个 asDoublePass()
方法。