在使用 Cloudflare 时依靠 "X-Forwarded-For" 来限制 Apache 中的 IP 访问是否安全?

Is it secure to rely on "X-Forwarded-For" to restrict access by IP in Apache while using Cloudflare?

我使用这样的 .htaccess 文件将对目录的访问限制为某些 IP 地址:

AuthType Basic
AuthName "Protected"

<RequireAny>
    Require ip 1.2.3.4
</RequireAny>

这在正常的服务器设置中工作正常,但是当使用 Cloudflare 作为 WAF 代理时,它停止工作,因为服务器接收通过 Cloudflare 的 IP 代理的所有请求。

作为一种解决方法,可以使用 "X-Forwarded-For" header 来识别客户端的 "real" IP 地址,因为 Cloudflare 会在其所有请求中传递此地址:

AuthType Basic
AuthName "Protected"

SetEnvIf X-Forwarded-For 1.2.3.4$ allowed

<RequireAny>
    Require env allowed
</RequireAny>

这是一种安全的方法还是有一种better/more安全的方法来限制使用 Cloudflare 时 Apache 中客户端 IP 的访问?

根据IETF RFC 2616, Section 4.2, a header can hold a comma separated list of values, and this is the case of X-Forwarded-For as Cloudflare uses it

If an X-Forwarded-For header was already present in the request to Cloudflare, Cloudflare appends the IP address of the HTTP proxy to the header:

Example: X-Forwarded-For: 203.0.113.1,198.51.100.101,198.51.100.102 

In the examples above, 203.0.113.1 is the original visitor IP address and 198.51.100.101 and 198.51.100.102 are proxy server IP addresses provided to Cloudflare via the X-Forwarded-For header.

习惯上取最左边的IP为真实IP,但并非总是如此。

如果你这样做,你应该检查与你的 IP 匹配的正则表达式

SetEnvIf X-Forwarded-For ^1\.2\.3\.4 allowed

(最左边的IP,转义点)

更好的方法(恕我直言)

Cloudflare 还发送 header cf-connecting-ip(这意味着在发送到您的计算机之前最后一个访问 cloudflare 的 IP)我宁愿使用那个。

Is this a safe approach or is there a better/more secure way to limit access by client IP in Apache when Cloudflare is being used?

有个问题。在这种情况下,您要告诉 Apache:

"we have cloudflare in the middle so instead of your native way to tell the visitor's IP let's look at this custom header".

那个习俗header是可以伪造的。绝对地。因此,你也应该说:

"this custom header should be taken as reliable if and only the request comes from a Cloudflare IP".

Cloudflare does explicitly list their IP ranges

最后,您应该使用 mod_remoteip 而不是手动构建 SetEnvIf 规则。

基本上:

# /etc/apache2/conf-enabled/remoteip.conf
RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 103.21.244.0/22
...
RemoteIPTrustedProxy 2606:4700::/32
RemoteIPTrustedProxy 2803:f800::/32

或者,将 IP 列表放在单独的文件中:

# /etc/apache2/conf-enabled/remoteip.conf
RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxyList conf/trusted-proxies.lst

# conf/trusted-proxies.lst
173.245.48.0/20
103.21.244.0/22
...
...
2803:f800::/32
2606:4700::/32

如果说 header 没有出现在请求中,Apache 回退到 ro REMOTE_ADDR。来自不受信任的 IP 的请求也是如此。如果 header 存在并且来自 Cloudflare,您只需执行以下操作:

Require ip 1.2.3.4

这种方法会在您需要使用它的任何地方(日志、身份验证等)替换 IP,并在边缘情况下优雅地退回到原始 REMOTE_ADDR。