The Problem
I'm just trying to figure out exactly how much of my own security I need to implement on the server side when saving changes in Breeze. In particular, I'm thinking about how a malicious user could manually hack the SaveChanges request, or hack the javascript in the client, to bypass my normal business rules - for example, to maliciously alter foreign key IDs on my entities.
I want to understand exactly where I need to focus my security efforts; I don't want to waste time implementing layers of security that are not required.
I'm using Breeze with .net and Entity Framework on the server side.
Example
Here's a trivial example. ObjectA
has a reference to an ObjectB
, and ObjectA
is owned by a particular User
. So, my database looks like this:
ObjectA:
Id ObjectB_Id SomeField User_Id
1 1 Alice's ObjectA 1
2 2 Bob's ObjectA 2
ObjectB:
Id SomeOtherField
1 Foo
2 Bar
User:
Id Name
1 Alice
2 Bob
From this model, the security concerns I have are:
- I don't want unauthenticated users to be changing any data
- I don't want Bob to be able to make any changes to Alice's
ObjectA
- I don't want Alice to try to point her
ObjectA
at Bob'sObjectB
. - I don't want Bob to try to change the
User_Id
on hisObjectA
to be Alice.
The solution for (1) is trivial; I'll ensure that my SaveChanges method has an [Authorize]
attribute.
I can easily use Fiddler to build a SaveChanges request to reproduce issues 2 to 4 - for example, I can build a request which changes Alice
's ObjectA to point to Bob's ObjectB
. This is what the message content might look like:
"entities":
[
{
"Id":1,
"ObjectB_Id":2,
"SomeField":"Alice's ObjectA",
"User_Id":1,
"entityAspect":
{
"entityTypeName":"ObjectA:#MyNamespace",
"defaultResourceName":"ObjectAs",
"entityState":"Modified",
"originalValuesMap":
{
"ObjectB_Id":"1"
},
"autoGeneratedKey":
{
"propertyName":"Id",
"autoGeneratedKeyType":"Identity"
}
}
}
],
As I'd expect, when no security is implemented on the server side, this persists the updated value for ObjectB_Id
into the database.
However, I've also confirmed that if there is no entry for ObjectB_Id
in the originalValuesMap
, then even if I change the value for ObjectB_Id
in the main body of the message it is NOT updated in the database.
General Rules?
So, I think this means that the general security rules I need to follow on the server are:
[Edited 4 July 2013 - rewritten for clarity]
In general:
- Nothing in the message can be trusted: neither values in the originalValuesMap nor supposedly "unchanged" values
- The only exception is the identity of the entity, which we can assume is correct.
- Supposedly "unchanged" properties may have been tampered with even if they are not in the originalValuesMap
For "Unchanged" properties (properties which are not also on the originalValuesMap):
- When "using" any "unchanged" property, we must NOT use the value from the message; we must retrieve the object from the database and use the value from that.
- for example, when checking owenership of an object to ensure that the user is allowed to change it, we cannot trust a UserId on the message; we must retrieve the entity from the database and use the UserId value from that
- For any other "unchanged" property, which we are not using in any way, we don't need to worry if it has been tampered with because, even if it has, the tampered value will not be persisted to the database
For changed properties (properties which are also on the originalValuesMap):
Business rules may prevent particular properties being changed. If this is the case, we should implement a check for each such rule.
If a value is allowed to be changed, and it is a foreign key, we should probably perform a security check to ensure that the new value is allowed to be used by the session identity
We must not use any of the original values in the originalValuesMap, as these may have been tampered with
[End of edit]
Implementing the Rules
Assuming that these rules are correct, I guess there are a couple of options to implement security around the changed foreign keys:
- If the business rules do not allow changes to a particular field, I will reject the SaveChanges request
- If the business rules DO allow changes to a particular field, I will check that the new value is allowed. In doing this, CANNOT use the
originalValuesMap
; I'll need to go to the database (or other trusted source, eg session Cookie)
Applying these rules to the security concerns that I gave above,
security concern (2). I'll need to check the user identity on the session against the
User_ID
on theObjectA
that is currently in the database. This is because I cannot trust the User_ID on the request, even if it is not in theoriginalValuesMap
.security concern (3). If the business rules allow a change of
ObjectB
, I will need to check who owns the new value ofObjectB_Id
; I'll do this by retrieving the specified ObjectB from the database. If thisObjectB
is not owned byObjectA
's owner, I probably want to reject the changes.security concern (4). If the business rules allow a change of
User
, this is already covered by (2).
Questions
So, really, I'm looking for confirmation that I'm thinking along the right lines.
- Are my general rules correct?
- Does my implementation of the rules sound reasonable?
- Am I missing anything?
- Am I over complicating things?