2

我正在考虑为 rails 编写一个自动垃圾邮件保护系统(也许我会写一个公共 gem)。

我的概念是在 application_controller fe 中包含一个辅助方法:

class ApplicationController < ActionController::Base
  automatic_captcha_redirect(:min_time => 30.seconds :limit => 50)
...
end

然后我想在每个控制器中自动包含一个 before_filter,它检查当前请求是通过 post、put 还是 delete 方法。

如果用户的最后一个 post-request 小于 :min_time,那么该请求应该被重定向到一个 captcha-input-page(发布的用户数据驻留在隐藏的 html 字段中)。

# before_filter :check_spam
def check_spam
  if !request.get? && session[:last_manipulation_at] 
      && session[:last_manipulation_at] >= DateTime.now - 30.seconds
    redirect_to captcha_path 
      # (doesn't know yet how to handle the post data to 
      # display in hidden fields in the spam-captcha-form)
  end
end

在 captcha.haml

=form_tag 
-request.params.each do |key, value|
  =hidden_field_tag key, value

=captcha_image
=submit_button_tag

如果用户提交了正确的验证码,他的数据将被发布到正确的操作中。

你认为这可以实现吗?有什么批评或建议吗?或者一个想法如何实现这种行为?

编辑:

  • 这不应该通过所有的 ActiveRecord 堆栈;不能作为中间件挂钩(Rails Rack)实现吗?
    • 是的,这是个好主意——但我对 rails rack 不是很熟悉:/
  • 文件上传呢?(您不能将其存储在隐藏文件中)
    • 嗯...也许检查一下帖子中是否有文件?(这怎么可能实现?)
  • Ajax 的发布呢?
    • 也许发回 http-status 代码(fe 503 服务暂时不可用)
  • 为什么只有 POST 而不是 PUT 和 DELETE?
    • 在我的问题中纠正了这一点

编辑:

第一种处理结构(作为非机架应用程序 - 我不知道如何编写机架应用程序):

0) environment.rb 中的设置

auto_recaptcha[:limit] = 10
auto_recaptcha[:min_time] = 1.minute

1)用户发布数据

检查 last_manipulation 和最大值。application_controller.rb 中允许的操作量

class ApplicationController < ActionController::Base
  before_filter :automatic_captcha_redirect

  def automatic_captcha_redirect
    session[:last_manipulation_at][:manipultation] = [] unless session[:last_manipulation_at][:manipultation]
    # Checks if requests are falling under the specifications for showing captcha


    if !request.get? 
       && session[:last_manipulation_at][:date] > DateTime.now - auto_recaptcha[:min_time] 
       && session[:last_manipulation_at][:manipultation].count < auto_recaptcha[:limit]

      # If user answered captcha, verify it
      if !verify_captcha(params)
        @url = request.url
        @params = request.params
        render "layouts/captcha.haml"
      else

        # Add successfull manipulation to counter
        session[:last_manipulation_at][:manipultation] << DateTime.now
        session[:last_manipulation_at][:date] = DateTime.now
      end
    end
  end
end

验证码

-form_tag @url do 
  -request.params.each do |key, value|
    =hidden_field_tag key, value

  =captcha_image
  =submit_button_tag

2)………………

最后)将用户数据发布到正确的位置

post(params) => users_path # path "/users" with method: post
4

2 回答 2

2

首先,我想说这是一个非常好的功能理念。

我的问题/评论:

  • 这不应该通过所有的 ActiveRecord 堆栈;不能作为中间件挂钩(Rails Rack)实现吗?
  • 文件上传呢?(您不能将其存储在隐藏文件中)
  • Ajax 的发布呢?
  • 为什么只有 POST 而不是 PUT 和 DELETE?

无论如何,我更感兴趣的是查看最后 5 分钟内的帖子数量,例如,最后一次请求的日期。我相信它更相关。

于 2009-07-28T12:11:24.197 回答
1

One way this could be put together:

  • Middleware/rails metal component that monitors the requests and adds the information to the rack session.

  • Controller helpers for before_filters on things that might need captchas

  • View helpers for displaying the captchas

You could make the captcha rate adjustable through the args passing mechanism of use

#config/environment.rb
config.middleware.use 'CaptchaMiddleware',:period=>5.minutes,:limit=>50,:captcha_url=>'/captcha'

Also, this should not rely on hidden form fields because a determined bot writer could just change the value they are posting to your server code.

Simple middleware example code(slightly better than a stab in the dark, but still)

class CaptchaMiddleware
  def initialize app,options
    @app = app
    @options=options
  end

  def update_stats!
    #session based,on account of laziness
    session[:reqs] ||= []
    session[:reqs].reject!{ |request| request < Time.now - @options[:period]}
    session[:reqs] << Time.now
  end

  def over_limit?
    session[:reqs].length > @options[:limit]
  end

  def call env
    @env = env
    if @env["REQUEST_METHOD"]!='GET'
      update_stats!
      if over_limit?
        return [302,{"Location: #{options[:captcha_url]}"},'']
      end
    end
    @app.call env
  end

  def session
    @env["rack.session"]
  end
end
于 2009-07-28T17:24:50.240 回答