问题
EDIT - Just a quick edit, to start this off with a clear question! What I'm essentially asking is, what is the most effective way of protecting my entity identifiers when posting back from a view?
I've been thinking about ways to protect the ID on a POST when editing a view model. Let's take an example entity
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
And its corresponding view model:
public class PostViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
Now, when I pass this view model to a view that allows a user to edit it, I'm going to be doing something like this:
public ActionResult EditPost(PostViewModel viewModel)
{
Post post = database.Posts.Single(p => p.Id.Equals(viewModel.Id));
post.Title = viewModel.Title;
post.Content = viewModel.Content;
database.Entry(post).State = System.Data.EntityState.Modified;
database.SaveChanges();
return View(viewModel);
}
Or maybe pass the ID through the parameter list like this:
public ActionResult EditPost(int postId, PostViewModel viewModel)
{
Post post = database.Posts.Single(p => p.Id.Equals(postId));
// and the rest
}
Either way, we need to return the identifier for the entity we're updating along with the POST data. How do we make sure that the entity updated is the one intended?
I suppose we could validate whether a user has sufficient access to update this entity... but what if a user's account becomes compromised, and some random hacker starts injecting random IDs using their account? Updating all sorts of Post
s at random.
Having a complex (like a GUID) identifier is likely recommended for entities, which would make guessing a lot harder, but then this makes your nice and friendly URLs look a bit intimidating to the average user, having to pass that around when viewing a Post
for example.
How do we get the best of both worlds here? Keeping clean URLs, but protecting our entities from injection attacks?
回答1:
This is a direct reference attack, and according to oswap recommendations, you can either
- obfuscate the id by swapping it for a guid and then holding the mapping in memory/session
- hold a reference to the item in session and make sure it is the same that comes back
The way i tackle this is with an attribute, I haven't any code to hand so you will need to something like
Decorate get action with attribute
on gets, attribute clears item list from session
Pull Item from db
store items id in session for item
decorate post action with attribute
attribute makes sure modelstate is valid first (saves double validating)
attribute looks in session for id
attribute checks the id against the stored value
if id matches, action can continue
if id doesn't match, an entry is made in modelstate
using this sort of methodolgy, you can protect yourself against someone fiddling with your ids in tools like burpsuite or using the console mode of a browser to flip the hidden fields.
also, as a starter to this process, always ensure the your get item doesnt blindly get from the db, but first ensures that the person actually can get the item, ie. belongs to their datasets etc etc
回答2:
You are talking about keeping somebody from hacking their form to say they are updating record 2 when they're supposed to be editing record 1, right? And not necessarily keeping people from figuring out that a post by user ABC has an identity of 1?
Maybe it would be a bit simpler to create your tables and view models with both an identity column and a guid. All of your urls / GET processes can be built around using the identity, and you can do all of the POST processing with guids. This would keep you from having to build some in-memory mappings, using session keys, creating action filters, etc. I'm like you - I'd want to reduce the amount of dependency you have on session since it is time sensitive; once your session expires, the mapping you kept in memory expires so it would always return a false negative.
This might eliminate some re-usability in your data layer though.
Edit: But I think the best protection is like you said - validating that the account editing the record has access to edit the record. If you combine that with a guid (or something encrypted on the form), it would make it very hard to guess records at random.
You could also try adding some sort of hashed algorithm to the POST form. Something like, hash the identity, the user who created the post, the time of when the post was created and maybe some random non-updatable salt on the entity that end users can't see. You also might include something random (like the time in ticks when the page was rendered) to make sure the hash is unique every time the page loads, which would prevent somebody from farming guids / hashes (you would need to post the ticks in a separate field) if an account was compromised. Your validation process would match the hash that is POST'ed with the hash of those same fields on the entity and only perform the update if the hashes match. But this seems like overkill to me.
来源:https://stackoverflow.com/questions/16215225/most-effective-method-of-protecting-an-entity-id-when-posting-back-from-a-view