7. 用戶管理
— 幾乎每個web應用都必須去處理授權和認證。避免你自己重復造輪子,建議你去使用通用的插件。但是請保持它們是最新的。一些額外的 預防措施可以讓你的應用更加安全。
有一些Rails可用的授權和認證插件。密碼加密以後保存好於直接保存純文本密碼。最流行的插件是可以避免session定制的 restful_authentication。 然而早期的版本在某些情況下你即使沒有用戶名和密碼也可以登陸。
每個新用戶可以通過一個帶激活碼鏈接的電子郵件來激活他的帳戶。帳戶激活之後,數據庫裡激活碼那一列的值會被設為NULL, 如果某人發 送一個這樣的請求,他將被以第一次激活的用戶身份記錄到數據庫裡。 (有機會成為管理員):
http://localhost:3006/user/activate
http://localhost:3006/user/activate?id=
這是可能的,因為在某些服務器上, 以id為參數的這種方式,如params[:id], 將會是nil。然而,這裡有以個activation action裡的 finder方法:
User.find_by_activation_code(params[:id])
如果參數是nil,那麼SQL查詢的結果會是:
SELECT * FROM users WHERE (users.`activation_code` IS NULL) LIMIT 1
這樣,在數據庫記錄裡的第一個激活用戶就被查到,返回這個結果,攻擊者就登陸了. 你能找到更多的信息在我的blog帖子裡。 不時的更 新你的插件是明智的. 此外,你可以查看你的應用找到更多的這樣的漏洞。
7.1.暴力猜解帳戶
— 暴力猜解帳戶攻擊是使用錯誤的登陸證書去嘗試。用更通用的錯誤信息來阻斷這種攻擊,可能需要輸入一個驗證碼.
你web應用的用戶名列表可能會被拿去用一組密碼來做暴力猜解, 因為大多數的人不使用復雜的密碼。大多數的密碼是字典裡單詞和數字組 合。因此配備一組用戶名名單和一個黑客字典,一個自動程序,可能在幾分鐘之內就能找到正確的密碼。
因為這個,大多數的web應用會在它們不正確的時候顯示一個通用的錯誤信息 “用戶名或密碼不正確”。如果顯示的錯誤信息是“用戶名沒 有被找到”,那麼攻擊者就會自動編制一份用戶名名單。
然而,大多數的web應用設計者都忽視了忘記密碼頁。 當輸入用戶名或email地址的時候,這些網頁往往很誠實的就顯示找到(沒有找到) 的消息。這使攻擊者能夠編制一份可以暴力猜解帳號的用戶名名單。
在忘記密碼這個網頁上也顯示一個通用的錯誤消息可以減輕這種攻擊。此外,在某一個ip地址多次登陸失敗之後你可以要求輸入一個驗證碼 。但是請注意,這不是一個完全防彈的做法, 這些自動的程序也可以頻繁的改變自己的ip地址。只是給攻擊增加障礙。
7.2.帳戶劫持
— 很多web應用很容易劫持帳戶。為什麼不能使它變得更困難呢 ?
7.2.1. 密碼
想想這種情況,一個攻擊者竊取了一個用戶的session cookie,因此他們可以共用同一個應用。如果這個應用可以很容易就修改了密碼,那 麼攻擊者只需要幾個鼠標點擊就劫持了這個用戶的帳號。或者,如果修改密碼的表單容易受到CSRF攻擊的話,那麼攻擊者就會通過引誘受害者 到一個藏有制作好的CSRF IMG -TAG的網頁來修改他的密碼。對策是,讓修改密碼的表單不能被CRSF攻擊,當然在改變密碼的時候,也需要用戶 去輸入舊密碼。
7.2.2. E-Mail
然而,攻擊者也可以通過修改email地址來接管帳戶。當他修改email地址之後,他會去一個忘記密碼網頁,可能一個新的密碼就會被發送到 攻擊者的電子郵箱裡了。對策是,當修改email地址的時候,也需要輸入一個密碼。
7.2.3. 其他
依靠不同的web應用,可能有更多的劫持用戶帳戶的方法。在許多情況下,CSRF和XSS都有助於這樣做。例如,Google Mail的一個CSRF漏洞 ,在這個概念驗證的攻擊中,受害者會被引誘到一個被攻擊者控制的站點。在這個站點有一個制作好的IMG-tag, 該tag的結果是,發送一個 http get請求去改變Google Mail的郵件過濾器設置。如果這個受害者登陸到Google Mail,攻擊者會改變過濾器設置將他的所有郵件轉發到攻 擊者的郵箱裡。這幾乎和劫持整個帳戶一樣邪惡。對策是, 審核你的web應用邏輯來堵上所有的XSS和CSRF漏洞。
7.3. 驗證碼(CAPTCHA )
— 一個驗證碼是質問-響應的測試以便於確定這個響應是不是被一個計算機產生的。它往往通過讓用戶輸入一副字母被扭曲的圖片上的字 符,來保護評論不被自動的垃圾發布機器人侵害,這個消極的CAPTCHA的主意,不是去讓用戶證明他是一個人,而是讓一個機器人證明他是個機 器人。
不僅僅是垃圾信息發布機器人是一個問題, 不要忘了還有自動登陸機器人。一個流行的CAPTCHA API是reCAPTCHA , 它是顯示來自於舊書的 兩個單詞的扭曲圖片。它還增加了一個直角線,而不是一種扭曲的背景和早期CAPTCHAS做的那樣對文字的高水平包裝,因為後者被破解了。作 為獎勵,使用reCAPTCHA有助於數字化舊書。ReCAPTCHA 也有一個同名的Rails插件實現這個API。
通過這個API你可以得到兩個key,一個公有key,一個私有key,你必須把他們放到你的Rails環境。之後,你可以在view層使用 recapcha_tags方法,在controller層使用verify_recaptcha方法。如果校驗失敗verify_recaptcha會返回false。使用CAPTCHAs的問題是,它 們很麻煩。一些視障用戶對某些扭曲的CAPTCHAs難以閱讀。這個消極的CAPTCHA的主意,不是去讓用戶證明他是一個人,而是讓一個機器人證明 他是個機器人。
大多數的機器人是愚蠢的,它們檢索網絡,並且把垃圾信息放到它們能找到的每一個表單域裡。Negative CAPTCHAs趁機利用它們的愚蠢, 在表單裡包含一個“蜜罐”,人類用戶可以通過JavaScript 和CSS來隱藏它。
這裡有一些如何通過JavaScript/CSS來隱藏‘蜜罐’域的ideas:
*
放置這些fields到頁面的不可見區域
*
使這些元素非常小,或者顏色和頁面的背景色一樣
*
就讓它們顯示在頁面上,但是要告訴人類用戶,讓它們為空。
大多數簡單的negative CAPTCHA就是一個隱藏的‘蜜罐’域。在服務端,你可以檢測這個域的值:如果它包含任意文本,它肯定是個機器人 。然後你可以忽略或者是返回一個有意義的結果,但是不把其保存到數據庫裡。這種方法會讓機器人感到滿意並離開。你這樣做也會使用戶更 麻煩。
在Ned Batchelder's blog post,你能找到更先進的negative CAPTCHAs文章 :
*
包含一個存儲當前UTC時間戳的字段,並在服務端檢查它,如果它是太遙遠的過去,或者是將來,那麼這個form是無效的。
*
隨機的字段名
*
包含一個以上所有類型的‘蜜罐’字段,包括提交按鈕。
請注意,這僅僅是使你免於自動機器人的騷擾,而對於那些有針對性的完全定制的機器人則不能阻止。因此在Negative CAPTCHAs可能不是 很好的保護措施。
7.4. 日志
— 告訴Rails不要把密碼放在日志文件裡。
默認情況下,Rails會記錄到web應用的所有請求。但是日志文件會是一個巨大的安全問題,因為它們可能包含登陸證書或是信用卡號碼等等 。設計一個web 應用的安全理念應該是考慮如果攻擊者獲得(全部)訪問web服務器的權限會發生什麼事情。如果日志文件以明文列出secret和 密碼,那麼把它們加密保存到數據庫裡是完全無用的。通過在controller裡的filter_parameter_logging方法你可以在log文件裡過濾某些請求 參數。這些參數會在log文件裡會以 [FILTERED]標記。
filter_parameter_logging :password
7.5. 良好的密碼
— 你發現去記住你所有的密碼是很難的了嗎 ?不要把它們都寫下來,可以用一句容易去記住的句子裡每個單詞的首字母的組合。
一個叫Bruce Schneier的安全技術員,他分析了34,000個真實世界來自於MySpace釣魚攻擊的用戶名和密碼。這證明了大多數的密碼是很容 易被破解的。20個最常用的密碼是:
password1, abc123, myspace1, password, blink182, qwerty1, **you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1 and monkey.
有趣的是,這些密碼裡僅有4%是字典裡的單詞,並且絕大多數實際上是字母。然而,黑客字典包含了大量的今天的密碼,並且它們嘗試了各 種(字母數字混合)組合。如果你個攻擊者知道你的用戶名,並且你使用了弱密碼,那麼你的帳戶會很容易就被破解。
一個好的密碼是一個長的字母混合體。由於這種密碼很難去記憶,所以最好是只輸入一個你很容易記住的句子裡每個單詞的第一個字母。例 如,"The quick brown fox jumps over the lazy dog" will be "Tqbfjotld".注意,這只是一個例子,你不應該使用眾所周知的短語組成這 樣的密碼,因為它也有可能出現在黑客字典裡。
7.6. 正則表達式
— 在Ruby的正則表達式裡有個共同的缺陷是匹配字符串的開始和結束的^和$,而不是\A和\z.
Ruby用一種和其他語言略有不同的做法去匹配字符串的開始和結束。這就是為什麼很多Ruby和Rails的書都犯這樣的錯。那麼這個安全威脅 是怎麼樣的呢? 假設你有一個文件model並且你像這樣來驗證這個文件名 :
class File < ActiveRecord::Base
validates_format_of :name, :with => /^[\w\.\-\+]+$/
end
這個意思是說,在保存之前,這個model會驗證僅由字母數字字符,點,+和-的組合的文件名。程序裡增加裡^和$以便文件名這個字符串 從開始到結束都包含這些字符。然而,在Ruby裡,^和$匹配的是行開始和行結束。因此這樣的一個文件名可以毫無問題的通過這個過濾器 :
file.txt%0A<script>alert('hello')</script>
由於%0A在URL編碼裡是一個換行符,所以Rails會自動將其轉為"file.txt\n<script>alert(hello)</script>". 這個文件名 能通過這個過濾器因為這個正則表達式能一直匹配到行尾,其余的都不重要。正確的表達式應該這樣:
/\A[\w\.\-\+]+\z/
[source, ruby]
7.7. 權限提升
— 改變一個單一的參數可能給用戶未授權的訪問。請記住,每個參數都可能被改變,不管你對它做了多少隱藏和混淆。
最常見的使用者可能篡改的是id參數。比如在http://www.domain.com/project/1, 因為1是id.它會給controller裡的params[:id]一個可用 的值. 在這裡,你可能做這樣的事情:
@project = Project.find(params[:id])
這對於某些web應用是沒有關系的,但是如果用戶沒有權限去看所有的projects就不行了。如果用戶把id修改成了43,並且他不被允許看到 那個信息,他會想盡辦法來訪問它。相反,查詢用戶的訪問權限:
@project = @current_user.projects.find(params[:id])
根據你的web應用,可能會有更多的參數用戶可以篡改。經驗告訴我們,在未證明之前,沒有一條用戶輸入的數據是安全的,並且來自於用 戶的每個參數都有可能被操縱。
不要被安全,困惑和javascript安全愚弄了。Mozilla Firefox的web開發工具欄讓你可以審查和修改每個form表單的隱藏字段。JavaScript 能被用來驗證用戶的輸入數據,但是也肯定不能防止攻擊者發送帶有意想不到數據的惡意請求。Mozilla Firefox的Live HTTP Headers插件記 錄每個請求,並可以重復和改變它們。這是一個避開任何JavaScript驗證的簡單方法。而且即使是客戶端代理,也可以讓你攔截來自互聯網的 任意請求和響應。