|
@@ -0,0 +1,146 @@
|
|
|
+local redis = require "redis"
|
|
|
+local regex = require "rex_pcre"
|
|
|
+local mime = require "mime"
|
|
|
+local cipher = require "openssl.cipher"
|
|
|
+
|
|
|
+local redis_conn = nil
|
|
|
+
|
|
|
+function scw()
|
|
|
+ -- local start_time = ngx.now()
|
|
|
+ local has_redis = false
|
|
|
+ local ignore = os.getenv("SCW_IGNORE")
|
|
|
+ local uri = ngx.var.uri
|
|
|
+ local req_uri = ngx.var.request_uri
|
|
|
+
|
|
|
+ ngx.log(ngx.INFO, "req_uri: " .. req_uri)
|
|
|
+
|
|
|
+ if ignore ~= nil and regex.match(uri, ignore) then
|
|
|
+ return -- do norhing if configured to ignore
|
|
|
+ end
|
|
|
+
|
|
|
+ -- --------------------------------------------------------------------------
|
|
|
+ -- Does the user have an encrypted scw cookie that proves he is human?
|
|
|
+ --
|
|
|
+ local cookie_name = "cookie_" .. tostring(os.getenv("SCW_COOKIE"))
|
|
|
+ if cookie_name == "cookie_" then
|
|
|
+ ngx.log(ngx.ERR, "SCW_COOKIE is not set!")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ local scw_key = os.getenv("SCW_KEY")
|
|
|
+ if scw_key == nil or scw_key == "" then
|
|
|
+ ngx.log(ngx.ERR, "SCW_KEY is not set!")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ local cookie_key = ngx.decode_base64(scw_key)
|
|
|
+ local human_cookie = ngx.var[cookie_name]
|
|
|
+
|
|
|
+ local xff = tostring(ngx.var["http_x_forwarded_for"])
|
|
|
+ local rip = tostring(ngx.var["http_x_real_ip"])
|
|
|
+
|
|
|
+
|
|
|
+ if human_cookie then
|
|
|
+ human_cookie = ngx.decode_base64(ngx.unescape_uri(human_cookie))
|
|
|
+ end
|
|
|
+ local is_human = false
|
|
|
+
|
|
|
+ if human_cookie ~= nil and cookie_key:len() == 32 then
|
|
|
+ local cookie_data = _decrypt(human_cookie, cookie_key)
|
|
|
+
|
|
|
+ local cookie_ip = string.gsub(cookie_data, "scw|(.-)|(%d+)$", "%1")
|
|
|
+ local cookie_expiry = string.gsub(cookie_data, "scw|(.-)|(%d+)$", "%2")
|
|
|
+
|
|
|
+ is_human = cookie_ip ~= "" and (cookie_ip == ngx.var.remote_addr or cookie_ip == xff or cookie_ip == rip) and ngx.now() <= tonumber(cookie_expiry)
|
|
|
+
|
|
|
+ if is_human then
|
|
|
+ return
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ -- --------------------------------------------------------------------------
|
|
|
+ -- check for blacklist status
|
|
|
+ --
|
|
|
+ if pcall(function() redis_conn:ping() end) then
|
|
|
+ has_redis = true
|
|
|
+ else
|
|
|
+ -- io.stderr:write("reconnecting to redis\n")
|
|
|
+ local redis_host = os.getenv("SCW_REDIS_HOST")
|
|
|
+ local redis_port = os.getenv("SCW_REDIS_PORT")
|
|
|
+
|
|
|
+ if redis_host == nil or redis_host == "" then
|
|
|
+ ngx.log(ngx.ERR, "SCW_REDIS_HOST is not set!")
|
|
|
+ return
|
|
|
+ end
|
|
|
+ if redis_port == nil or redis_port == "" then
|
|
|
+ ngx.log(ngx.ERR, "SCW_REDIS_PORT is not set!")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ if pcall(function() redis_conn = redis.connect(redis_host, 6379) end) then
|
|
|
+ has_redis = true
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ local captcha_url = os.getenv("SCW_CAPTCHA_URL")
|
|
|
+ if captcha_url == nil or captcha_url == "" then
|
|
|
+ ngx.log(ngx.ERR, "SCW_CAPTCHA_URL is not set!")
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ if has_redis and captcha_url ~= "" then
|
|
|
+ -- the client ip
|
|
|
+ local v = redis_conn:get("bl:" .. ngx.var.remote_addr)
|
|
|
+
|
|
|
+ -- the X-Forwarded-For IP
|
|
|
+ if v == nil then
|
|
|
+ v = redis_conn:get("bl:" .. xff)
|
|
|
+ end
|
|
|
+
|
|
|
+ -- the X-Real-IP IP
|
|
|
+ if v == nil then
|
|
|
+ v = redis_conn:get("bl:" .. rip)
|
|
|
+ end
|
|
|
+
|
|
|
+ if v ~= nil then -- and h == nil then
|
|
|
+ local rprotocol = "http"
|
|
|
+ if ngx.var.scheme == "https" or ngx.var["http_x_forwarded_proto"] == "https" then
|
|
|
+ rprotocol = "https"
|
|
|
+ end
|
|
|
+
|
|
|
+ ngx.log(ngx.INFO, "port: " .. ngx.var.server_port)
|
|
|
+ local rport = ""
|
|
|
+ if (rprotocol == "https" and ngx.var.server_port ~= 443) or (rprotocol == "http" and ngx.var.server_port ~= 80) then
|
|
|
+ rport = string.format(":%d", ngx.var.server_port)
|
|
|
+ end
|
|
|
+
|
|
|
+ local referer = string.format("%s://%s%s%s", rprotocol, ngx.var.host, rport, ngx.var.uri)
|
|
|
+
|
|
|
+ return ngx.redirect(string.format(captcha_url, ngx.var.remote_addr, ngx.escape_uri(referer)))
|
|
|
+ end
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function _decrypt(message_and_iv, key)
|
|
|
+ if message_and_iv:len() < 32 then
|
|
|
+ return ""
|
|
|
+ end
|
|
|
+
|
|
|
+ iv, _ = message_and_iv:sub(1, 16)
|
|
|
+ message, _ = message_and_iv:sub(17, -1)
|
|
|
+
|
|
|
+ local c = cipher.new("aes-256-cbc")
|
|
|
+
|
|
|
+ c:decrypt(key, iv)
|
|
|
+ cleartext, err = c:final(message)
|
|
|
+ if err ~= nil then
|
|
|
+ return ""
|
|
|
+ end
|
|
|
+
|
|
|
+ return cleartext
|
|
|
+end
|
|
|
+
|
|
|
+scw()
|