雖然許多文章曾經討論過J2EE最佳實踐。那麼,為什麼我還要再寫一篇文章呢?本文究竟與以前的文章有何不同或者說比其他文章好在哪呢?
<!-- frame contents -->
<!-- /frame contents -->
首先,本文的目標讀者是正在從事技術工作的架構師。為了避免浪費大家的才智,我會避免講述一些陳腐的最佳實踐,例如"日常構建(build daily)"、"測試一切(test everything)"和"經常集成( integrate often)。 任何具有稱職架構師的項目都有分工明確的、定義良好的團隊結構。他們還為進行編碼檢查、構建代碼(每日或在需要時)、進行測試(單元、集成和系統的)、部署和配置/釋放治理而具備已記錄的過程。
其次,我將跳過通常吹捧的最佳實踐,例如"基於接口的設計"、"使用聞名的設計模型"以及"使用面向服務的架構"等。相反,我將集中講述我曾學過並且使用了若干年的6(不是很多)個方面的in-the-trench課程。最後,本文的目的是讓您思考一下自己的架構,提供工作代碼示例或者解決方案超出了本文的范圍。下面就讓我介紹一下這6課:
第1課:切勿繞過服務器端驗證 作為一位軟件顧問,我曾有機會不但設計並實現了Web應用程序,而且還評估/審核了許多Web應用程序。在復雜的、並且用javascript客戶端封裝的應用程序內,我經常碰到對用戶輸入信息執行大量檢查的Web頁面。即使Html元素具有數據有效性的屬性也如此,例如MAXLENGTH。只有在成功驗證所有輸入信息後,才能提交HTML表單。結果,一旦服務器端收到通知表單(請求),便恰當地執行業務邏輯。
在此,您發現問題了麼?開發人員已經做了許多重要的假設。例如,他們假設所有的Web應用程序用戶都同樣老實。開發人員還假設所有用戶將總是使用他們測試過的浏覽器訪問Web應用程序。還有很多其他的假設。這些開發人員忘記了利用可以免費得到的工具,通過命令行很輕易地模擬類似浏覽器的行為。事實上,通過在浏覽器窗口中鍵入適當的URL,您可以發送任何"posted"表單,盡管如此,通過禁用這些頁面的GET請求,您很輕易地阻止這樣的"表單發送"。但是,您不能阻止人們模擬甚至創建他們自己的浏覽器來入侵您的系統。
根本的問題在於開發人員不能確定客戶端驗證與服務器端驗證的主要差別。兩者的主要差別不在於驗證究竟發生在哪裡,例如在客戶端或者在服務器端。主要的差別在於驗證背後的目的不同。
客戶端驗證僅僅是方便。執行它可為用戶提供快速反饋??使應用程序似乎做出響應,給人一種運行桌面應用程序的錯覺。
另一方面,服務器端驗證是構建安全Web應用程序必需的。不管在客戶端一側輸入的是什麼,它可以確保客戶端送往服務器的所有數據都是有效的。
因而,只有服務器端驗證才可以提供真正應用程序級的安全。許多開發人員陷入了錯誤感覺的圈套:只有在客戶端進行所有數據的驗證才能確保安全。下面是說明此觀點的一個常見的示例:
一個典型的登錄頁面擁有一個用來輸入用戶名的文本框和一個輸入密碼的文本框。在服務器端,某人在接收servlet中可能碰到一些代碼,這些代碼構成了下面形式的SQL查詢:
"SELECT * FROM SecurityTable WHERE username = '" + form.getParameter("username") + "' AND passWord = '" + form.getParameter("password") + "';",並執行這些代碼。假如查詢在結果集的某一行返回,則用戶登錄成功,否則用戶登錄失敗。
第一個問題是構造SQL的方式,但現在讓我們暫時忽略它。假如用戶在用戶名中輸入"Alice'--"會怎樣呢?假設名為"Alice"的用戶已經在SecurityTable中,這時此用戶(更恰當的說法是黑客)成功地登錄。我將把找出為什麼會出現這種情況的原因做為留給您的一道習題。
許多創造性的客戶端驗證可以阻止一般的用戶從浏覽器中這樣登錄。但對於已經禁用了JavaScript的客戶端,或者那些能夠使用其他類似浏覽器程序直接發送命令(HTTP POST和GET命令)的高級用戶(或者說黑客)來說,我們又有什麼辦法呢?服務器端驗證是防止這種漏洞類型所必須的。這時,SSL、防火牆等都派不上用場了。
第2課:安全並非是附加物 如第1課所述,我曾有幸研究過許多Web應用程序。我發現所有的JavaServer Page(jsp)都有一個共同的主題,那就是具有類似下面偽代碼的布局: