鼬~~哩賀,我是寫程式的山姆老弟,昨天跟大家一起看了 Rails 的多資料庫,今天來看 RailsGuide 的 Security 篇,看看 Rails 預設幫我們做了哪些資安的防禦,夠夠~
先了解有哪些攻擊,再針對這些攻擊,制定防禦策略,這一篇 RailsGuide 就是以這樣的角度來寫的,我們也跟著這個思路來看 Rails
有哪些策略
Security depends on the people using the framework, and sometimes on the development method. - RailsGuide
從 RailsGuide 的 ActionController Session 篇可以知道,Rails 的 Session 可以存放在 Cookie
(預設)、Cache
、ActiveRecord
等地方,以下有幾個跟 Session 相關的攻擊方式:
駭客各種透過 cookies,偷取使用者個資的一種攻擊方式,其中有幾種相關的攻擊方法:
如果網站並不是透過 https(沒有 SSL 加密保護傳輸資料),而是用 http 的話,駭客可能透過查看封包,從 header 撈到 cookies,用這個 cookies 偽裝成使用者
Rails 的解決方法:提供強制啟用 SSL 的設定選項,使用 SSL 將傳輸資料加密,防止 cookies 洩漏
config.force_ssl = true
在公共裝置使用後,沒有登出,駭客可以直接使用,或者一樣透過 cookies 拿到 session
有些 Cross-site scripting (XSS)
就是以 cookies 為目標
還有 Session Fixation
也算是一種 Session Hijacking
這類型的攻擊,主要就是要賣使用者的個資來賺錢,有趣的是,RailsGuide 竟然把個資的價格範圍也列出來了 XDDD
Rails 的 Cookie 預設會加密,所以即使駭客拿到 cookies,駭客只能利用這個 cookies,不能去改 cookies 裡面的內容、也不能知道這個 cookies 裡面裝什麼內容,除非他也偷到 server 的 secret key
駭客透過得到 cookies 來重複發送請求,如果是一般的請求是沒差,但如果是跟錢有關係的請求,假設網站把點數存在 cookies 裡面,那駭客可以先複製 cookies,再用點數消費,消費完再把原本的 cookies 覆蓋回去,如此就得到無限點數了
駭客透過把駭客已知的 session ID,想方設法(e.g. 透過 XSS 強制修改使用者的 cookies)讓使用者也使用這個 session ID,這樣使用者在自己裝置做的事情,駭客也可以透過同一個 session ID 在自己的電腦也會同步到
reset_session
,讓每次登入之後,就重新更新 session ID,並且把舊的 session ID 失效,這個方法同樣也適用 Session Hijacking
攻擊,即使駭客拿到舊的 session ID 也沒用(但我猜他能拿到一次,就能拿到更多次 XD)Rails
常見的 Devise
gem 會自動過期 sessionCSRF,aka. 跨站請求偽造,換句話說就是,讓使用者誤信假網站,假網站卻能對真網站做壞壞的事,的一種攻擊方式
駭客仿造做出了 A’ 網站,讓使用者相信 A’ 就是真正的 A 網站,而駭客在 A’ 網站放了會去執行 A 網站的某些動作,讓使用者點了 A’ 網站的按鈕或圖片之後,卻影響了 A 網站的資料
例如,駭客做了假的 FB 網站,讓使用者點了某個圖片,但這圖片實際上是去執行真正的 FB 的刪除帳號之類的指令(這個舉例是不可能發生的,只是為了好理解)
其中利用的也是剛剛 Session 篇提到的,在對 A 網站發送請求的時候,瀏覽器會自動把 A 網站的 cookies 帶出去,所以才會導致影響到真正的資料
聽起來很荒謬,這種攻擊方式在日常也是很少,在 2006 年只佔小於 0.1% 的攻擊比例
config.action_controller.default_protect_from_forgery
的預設設定,在 development 之外的環境,預設開啟,防止 CSRF 攻擊
防止的方法是,自動生成一個 csrf token
,這個 token 會出現在每個 form,如果發送 form 的時候,沒有帶這個 token 或帶的 token 是錯的,那 Rails 就會報錯
這個 token 會預設加入 X-CSRF-Token
的 header,如果發送 ajax 請求的時候,沒有這個 header 的話,那就不會接受請求
這東西預設在我們的 app/views/layouts/application.html.erb
的 header 裡面,以 <%= csrf_meta_tags %>
的形式出現
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>YourApp</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
redirect_to
接使用者傳進來的參數,可能會導致導轉到可疑的網站,建議用白名單方法,把可接受的參數用白名單列出來比對
使用者傳進來的檔名,記得也要用白名單過濾,避免駭客用 “../../../etc/passwd”
把 linux 重要的設定檔覆蓋掉
../
等字元,因為如果駭客傳 ....//
,取代掉之後就變成 ../
了注意使用者傳的檔案是可執行的程式碼,這篇有舉例 Apache 的 DocumentRoot 有某些特定副檔名的檔案的話,會直接被執行,如果使用者傳的是惡意的程式碼、同時你收檔案的位置又是 DocumentRoot 或是會直接被執行的路徑的話,那這樣就會直接被執行
不只使用者上傳的檔案要注意,連要傳給使用者的檔案也要注意,如果傳給使用者的檔案,會依照參數而定的話,也要注意會有以下狀況發生,如果 params[:filename]
是 “../../../etc/passwd”
,那重要的檔案就要被拿走了
send_file('/var/www/uploads/' + params[:filename])
如果我們有個 query 是 Project.where("name = '#{params[:name]}'")
,這時候駭客傳進 name 的值 ' OR 1 —
,這樣會變成 SELECT ***** FROM projects WHERE name **=** '' OR 1 *--'*
,變成拿取空字串或所有,後面的單引號就被註解掉,這樣不管駭客有沒有權限,他都能從資料庫取得想要的資料,這是著名的 SQL Injection
'
、 "
、NULL、\n
等特殊字元,預設在 Model.find(id)
和 Mode.find_by_sth(something)
會啟動
xxx.where("…")
或 connection.execute()
或 Model.find_by_sql()
就需要人工處理Model.where("zip_code = ? AND quantity >= ?", entered_zip_code, entered_quantity).first
來處理駭客想方設法(典型有幾種的方法)把 JS 程式碼餵給受害者的瀏覽器執行,駭客可能會偷盜使用者的 cookie,再進一步去偷取使用者個資
Rails 的解決方法:提供 sanitize
方法,sanitize 是消毒的意思,搭配白名單,阻擋駭客的惡意輸入
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
s = sanitize(user_input, tags: tags, attributes: %w(href title))
config.filter_parameters << :password
設定,可以把密碼或其他機密欄位給過濾掉(預設有 :passw
, :secret
, :token
)"<sc<script>ript>".gsub("<script>", "")
,你想把危險的部分拿掉,反而是更危險的但看了這篇才知道 Ruby 的正規有個漏洞,正常在比對開頭、結尾,會使用 ^
作為開頭、$
作為結尾
在 Ruby 的 ^
, $
是 行的開頭和結尾,所以假設我們的 pattern 是 /^https?:\/\/[^\n]+$/i
,駭客只要傳入像是下面的多行字串,http://hi.com
是行的開頭,所以依舊符合 pattern,駭客就能通過 pattern、並且植入 JS 程式碼
javascript:exploit_code();/*
http://hi.com
*/
所以要使用 \A
取代 ^
、用 \z
取代 $
Rails 預設會塞入這些 headers
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN', # 防止 cross domain 的 iframe 植入
'X-XSS-Protection' => '0', # 不啟用瀏覽器的 XSS 防護,這是比較舊的 header 了,預設是不啟用
'X-Content-Type-Options' => 'nosniff', # 阻止瀏覽器去根據檔案內容猜測檔案類型
'X-Download-Options' => 'noopen',
'X-Permitted-Cross-Domain-Policies' => 'none',
'Referrer-Policy' => 'strict-origin-when-cross-origin'
}
還有一些常見的 headers 可以自行在 config/application.rb
或 config/environments/*.rb
加上去
X-Content-Security-Policy
Access-Control-Allow-Origin
Strict-Transport-Security
也可以拿掉預設的 headers:config.action_dispatch.default_headers.clea**r**
Rails 有針對 CSP 的設定,可以對不同的資源類型設定不同的規則
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
policy.font_src :self, :https, :data
policy.img_src :self, :https, :data
policy.object_src :none
policy.script_src :self, :https
policy.style_src :self, :https
# Specify URI for violation reports
policy.report_uri "/csp-violation-report-endpoint"
end
也可以對特定資源自訂規則,覆蓋掉 Global 的設定
class PostsController < ApplicationController
content_security_policy do |policy|
policy.upgrade_insecure_requests true
policy.base_uri "https://www.example.com"
end
end
或是對某個 action 取消掉 CSP
class LegacyPagesController < ApplicationController
content_security_policy false, only: :index
end
sanize
過濾掉使用者輸入的危險部分
config.force_ssl
避免竊聽
config.action_controller.default_protect_from_forgery
防止 CSRF 攻擊
用 master.key
加密重要資料為 credentials.yml.enc
這篇 RailsGuide 的文本量超大,再加上有些攻擊方法,沒有實際遇過,很難想像駭客可以怎麼攻,才知道大概怎麼防,其實 Rails 能做的真的也不多,蠻多篇幅都是在提醒開發者可能的陷阱有哪些,剩下的就要靠開發者自己去防治了
好的,這個系列就已經到尾聲了,明天會統整一下這三十篇的心得,我們明天見~