scw.lua 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. local redis = require "redis"
  2. local regex = require "rex_pcre"
  3. local mime = require "mime"
  4. local cipher = require "openssl.cipher"
  5. local redis_conn = nil
  6. function scw()
  7. -- local start_time = ngx.now()
  8. local has_redis = false
  9. local ignore = os.getenv("SCW_IGNORE")
  10. local uri = ngx.var.uri
  11. local req_uri = ngx.var.request_uri
  12. ngx.log(ngx.INFO, "req_uri: " .. req_uri)
  13. if ignore ~= nil and regex.match(uri, ignore) then
  14. return -- do norhing if configured to ignore
  15. end
  16. -- --------------------------------------------------------------------------
  17. -- Does the user have an encrypted scw cookie that proves he is human?
  18. --
  19. local cookie_name = "cookie_" .. tostring(os.getenv("SCW_COOKIE"))
  20. if cookie_name == "cookie_" then
  21. ngx.log(ngx.ERR, "SCW_COOKIE is not set!")
  22. return
  23. end
  24. local scw_key = os.getenv("SCW_KEY")
  25. if scw_key == nil or scw_key == "" then
  26. ngx.log(ngx.ERR, "SCW_KEY is not set!")
  27. return
  28. end
  29. local cookie_key = ngx.decode_base64(scw_key)
  30. local human_cookie = ngx.var[cookie_name]
  31. local xff = tostring(ngx.var["http_x_forwarded_for"])
  32. local rip = tostring(ngx.var["http_x_real_ip"])
  33. if human_cookie then
  34. human_cookie = ngx.decode_base64(ngx.unescape_uri(human_cookie))
  35. end
  36. local is_human = false
  37. if human_cookie ~= nil and cookie_key:len() == 32 then
  38. local cookie_data = _decrypt(human_cookie, cookie_key)
  39. local cookie_ip = string.gsub(cookie_data, "scw|(.-)|(%d+)$", "%1")
  40. local cookie_expiry = string.gsub(cookie_data, "scw|(.-)|(%d+)$", "%2")
  41. 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)
  42. if is_human then
  43. return
  44. end
  45. end
  46. -- --------------------------------------------------------------------------
  47. -- check for blacklist status
  48. --
  49. if pcall(function() redis_conn:ping() end) then
  50. has_redis = true
  51. else
  52. -- io.stderr:write("reconnecting to redis\n")
  53. local redis_host = os.getenv("SCW_REDIS_HOST")
  54. local redis_port = os.getenv("SCW_REDIS_PORT")
  55. if redis_host == nil or redis_host == "" then
  56. ngx.log(ngx.ERR, "SCW_REDIS_HOST is not set!")
  57. return
  58. end
  59. if redis_port == nil or redis_port == "" then
  60. ngx.log(ngx.ERR, "SCW_REDIS_PORT is not set!")
  61. return
  62. end
  63. if pcall(function() redis_conn = redis.connect(redis_host, 6379) end) then
  64. has_redis = true
  65. end
  66. end
  67. local captcha_url = os.getenv("SCW_CAPTCHA_URL")
  68. if captcha_url == nil or captcha_url == "" then
  69. ngx.log(ngx.ERR, "SCW_CAPTCHA_URL is not set!")
  70. return
  71. end
  72. if has_redis and captcha_url ~= "" then
  73. -- the client ip
  74. local v = redis_conn:get("bl:" .. ngx.var.remote_addr)
  75. -- the X-Forwarded-For IP
  76. if v == nil then
  77. v = redis_conn:get("bl:" .. xff)
  78. end
  79. -- the X-Real-IP IP
  80. if v == nil then
  81. v = redis_conn:get("bl:" .. rip)
  82. end
  83. if v ~= nil then -- and h == nil then
  84. local rprotocol = "http"
  85. if ngx.var.scheme == "https" or ngx.var["http_x_forwarded_proto"] == "https" then
  86. rprotocol = "https"
  87. end
  88. -- ngx.log(ngx.INFO, "port: " .. ngx.var.server_port)
  89. local rport = ""
  90. if (rprotocol == "https" and ngx.var.server_port ~= 443) or (rprotocol == "http" and ngx.var.server_port ~= 80) then
  91. rport = string.format(":%d", ngx.var.server_port)
  92. end
  93. local referer = string.format("%s://%s%s%s", rprotocol, ngx.var.host, rport, ngx.var.uri)
  94. return ngx.redirect(string.format(captcha_url, ngx.var.remote_addr, ngx.escape_uri(referer)))
  95. end
  96. end
  97. end
  98. function _decrypt(message_and_iv, key)
  99. if message_and_iv:len() < 32 then
  100. return ""
  101. end
  102. iv, _ = message_and_iv:sub(1, 16)
  103. message, _ = message_and_iv:sub(17, -1)
  104. local c = cipher.new("aes-256-cbc")
  105. c:decrypt(key, iv)
  106. cleartext, err = c:final(message)
  107. if err ~= nil then
  108. return ""
  109. end
  110. return cleartext
  111. end
  112. scw()