技术解析

nginx 获取真实 IP 的疑问,以及 nginx lua redis 防 CC 针对部分 IP 不起作用的问题
0
2021-05-21 03:57:18
idczone

服务器是公网 IP,单机所以没有用反向代理 是不是这种情况下,$remote_addr 就会始终是真实 IP 呢?

我额外设置了

fastcgi_param HTTP_X_FORWARDED_FOR  $remote_addr;

这样就能保证$remote_addr 始终是用户的真实 IP 呢?

我在这样的配置下,参考下面的文章 配置了 redis 和 lua https://blog.linuxeye.cn/453.html

然后修改了其中的 IP 部分为

function getClientIp()
   IP  = ngx.var.remote_addr
    return IP
end

然后检测 redis,发现可以正确生成黑白名单数据。但是,检查 php 的 log。发现依然会出现部分 Ip 的高频访问,这些 IP 没有被加入黑名单。比如下面这个 ip,几个小时内访问了 60W 次,基本上每秒 100 次请求。而没有被加入黑名单

61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /抗投诉服务器index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"

有没有大神帮忙分析下是哪里配置有错误呢


```
local function getClientIp()
local headers = ngx.req.get_headers()
if not headers['x-forwarded-for'] then
realIp = ngx.var.remote_addr or '127.0.0.1'
return realIp
end
if type(headers['x-forwarded-for']) == "table" then
realIp = headers['x-forwarded-for'][1]
else
realIp = headers['x-forwarded-for']
end
return realIp
end
```

服务器设置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
之后取 X-Forwarded-For 的第一个段值 可以拿到真实 ip

感谢回复
但是不起作用的 IP,从 log 上看并没有出现 x_forwarded_for。所以我不是很明白为什么 lua 没有起作用呢

今天看 log,又出现了
61.151.186.154 - - [15/Jun/2017:01:30:00 +0800] "GET / HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.154 - - [15/Jun/2017:01:30:00 +0800] "GET / HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
访问次数 61.151.186.154 次
但是看 redis,完全没有记录。。
redis /tmp/redis.sock> keys 61.151.186
(empty list or set)
非常奇怪

那你完整的代码贴出来看看

代码如下
local get_headers = ngx.req.get_headers
local ua = ngx.var.http_user_agent
local uri = ngx.var.request_uri
local url = ngx.var.host .. uri
local redis = require 'redis'
local red = redis.new()
local CCcount = 100
local CCseconds = 30
local blackseconds = 7200
if ua == nil then
ua = "unknown"
end
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1 sec
local ok, err = red:connect("localhost")
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, err = red:auth("password1")
if not res then
ngx.say("failed to authenticate: ", err)
return
end
if ok then
function getClientIp()
IP = ngx.var.remote_addr
--[[
IP = ngx.req.get_headers()["X-Real-IP"]
if IP == nil then
IP = ngx.req.get_headers()["x_forwarded_for"]
end
if IP == nil then
IP = ngx.var.remote_addr
end
if IP == nil then
IP = "unknown"
end
--]]
return IP
end
local token = getClientIp() .. "." .. ngx.md5(url .. ua)
local req = red:exists(token)
if req == 0 then
red:incr(token)
red:expire(token,CCseconds)
else
local times = tonumber(red:get(token))
if times >= CCcount then
local blackReq = red:exists("black." .. token)
if (blackReq == 0) then
red:set("black." .. token,1)
red:expire("black." .. token,blackseconds)
red:expire(token,blackseconds)
ngx.exit(503)
else
ngx.exit(503)
end
return
else
red:incr(token)
end
end
return
end
-- put it into the connection pool of size 100,
-- with 10 seconds max idle time
local ok, err = red:set_keepalive(10000, 1000)
if not ok then
return
end

你代码里 存入 redis 的 key 不是 IP 啊,key 是 token = getClientIp() .. "." .. ngx.md5(url .. ua) ,直接取 IP 当然是空

还有最好把 IP 作为 key,如果按照你代码写的用 token,那么攻击者可以不停的更换 url 来达到攻击的目的

不是啊。查看是不是屏蔽也是查看的这个 token 啊。查看 redis 也是有被屏蔽的
1) "black.202.141.176.9.7c879b2da20d1694c748b1a70b8d9281"
2) "black.1.180.215.60.9a394cf0c48fba4606c9757d3d8527c5"
采用这个 token 也是经过考虑的。如果单纯用 IP,很容易把一些通过代理或者共用 IP 的地址封掉。所以只能采用 IP+URL+浏览器来计算

你 27 天前的那个回复取的是 IP。。。 计数器可以用 ngx_lua 的内存字典,然后再放入 redis

其实是一样的啊,利用 token 来屏蔽 IP
现在的问题就是像我主楼发的一样,有的 IP 大量访问同样的 URL (几十万次),居然没被屏蔽。这就是我没搞明白的问题。。。

日能说能说一切

日志能说明一切

解决了没有? nginx 的其他配置里面,有没有 if 判断,if 很容易导致各种各样的问题。

fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
这个配置是不是很多余?

不多于的,防止伪造 IP 啊

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服