再論驗證碼安全:請及時銷燬你的驗證碼

我在 上一篇文章中講到了如何使用C#模擬用戶登陸具備驗證碼網站。今天我就換位思考一下,站在網站開發人員的角度講一講驗證碼的的一個安全問題:及時銷燬網站中的驗證碼。
爲了方便你們理解,這裏我就以一個投票的應用網站爲例進行說明。投票網站首先要防止的就是用戶不斷點擊投票按鈕來重複投票;固然,避免重複投票的解決辦法有不少,好比記錄IP、寫入Session、Cookie甚至還有要求用戶輸入×××號碼等。可是你記錄IP,那我就寫一個程序來模擬發包,每投1票後自動換代理,而後繼續投票,若是是寫入到Session中那麼我寫個投票程序,每投1票就從新開啓一個新的會話就是。若是是記入Cookie,那我該表Cookie的值再模擬投票發包,要輸入×××號進行驗證?寫個××××××程序也十分容易……
在投票機器人面前,記錄IP、記錄×××號碼、寫入Session和Cookie等防做弊技術形同虛設。因此在投票網站中驗證碼功能必不可少。那麼咱們將驗證碼功能加入到網站投票中:
1.生成驗證碼圖片的頁面CreateImg.aspx,其後臺代碼爲:
protected void Page_Load( object sender, EventArgs e)
     {
string checkCode = CreateCode(4); //生成隨機是4位驗證碼
             Session[ "CheckCode"] = checkCode; //將驗證碼保存到Session中
             CreateImage(checkCode); //將驗證碼以圖片的方式輸出
     }    
2.在用戶單擊投票按鈕後觸發的事件:
protected void btnVote_Click( object sender, ImageClickEventArgs e)
{
         if (!ValidateUserInfo()) //驗證IP在數據庫中的狀況,一個IP一天只能投5張票,從而防止重複投票
        {
                UIHelper.Alert(Page, "一個IP一天只能投5張票,請勿重複投票");
                 return;
        }
         if (Session[ "CheckCode"] == null)
        {
                UIHelper.Alert(Page, "驗證碼已過時,請從新輸入");
                 return;
        }
         if (Session[ "CheckCode"].ToString().ToLower() != txbCode.Text.ToLower()) //驗證碼忽略大小寫
        {
                UIHelper.Alert(Page, "驗證碼錯誤");
                 return;
        }
         else //驗證碼正確
        {
                UpdateVote(); //修改數據庫,將投票數加1
        }
}
OK,大功告成!這個程序邏輯上有問題嗎?沒有吧,驗證碼是生成的圖片,圖片是有干擾因素的,不會被程序識別,並且驗證碼的內容是保存到服務器的,邏輯處理也是錯。彷佛一切都那麼完美,可是事實並非這樣,對於這樣的投票網站,個人投票機器人仍然肆無忌憚的不斷切換IP,不斷刷票。(要作投票機器人的同志們注意啦,不要看到投票的地方是有驗證碼的就束手無策了哦,也許他的網站就存在如下描述的漏洞哦!)
漏洞就出在投票時對驗證碼進行驗證後沒有對服務器上Session中的驗證碼內容進行銷燬。在平時使用IE瀏覽時,每投票一次後刷新頁面,驗證碼生成頁面被從新請求,因此Session值在請求驗證碼生成頁時被替換,因此不會有什麼問題。可是如今面對的是投票機器人,個人機器人在第一次請求時得到驗證碼的圖片並展現給用戶,用戶肉眼識別驗證碼,而後輸入程序的文本框中,因爲服務器上驗證碼的內容並無被銷燬,並且投票程序也不會再請求驗證碼生成圖片的URL,因此接下來每次使用相同的SessionID和用戶輸入的驗證碼值,服務器驗證投票時 
if (Session["CheckCode"].ToString().ToLower() != txbCode.Text.ToLower())
都會返回false,驗證碼都是經過的,因此投票天然成功。終究是百密一疏啊!費盡心思防止投票做弊,最終卻由於這一個地方的疏忽而前功盡棄,投票做弊成功,投票結果仍是被投票機器人所左右。
也許有人想到了,那能夠在Session中放置一個標記,若是投票成功了就將標記置「1」,下次請求時判斷Session中標記爲「1」就拒絕投票就是了。可是投票只是我這裏舉的一個例子,像論壇這種用驗證碼防止用戶惡意灌水的總不可能限制用戶只發一帖吧。論壇發帖時的驗證碼若是沒有被及時銷燬,那麼個人灌水機器人就仍然能夠處處肆意發帖了,哈哈哈哈。
要避免這個漏洞被利用仍是很簡單,只須要將上面的代碼中投票完成後當即將驗證碼從服務器上銷燬便可:
protected void btnVote_Click( object sender, ImageClickEventArgs e)
{
         if (!ValidateUserInfo()) //驗證IP在數據庫中的狀況,一個IP一天只能投5張票,從而防止重複投票
        {
                UIHelper.Alert(Page, "一個IP一天只能投5張票,請勿重複投票");
                 return;
        }
         if (Session[ "CheckCode"] == null)
        {
                UIHelper.Alert(Page, "驗證碼已過時,請從新輸入");
                 return;
        }
         if (Session[ "CheckCode"].ToString().ToLower() != txbCode.Text.ToLower()) //驗證碼忽略大小寫
        {
                UIHelper.Alert(Page, "驗證碼錯誤");
                 return;
        }
         else //驗證碼正確
        {
                UpdateVote(); //修改數據庫,將投票數加1
        }

Session[ "CheckCode"] = null; //驗證碼使用後立刻從服務器銷燬
}
這樣若是仍然使用相同的SessionID和驗證碼值,那麼將會在 if (Session["CheckCode"] == null)這裏判斷出驗證碼已通過期,想成功投票?從新請求驗證碼頁面得到驗證碼圖片,而後從新輸入驗證碼吧!
這個問題雖然看起來不覺得然,可是正所謂「千里之堤毀於蟻穴」,只要驗證碼沒有從服務器上銷燬,那麼頁面上的驗證碼仍是形同虛設,和驗證碼的圖片地址爲<img src="CreateCheckCode.aspx?code=af5d" alt="驗證碼">
這樣把驗證碼直接暴露在HTML中或者直接使用文本而不是圖片來表示驗證碼有什麼區別呢?
另外有人提到,這裏是Session保存驗證碼纔會有這個問題,那徹底基於Cookie加密的呢?在前面的文章中我也提到過Cookie加密的方式保存驗證碼的內容,可是今天我又仔細想了一下,得出結論: 驗證碼內容不能保存到客戶端,也就是說根本就不該該使用Cookie加密的方式,Cookie加密保存驗證碼明文是沒有什麼意義的,必需要在服務器端保存與驗證碼相關的信息(好比驗證碼明文或者驗證碼加密解密密鑰)。爲何不能使用Cookie加密保存驗證碼?我舉個簡單的例子吧:
好比如今頁面上顯示的驗證碼是1234,同時抓包發現提交的時候Cookie中有值:「EncryptCode=asdf」這是驗證碼的明文通過加密後的密文,我不知道加密算法是什麼,可是我每次程序提交時就將1234做爲驗證碼的值同時將「EncryptCode=asdf」做爲Cookie的一部分發送到服務器,那麼服務器將1234加密後與發送過來的Cookie值「asdf」一比較,兩者相同,驗證經過!!!
因此我認爲驗證碼的明文是不可能徹底基於客戶端的,必需要在服務器上保存與驗證碼相關的信息(驗證碼明文或密鑰)。既然要在服務器上保存相關信息,那麼就可能出現這個漏洞。固然這是我想遍了全部驗證碼明文保存的方法後得出的結論,我還不敢拍胸脯說這是100%正確的,若是你們認爲這個結論不正確,那但願可以提出具體的狀況。
但願你們若作過驗證碼的都再回頭看看本身的驗證碼內容在服務器上及時銷燬沒有。這個錯誤很容易犯,我在某大公司的網站上都發現了這個漏洞,可見犯此錯的網站絕對不在少數。
最後但願你們的網站更加安全,更加健壯。
【出自 博客園深藍居

0html

收藏數據庫

fangdaren

15篇文章,3W+人氣,0粉絲

相關文章
相關標籤/搜索