scw.lua 3.9 KB

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