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()