over 3 years ago

第四章的七個 Action 我們都在上一節講完了,但似乎漏掉一個小部分沒有解釋到:

  def group_params
    params.require(:group).permit(:title, :description)
  end

這一段程式碼到底是什麼意思呢?這一段程式碼是 Strong Parameter 的 permit 機制。

Strong Parameters 的緣起

2012 年 Github 曾經發生轟動一時的 被 hack 事件
原本只是一個好心的 hacker 發現漏洞回報問題
因為沒人理他(!?) 所以乾脆直接 hack 給大家看,來證明此問題的嚴重性 XDDD

簡單來說呢,在 Rails 的架構下,表單的欄位內容是綁 Model 資料庫的欄位
例如當我們 create 一筆 group 資料,如果用 HTTP Request 的訊息來看,其實是這樣

# 準備上傳一組 Hash (雜湊) 內容如下:


Started POST "/groups" for 127.0.0.1 at 2014-07-24 16:57:19 +0800
# HTTP verb: post , 網址: "/groups" 


Processing by GroupsController#create as HTML

# 執行 group_controller.rb 的 Create action


  Parameters: {"utf8"=>"✓", 
               "authenticity_token"=>"un1ahUt88HhFVxLcopkQojkdD6PaoPfPUjAQ8YNFN+0=", 
               "group"=>{"title"=>"new group", 
                         "description"=>"bala bala ...."}, 
               "commit"=>"Submit"}
# 送到 server 端的參數: ...... (bj4)

然後 Rails 很聰明地幫我們跟資料庫溝通,執行以下動作

  User Load (0.4ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
# 讀取 Current_user (登入的使用者) 資料


(0.2ms)  begin transaction
  SQL (0.8ms)  INSERT INTO "groups" ("created_at", 
                                     "description", 
                                     "title", 
                                     "updated_at",
                                     "user_id") 
                VALUES (?, ?, ?, ?, ?)  [["created_at", "2014-07-24 08:57:19.933822"], 
                                         ["description", "bala bala ...."], 
                                         ["title", "new group"],  
                                         ["updated_at", "2014-07-24 08:57:19.933822"], 
                                         ["user_id", 1]]
(1.0ms)  commit transaction
# 用 SQL 語法真正把資料存進資料庫裡面, 有 create_at, title, description, updated_at, user_id 等欄位被存入資料

然後開始執行 create action 裡面寫的

if @group.save
  flash[:notice] '新增討論版成功'
  redirect_to groups_path
... ()

對應的 log 是

Redirected to http://localhost:3000/groups
# 開始執行 create action 裡面寫的 if @group.save =>  redirect_to groups_path (回到 groups#index 頁面)


Completed 302 Found in 395ms (ActiveRecord: 76.3ms)

我們就會在瀏覽器出這個畫面


安全問題

這是一個很棒理想的狀態。我們不必為了欄位的輸入,在 html 裡面折騰一次,controller 又折騰一次。只要表格裡面欄位是什麼,controller 就吃什麼欄位。

但是,這確有一個嚴重的問題在裡面。這個機制,讓大家都知道你的資料庫是怎麼設計的,而且 HTML 表單的元素是可以用瀏覽器改的。比如說他可以猜你的使用者「判斷是否為 admin 」的表格欄位是「is_admin」(即便你沒公開在網站上)。

於是他就在「使用者更新」資料頁,自己變更表單,送看看 { :is_admin => true } ,看看會不會通過,搖身一變就變成 admin。

這實在太危險了。

所以經過 Github 的被駭客事件後,Rails Team 決定做出變革:「不是使用者送來的東西,controller 通通都吃」,而是「有規定在白名單裡面的變數才被接受」。

這就是 strong_params 的意義。

也就是利用這個設定(以 groups_controller 舉例) 讓可以被變更的欄位通過,其他亂猜的參數不能過。

  def group_params
      params.require(:group).permit(:title, :description)
  end

整串 Hash 參數 (params) 只允許 (.permit) group 資料表 (:group) 的 :title 跟 :descrition 這二個欄位的資料通過,
進入下一個階段的 SQL 資料庫存取,其餘的參數一律忽略

用此方式來做到資訊安全機制


所以我們在做使用者功能裡面

當增加了 name 的欄位,就必須要在 devise 預設的 strong_params => configure_permitted_parameters

   def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation) }
   end

把 :name 加進去,這樣在表單建立資料的時候, name 欄位的資料才會被允許送進資料庫存取裡面

然後在修改 (update) 表單的頁面還要加入

   devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :password, :password_confirmation, :current_password) }

name 跟 current_password ( 使用者賬戶密碼確認 ) ,才能通過,允許資料修改

← [延伸閱讀] 深度解析 CRUD 功能裡的 Controller & Model [延伸閱讀] Active Record Association →
 
comments powered by Disqus