未添加CSRF Token的Rails表单为何能正常工作?
相关代码
HTML表单
<form accept-charset="UTF-8" action="/users" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="user[username]"> <br> <label for="email">Email:</label> <input type="text" id="email" name="user[email]"> <br> <label for="password">Password:</label> <input type="password" id="password" name="user[password]"> <input type="submit" value="Submit"> </form>
Rails控制器代码
def create @user = User.new(user_params) if @user.save redirect_to new_user_path else render :new, status: :unprocessable_entity end end private def user_params params.require(:user).permit(:username, :email, :password) end
请求参数
#<ActionController::Parameters {"user"=>{"username"=>"username", "email"=>"email", "password"=>"password"}, "controller"=>"users", "action"=>"create"} permitted: false>
核心原因分析
正常情况下,Rails默认会强制校验POST/PUT/PATCH/DELETE请求的CSRF Token,未添加Token的表单应该会被拦截并抛出ActionController::InvalidAuthenticityToken异常。能正常提交的情况,通常是以下几种:
CSRF保护未启用
Rails的ApplicationController默认包含protect_from_forgery with: :exception来开启防护,如果这条代码被注释,或者你的UsersController通过skip_before_action :verify_authenticity_token主动跳过了CSRF验证,表单提交就不会被拦截。环境配置关闭了CSRF检查
如果你在config/environments/development.rb里设置了config.action_controller.allow_forgery_protection = false,开发环境下会直接关闭CSRF校验,这种设置一般用于调试,但上线前必须改回默认值(true)。第三方gem或工具禁用了校验
部分API开发相关的gem(比如devise_token_auth)或者调试工具,可能会自动添加skip_before_action :verify_authenticity_token,导致CSRF校验被绕过。
注意事项
CSRF保护是防止跨站请求伪造的关键机制,即使当前表单能正常提交,也必须补上Token。手写HTML表单时,可通过以下方式添加:
- 在页面头部添加
<%= csrf_meta_tags %>生成meta标签 - 在表单内添加隐藏字段:
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
内容的提问来源于stack exchange,提问作者vladislav

