2010年10月10日 星期日

.NET 4 + MVC2 設定真正 ValidateRequest = false 並回應更友善的錯誤頁面與錯誤訊息

因為.NET 4中,對惡意程式碼的欄截(AntiXSS),已經提升到BeginRquest的層面了,請看ASP.NET4 WhitePaper,若我們再搭配MVC2架構,想要「不啟用」ValidateRequest,則原先我們要在View頁面上自訂

<%@ Page Language="C#" ValidateRequest="false" %>

或者是在Action上方加上[ValidateInput(false)]

   1:  [ValidateInput(false)]
   2:  public class HomeController : Controller
   3:  {
   4:      :
   5:  }

上面這兩個方式都還是會拋出錯誤畫面如下圖,無法真正的如同之前可以將ValidateRequest設為「不啟用」的狀態,所以我們在Action內並無法欄截使用者輸入的惡意程式(如<script>....</script>)。


如何提供給使用者更友善的介面與錯誤訊息

假設我們現在有一個登入頁面如下圖,讓使用者輸入UserID與UserPassword,我們希望若是使用者輸入惡意程式碼,我們要在應用程式這個層面加以欄截並且顯示出錯誤訊息給使用者看,當然我們也可以直接導到一個客製化的error page,但是這不是很友善的介面,因為使用者還要再自行連回登入頁面,然後再輸入一次UserID與UserPassword。



我們可以透過下面的步驟,實現我們在.NET 4 + MVC2 中將惡意輸入程式碼加以欄截並且顯示出錯誤訊息給使用者。

1. web.config中

我們先設定web.config中加上如下的requestValidationMode

   1:  <system.web>
   2:      <httpRuntime requestValidationMode="2.0" />
   3:  </system.web>

2.controller中

因為之前提過.NET 4中對惡意程式碼的欄截(AntiXSS),已經提升到BeginRquest的層面了,所以我們必須在每一個controller在啟始化過程中,設定ValidateRequest=false,然後我們就可在這個controller的Action中去欄截與判斷使用者輸入的每一個正常與惡意的字元。下面為我們再Request的Initialize時候,加上ValidateRequest=false。

   1:  [HandleError(View = "Error")]
   2:  public class HomeController : Controller
   3:  {       
   4:      //一定要加上去 
   5:      protected override void Initialize(RequestContext requestContext)
   6:      {
   7:          this.ValidateRequest = false;           
   8:          base.Initialize(requestContext);
   9:      }
  10:   
  11:      [HttpPost]
  12:      public ActionResult Index(User user)
  13:      {
  14:          if (!ModelState.IsValid)
  15:          {
  16:              return View(user)
  17:          }    
  18:              :
  19:              :
  20:      }
  21:  }

3.使用者類別(User.cs)中

可以按照MVC2規範中的方法,我們去設定每個欄位所要檢查的各種條件,與回應的錯誤訊息。

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.ComponentModel;
   5:  using System.ComponentModel.DataAnnotations;
   6:  using HoraceRobot.MVC2.Models;
   7:   
   8:  namespace HoraceRobot.MVC2.ViewModels
   9:  {
  10:      public class User
  11:      {
  12:          [Required(ErrorMessage = "不可為空白")]
  13:          [RegularExpression("^[0-9a-zA-Z_]{1,12}$", 
  14:              ErrorMessage= "使用者帳號錯誤")]
  15:          public string userID { get; set; }
  16:   
  17:          [Required(ErrorMessage = "不可為空白")]
  18:          [RegularExpression(@"^[^\<^\>]{6,16}$",
  19:              ErrorMessage= "使用者帳號錯誤")]
  20:          public string userPwd { get; set; }    
  21:          :
  22:          :
  23:      }
  24:  }

透過上面的設定與程式的撰寫,我們實際來驗證一下,是否可達到我們想要的,就是可以將惡意程式碼欄截並提供使用者更友善的介面與錯誤訊息。現在假設我們輸入一些不正確且具惡意的程式碼當作是登入頁的UserID與UserPassword,如下圖:


按下登入按鈕後,可以發現,此時我們可以真的欄截這些惡意的程式碼,並且透過上述第3點User類別中,我們所設定的欄位檢核條件與錯誤訊息,回應給登入頁錯誤訊息。而這些並非是我們自己撰寫很多客製化的程式,而只是遵循著ASP.NET MVC2中的各種規範去進行使用者輸入欄位驗證與檢核,配合在Controller中BeginRequest的時候去設定ValidateRequest = false來達成我們的需求。如下圖:


另外,因為我們將ValidateRequest = false,且在web.config中將requestValidationMode設回之前的2.0版本,這的確是有失.NET 4增強對抗惡意程式或XSS攻擊的美意,但是安全性的措施實在不嫌多,雖然我們按照MVC2的規範,我們都已經會對輸入與輸出前加上Html.Encode()或是Url.Encode(),但若想要再加強網站的安全防護,也可以参考Phil Haack的文章--Using AntiXss As The Default Encoder For ASP.NET,進一步增強網站Encoder機制,去防止全面性的XSS攻擊。

沒有留言:

張貼留言