1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
| -- core.Debug("Starting Redis connection test")
-- core.Debug("Lua version: " .. _VERSION)
--
local REDIS = {
debug = true,
socket_path = "/wwwFS.in/u98/unix.redis.sock",
password = "your_secure_password_here1",
database = 7
}
local result
local captcha_session
local captcha_auth
-- Binary-safe Redis protocol formatter
local function format_command(args)
local parts = {"*" .. #args .. "\r\n"}
for _, arg in ipairs(args) do
parts[#parts+1] = "$" .. #arg .. "\r\n" .. arg .. "\r\n"
end
return table.concat(parts)
end
-- Custom line reader (since receive_line doesn't exist)
local function read_line(sock)
local line = ""
while true do
local char = sock:receive(1)
if not char then return nil end
line = line .. char
if #line >= 2 and line:sub(-2) == "\r\n" then
return line
end
end
end
local function readAndCmpFromRedis()
result = "" ;
-- 1. Establish connection (fail fast)
local sock = core.tcp()
if not sock or not sock:connect("unix@" .. REDIS.socket_path) then
if debug then core.Debug("redisRead " .. "connect_fail") end
return false
end
-- 2. Pipeline all commands
local cmds = {
format_command({"AUTH", REDIS.password}),
format_command({"SELECT", tostring(REDIS.database)}),
--format_command({"HGET", "captcha:"..(txn.sf:req_fhdr("X-Captcha-Token") or ""), "auth"})
--format_command({"HGET", "captcha:".."634437e5c98742a488429ed20a29aaad", "auth"})
format_command({"HGET", "captcha:".. captcha_session, "auth"})
}
if not sock:send(table.concat(cmds)) then
sock:close()
if debug then core.Debug("redisRead " .. "send_fail") end
return false
end
-- 3. Validate responses sequentially
-- AUTH response (+OK\r\n)
local auth_resp = sock:receive(5) -- Fixed length for simple string
if auth_resp ~= "+OK\r\n" then
sock:close()
if debug then core.Debug("redisRead " .. "auth_fail") end
return false
end
-- SELECT response (+OK\r\n)
local select_resp = sock:receive(5) -- Fixed length for simple string
if select_resp ~= "+OK\r\n" then
sock:close()
if debug then core.Debug("redisRead " .. "select_fail") end
return false
end
-- HGET response ($N\r\n<value>\r\n)
local hget_header = read_line(sock) -- Custom line reader
if not hget_header then
sock:close()
if debug then core.Debug("redisRead " .. "hget_header_fail") end
return false
end
core.Debug("HGET 1 :["..hget_header.."]" );
local value_len = tonumber(string.match(hget_header, "^%$(.-)\r\n")) or -1
local value = ""
if value_len > 0 then
value = sock:receive(value_len)
sock:receive(2) -- Consume trailing \r\n
if value and value == captcha_auth then
-- Send EXPIREAT command for the captcha session (7200 seconds from now)
local expire_time = os.time() + 7200
local expire_cmd = format_command({"EXPIREAT", "captcha:"..captcha_session, tostring(expire_time)})
if sock:send(expire_cmd) then
local expire_resp = read_line(sock)
if expire_resp and expire_resp:match("^:1\r\n") then
if debug then core.Debug("EXPIREAT succeeded for captcha:"..captcha_session) end
else
if debug then core.Debug("EXPIREAT failed for captcha:"..captcha_session) end
end
else
if debug then core.Debug("Failed to send EXPIREAT") end
end
end
elseif value_len == 0 then
sock:receive(2) -- Consume $\r\n\r\n for empty bulk
end
core.Debug("HGET 1 :["..value.."]" );
result = value ;
sock:close()
if debug then core.Debug("redisRead " .. "success") end
return true
end
function verify_captcha_auth(txn)
-- Get cookies from the request
captcha_session = txn.f:req_cook("captcha_session")
captcha_auth = txn.f:req_cook("captcha_auth")
-- Log cookie values for debugging
if debug then core.Debug("CAPTCHA Session: " .. (captcha_session or "nil")) end
if debug then core.Debug("CAPTCHA Auth: " .. (captcha_auth or "nil")) end
--if debug then core.Debug("HAProxy version: " .. core.info.version) end
--if debug then core.Debug("Lua version: " .. _VERSION) end
-- If either cookie is missing, return false
if not captcha_session or not captcha_auth then
if debug then core.Debug("Missing required cookies") end
txn.set_var(txn, "txn.captcha_auth_valid", false)
return
end
if not readAndCmpFromRedis( ) then
if debug then core.Debug("readAndCmpFromRedis error") end
txn.set_var(txn, "txn.captcha_auth_valid", false)
return
end
if debug then core.Debug("Redis auth value: [" .. (result or "nil").."]") end
-- Compare stored auth with cookie auth
if result and result == captcha_auth then
-- Auth is valid
if debug then core.Debug("Auth validation successful") end
txn.set_var(txn, "txn.captcha_auth_valid", true)
else
-- Auth is invalid
if debug then core.Debug("Auth validation failed") end
txn.set_var(txn, "txn.captcha_auth_valid", false)
end
end
-- Register the action
core.register_action("verify_captcha_auth", {"http-req"}, verify_captcha_auth)
|