scw.lua 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. require "apache2"
  2. local redis = require "redis"
  3. local regex = require "rex_pcre"
  4. local mime = require "mime"
  5. local cipher = require "openssl.cipher"
  6. local inspect = require 'inspect'
  7. -- --------------------------------------------------------------------------
  8. -- try to connect to redis
  9. --
  10. local redis_conn = nil
  11. function _decrypt(message_and_iv, key)
  12. if message_and_iv:len() < 32 then
  13. return ""
  14. end
  15. iv, _ = message_and_iv:sub(1, 16)
  16. message, _ = message_and_iv:sub(17, -1)
  17. local c = cipher.new("aes-256-cbc")
  18. c:decrypt(key, iv)
  19. cleartext, err = c:final(message)
  20. if err ~= nil then
  21. return ""
  22. end
  23. return cleartext
  24. end
  25. function scw(r)
  26. local start_time = r:clock()
  27. local has_redis = false
  28. local ignore = os.getenv("SCW_IGNORE")
  29. if ignore ~= nil and regex.match(r.uri, ignore) then
  30. return apache2.DECLINED
  31. end
  32. -- --------------------------------------------------------------------------
  33. -- Does the user have an encrypted scw cookie that proves he is human?
  34. --
  35. local cookie_name = os.getenv("SCW_COOKIE")
  36. local cookie_key = r:base64_decode(os.getenv("SCW_KEY"))
  37. local human_cookie = r:getcookie(cookie_name)
  38. local xff = tostring(r.headers_in["X-Forwarded-For"])
  39. local rip = tostring(r.headers_in["X-Real-IP"])
  40. if human_cookie then
  41. human_cookie = r:base64_decode(r:unescape(human_cookie))
  42. end
  43. local is_human = false
  44. if human_cookie ~= nil and cookie_key:len() == 32 then
  45. local cookie_data = _decrypt(human_cookie, cookie_key)
  46. is_human = string.gsub(cookie_data, "scw|(.-)|(%d+)$", function (ip, exp)
  47. if (ip == r.useragent_ip or ip == xff or ip == rip) and r:clock() <= tonumber(exp) then
  48. return true
  49. end
  50. return false
  51. end)
  52. if is_human then
  53. return apache2.DECLINED
  54. end
  55. end
  56. -- --------------------------------------------------------------------------
  57. -- check for blacklist status
  58. --
  59. if pcall(function() redis_conn:ping() end) then
  60. has_redis = true
  61. else
  62. -- io.stderr:write("reconnecting to redis\n")
  63. local redis_host = os.getenv("SCW_REDIS_HOST")
  64. local redis_port = os.getenv("SCW_REDIS_PORT")
  65. if pcall(function() redis_conn = redis.connect(redis_host, 6379) end) then
  66. has_redis = true
  67. end
  68. end
  69. local captcha_url = os.getenv("SCW_CAPTCHA_URL")
  70. if has_redis and captcha_url ~= "" then
  71. -- the client ip
  72. local v = redis_conn:get("bl:" .. r.useragent_ip)
  73. -- the X-Forwarded-For IP
  74. if v == nil then
  75. v = redis_conn:get("bl:" .. xff)
  76. end
  77. -- the X-Real-IP IP
  78. if v == nil then
  79. v = redis_conn:get("bl:" .. rip)
  80. end
  81. if v ~= nil then -- and h == nil then
  82. local rprotocol = "http"
  83. if r.is_https then
  84. rprotocol = "https"
  85. end
  86. local rport = ""
  87. if (r.is_https and r.port ~= 443) or (r.is_https ~= true and r.port ~= 80) then
  88. rport = string.format(":%d", r.port)
  89. end
  90. local referer = string.format("%s://%s%s%s", rprotocol, r.hostname, rport, r.unparsed_uri)
  91. r.headers_out["location"] = string.format(captcha_url, r.useragent_ip, r:escape(referer))
  92. --[[
  93. r.headers_in["X-SCW-IP"] = v
  94. r.handler = "proxy-server"
  95. r.proxyreq = apache2.PROXYREQ_REVERSE
  96. r.filename = string.format("proxy:http://captcha:8080/?src=%s&r=%s", r.useragent_ip, r.unparsed_uri)
  97. --]]
  98. return apache2.HTTP_MOVED_TEMPORARILY
  99. end
  100. end
  101. return apache2.DECLINED
  102. end