ASP.NET MVC คำขอที่อาจเป็นอันตรายตรวจพบค่าแบบฟอร์มจากไคลเอนต์เมื่อใช้ modelbinder ที่กำหนดเอง


97

รับข้อผิดพลาดที่นี่:

ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage");

ฉันจะอนุญาตเฉพาะค่าที่เลือกได้อย่างไร กล่าวคือ

[ValidateInput(false)]
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage");
    ValueProviderResult value2 = bindingContext.ValueProvider.GetValue("ConfirmationMessage2");
}


2
ขอบคุณ แต่คุณไม่ได้มองว่าปัญหาของฉันแตกต่างออกไป
DW

ปัญหารากเดียวกันที่แน่นอนข้อแตกต่างเพียงอย่างเดียวคืออาจมีวิธีการเฉพาะ MVC ในการจัดการกับมัน
Erik Philips

เมื่อใช้ EF ดูคำตอบของ bizzehdee ที่นี่stackoverflow.com/questions/17964313/…
Petr

คำตอบ:


227

คุณมีทางเลือกน้อย

ในโมเดลให้เพิ่มแอตทริบิวต์นี้ในแต่ละคุณสมบัติที่คุณต้องอนุญาต HTML - ทางเลือกที่ดีที่สุด

using System.Web.Mvc;

[AllowHtml]
public string SomeProperty { get; set; }

ในการดำเนินการควบคุมเพิ่มแอตทริบิวต์นี้เพื่ออนุญาต HTML ทั้งหมด

[ValidateInput(false)]
public ActionResult SomeAction(MyViewModel myViewModel)

กำลังดุร้ายใน web.config - ไม่แนะนำอย่างแน่นอน

ในไฟล์ web.config ภายในแท็กให้แทรกองค์ประกอบ httpRuntime ด้วยแอตทริบิวต์ requestValidationMode = "2.0" เพิ่มแอตทริบิวต์ validateRequest = "false" ในองค์ประกอบหน้า

<configuration>
  <system.web>
   <httpRuntime requestValidationMode="2.0" />
  </system.web>
  <pages validateRequest="false">
  </pages>
</configuration>

ข้อมูลเพิ่มเติม: http://davidhayden.com/blog/dave/archive/2011/01/16/AllowHtmlAttributeASPNETMVC3.aspx

ข้างต้นใช้ได้กับการใช้งาน modelbinder เริ่มต้น

Custom ModelBinder

ดูเหมือนว่าการเรียกใช้ bindingContext.ValueProvider.GetValue () ในโค้ดด้านบนจะตรวจสอบความถูกต้องของข้อมูลเสมอโดยไม่คำนึงถึงแอตทริบิวต์ใด ๆ การขุดลงในซอร์ส ASP.NET MVC พบว่า DefaultModelBinder ตรวจสอบก่อนว่าจำเป็นต้องมีการตรวจสอบความถูกต้องจากนั้นเรียกใช้เมธอด bindingContext.UnvalidatedValueProvider.GetValue () พร้อมพารามิเตอร์ที่ระบุว่าจำเป็นต้องมีการตรวจสอบความถูกต้องหรือไม่

น่าเสียดายที่เราไม่สามารถใช้รหัสกรอบงานใด ๆ ได้เนื่องจากมีการปิดผนึกเป็นส่วนตัวหรืออะไรก็ตามเพื่อปกป้องผู้พัฒนาที่เพิกเฉยไม่ให้ทำสิ่งที่เป็นอันตราย แต่ก็ไม่ยากเกินไปที่จะสร้างตัวประสานโมเดลแบบกำหนดเองที่ใช้งานได้ซึ่งเคารพแอตทริบิวต์ AllowHtml และ ValidateInput:

public class MyModelBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // First check if request validation is required
        var shouldPerformRequestValidation = controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled;

        // Get value
        var valueProviderResult = bindingContext.GetValueFromValueProvider(shouldPerformRequestValidation);
        if (valueProviderResult != null)
        {
            var theValue = valueProviderResult.AttemptedValue;

            // etc...
        }
    }
}

ส่วนที่ต้องการอื่น ๆ คือวิธีการดึงค่าที่ไม่ได้ตรวจสอบ ในตัวอย่างนี้เราใช้วิธีการขยายสำหรับคลาส ModelBindingContext:

public static class ExtensionHelpers
{
    public static ValueProviderResult GetValueFromValueProvider(this ModelBindingContext bindingContext, bool performRequestValidation)
    {
        var unvalidatedValueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider;
        return (unvalidatedValueProvider != null)
          ? unvalidatedValueProvider.GetValue(bindingContext.ModelName, !performRequestValidation)
          : bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    }
}

ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ที่http://blogs.taiga.nl/martijn/2011/09/29/custom-model-binders-and-request-validation/


ฉันมีสิ่งนี้ในคอนโทรลเลอร์ [HttpPost, ValidateInput (false)] และฉันยังคงได้รับข้อผิดพลาด
DW

ดูคำตอบที่แก้ไขของฉันพร้อมวิธีแก้ปัญหานี้เมื่อใช้ modelbinder ที่กำหนดเอง
ericdc

ขอบคุณ แต่มันไม่ชอบบรรทัดนี้ bindingContext.GetValueFromValueProvider
DW

GetValueFromValueProvider ต้องอยู่ในคลาสคงที่สาธารณะ ตรวจสอบการแก้ไขด้านบน
ericdc

Ta, valueProviderResult reutrns null tho? var valueProviderResult = bindingContext.GetValueFromValueProvider (shouldPerformRequestValidation);
DW

31

ลอง:

HttpRequestBase request = controllerContext.HttpContext.Request;
string re = request.Unvalidated.Form.Get("ConfirmationMessage")

เมื่อฉันลองสิ่งนี้ฉันได้รับข้อยกเว้นซึ่งระบุว่า: สมาชิกที่ไม่สามารถเรียกคืนได้ 'System.web.HttpRequestBase.Unvalidated' ไม่สามารถใช้เหมือนกับวิธีการ สิ่งนี้เปลี่ยนไปหรือไม่?
Stack0verflow

7
บรรทัดที่สองควรจะเป็นvar re = request.Unvalidated.Form["ConfirmationMessage"];
Stack0verflow

5

เมื่อขยายคำตอบจาก @DW ในตัวควบคุมแก้ไขของฉันในการวนซ้ำค่าฟอร์มฉันต้องแทนที่อินสแตนซ์ทั้งหมดRequest.Params.AllKeysด้วยRequest.Unvalidated.Form.AllKeysและอินสแตนซ์ทั้งหมดRequest[key]ด้วยRequest.Unvalidated.Form[key]ด้วย

นี่เป็นทางออกเดียวที่ใช้ได้ผลสำหรับฉัน


0

ตามที่ Mike Godin เขียนแม้ว่าคุณจะตั้งค่าแอตทริบิวต์ [ValidateInput (false)] คุณต้องใช้Request.Unvalidated.FormแทนRequest.Form สิ่งนี้ใช้ได้กับฉันกับ ASP.NET MVC 5


1
นี่เป็นคำแนะนำที่มีประโยชน์จริง ๆ เนื่องจากการเข้าถึงข้อมูลจากตัวควบคุมพื้นฐาน (เช่นเพื่อวัตถุประสงค์ในการบันทึกหรือการดีบัก) การเข้าถึงคำขอใด ๆ แบบฟอร์มจะแสดงข้อยกเว้นแม้ว่าโมเดลจะมีแอตทริบิวต์นี้ก็ตาม
nsimeonov

-4

นี่คือขั้นตอนในการเข้ารหัสที่ระดับไคลเอนต์และถอดรหัสที่ระดับเซิร์ฟเวอร์:

  1. โพสต์แบบฟอร์มโดยใช้วิธีส่ง jquery

  2. ในปุ่ม jquery ให้คลิกช่องเข้ารหัสวิธีเหตุการณ์ที่คุณต้องการโพสต์ไปยังเซิร์ฟเวอร์ ตัวอย่าง:

    $("#field").val(encodeURIComponent($("#field").val()))
    $("#formid").submit();
    
  3. ในระดับตัวควบคุมเข้าถึงค่ารหัสฟอร์มทั้งหมดโดยใช้

    HttpUtility.UrlDecode(Request["fieldid"])
    

ตัวอย่างตัวอย่าง:

  • ระดับตัวควบคุม:

    public ActionResult Name(string id)
    {
    
        CheckDispose();
        string start = Request["start-date"];
        string end = Request["end-date"];
        return View("Index", GetACPViewModel(HttpUtility.UrlDecode(Request["searchid"]), start, end));
    }
    
  • ระดับลูกค้า:

    <% using (Html.BeginForm("Name", "App", FormMethod.Post, new { id = "search-form" }))
    { %>
    <div>
    <label  for="search-text" style="vertical-align: middle" id="search-label" title="Search for an application by name, the name for who a request was made, or a BEMSID">App, (For Who) Name, or BEMSID: </label>
    <%= Html.TextBox("searchid", null, new {value=searchText, id = "search-text", placeholder = "Enter Application, Name, or BEMSID" })%>
    </div>
    <div>
    <input id="start-date" name="start-date" class="datepicker" type="text"  placeholder="Ex: 1/1/2012"/>
    </div>
    <div>
    <input id="end-date" name="end-date" class="datepicker" type="text"  placeholder="Ex: 12/31/2012"/>
    </div>
    <div>
    <input type="button" name="search" id="btnsearch" value="Search" class="searchbtn" style="height:35px"/>
    </div> 
    <% } %>
    

ในฟังก์ชัน Document Ready:

$(function () {     
  $("#btnsearch").click(function () {  
    $("#search-text").val(encodeURIComponent($("#search-text").val()));
    $("#search-form").submit();
  });
});

4
Jquery และเทคโนโลยีฝั่งไคลเอ็นต์ไม่มีส่วนเกี่ยวข้องกับ MVC การตรวจสอบความถูกต้องเกิดขึ้นที่ฝั่งเซิร์ฟเวอร์ด้วยเฟรมเวิร์ก MVC ไม่ใช่คำตอบที่ถูกต้อง
diegosasw

2
เนื่องจาก Microsoft ละเว้นแอตทริบิวต์ AllowHtml อย่างแท้จริงและเนื่องจากโซลูชันที่ใช้งานได้เพียงฝั่งเซิร์ฟเวอร์คือการแทนที่ฟังก์ชันการทำงานของตัวประสานรุ่นเริ่มต้นฉันขอยืนยันว่าการเข้ารหัสฝั่งไคลเอ็นต์เป็นตัวเลือกที่ถูกต้องอย่างสมบูรณ์
Jonathan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.