You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 2.2中安全更新模型的正确方法(CRUD脚手架隐藏字段问题)

Great question—this is such a common gotcha with ASP.NET Core's scaffolded CRUD pages, and it’s awesome that you’re prioritizing security here instead of just rolling with the default code. The core issue here is that scaffolded code prioritizes speed over security, so we need to adjust it to avoid trusting client-submitted data entirely. Here’s how to fix this properly:

Core Rule: Never Trust Client-Submitted Data

No matter what controls you use (hidden fields or not), the backend should always verify and overwrite untrusted client data with values from your database. Clients can tamper with any data sent to the server—hidden fields are just as vulnerable as visible ones.

The best approach is to separate what the client can edit from your database entity. Create a ViewModel that only includes the fields users are allowed to modify, and pass the record ID via the route (not the form). This way, clients never even get a chance to send Id or CreatedBy values.

Step 1: Create the ViewModel

public class EditTestViewModel
{
    [Required]
    [Display(Name = "Your Editable Field")]
    public string Blahblah { get; set; }
    // Only include fields users should be able to change—no Id, CreatedBy, etc.
}

Step 2: Update the Controller

Use the route ID to fetch the trusted record from your database, then map only the editable fields from the ViewModel to the entity:

// GET: Test/Edit/5
[HttpGet]
public IActionResult Edit(int id)
{
    var testRecord = _context.Tests.Find(id);
    if (testRecord == null)
    {
        return NotFound();
    }

    // Map only editable fields to the ViewModel
    var viewModel = new EditTestViewModel
    {
        Blahblah = testRecord.Blahblah
    };

    return View(viewModel);
}

// POST: Test/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, EditTestViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        return View(viewModel);
    }

    // Fetch the TRUSTED record from the database
    var existingTest = _context.Tests.Find(id);
    if (existingTest == null)
    {
        return NotFound();
    }

    // ONLY update the fields users are allowed to change
    existingTest.Blahblah = viewModel.Blahblah;
    // Leave CreatedBy, Created, and Id as their original database values—never touch client data for these

    // Optional: Add permission check here (e.g., only allow the creator to edit)
    if (existingTest.CreatedBy != User.Identity.Name && !User.IsInRole("Admin"))
    {
        ModelState.AddModelError("", "You don't have permission to edit this record.");
        return View(viewModel);
    }

    _context.SaveChanges();
    return RedirectToAction(nameof(Index));
}

Step 3: Update the View

Remove the hidden Id and CreatedBy fields—your form only needs the editable fields from the ViewModel:

<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <!-- No hidden Id/CreatedBy fields needed -->
    <div class="form-group">
        <label asp-for="Blahblah" class="control-label"></label>
        <input asp-for="Blahblah" class="form-control" />
        <span asp-validation-for="Blahblah" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input type="submit" value="Save" class="btn btn-primary" />
    </div>
</form>

If you want to stick with your original entity for simplicity, you can still secure it by ignoring untrusted fields and validating the record:

  1. Ignore uneditable fields with the [BindNever] attribute on your entity. This tells model binding to discard any client-submitted values for these fields:

    public class Test
    {
        public int Id { get; set; }
        public DateTime Created { get; set; }
        [BindNever] // This field will never be updated from client data
        public string CreatedBy { get; set; }
        public string Blahblah { get; set; }
    }
    
  2. Re-fetch the record from the database before saving. Even if the client tampers with the Id hidden field, you’ll validate that the record exists and that the user has permission to edit it:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(Test submittedTest)
    {
        if (!ModelState.IsValid)
        {
            return View(submittedTest);
        }
    
        var existingTest = _context.Tests.Find(submittedTest.Id);
        if (existingTest == null)
        {
            return NotFound();
        }
    
        // Update only allowed fields
        existingTest.Blahblah = submittedTest.Blahblah;
        // CreatedBy/Created stay as original database values—[BindNever] ensures submitted values are ignored
    
        // Add permission check here
        if (existingTest.CreatedBy != User.Identity.Name && !User.IsInRole("Admin"))
        {
            ModelState.AddModelError("", "Unauthorized to edit this record.");
            return View(submittedTest);
        }
    
        _context.SaveChanges();
        return RedirectToAction(nameof(Index));
    }
    

Critical Add-On: Always Validate Permissions

No matter which approach you use, you must add a permission check to ensure the current user is allowed to edit the record. For example:

  • If only the creator can edit, verify existingTest.CreatedBy == User.Identity.Name
  • If admins can edit anything, check User.IsInRole("Admin")

This prevents users from tampering with the route ID or hidden field to edit records they don’t own.


内容的提问来源于stack exchange,提问作者Orlando

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:35:20