Asegurar Breeze en el servidor para evitar actualizaciones maliciosas a claves externas
Solo estoy tratando de averiguar exactamente cuánto de mi propia seguridad necesito implementar en el lado del servidor al guardar los cambios en Breeze. En particular, estoy pensando en cómo un usuario malintencionado podría piratear manualmente la solicitud de SaveChanges, o piratear el javascript en el cliente, para pasar por alto mis reglas comerciales normales, por ejemplo, para alterar de manera malintencionada las identificaciones de claves extranjeras en mis entidades.
Quiero entender exactamente dónde debo enfocar mis esfuerzos de seguridad; No quiero perder tiempo implementando capas de seguridad que no son necesarias.
Estoy usando Breeze con .net y Entity Framework en el lado del servidor.
EjemploAquí hay un ejemplo trivial.ObjectA
tiene una referencia a unaObjectB
yObjectA
es propiedad de un particularUser
. Entonces, mi base de datos se ve así:
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
Desde este modelo, las preocupaciones de seguridad que tengo son:
No quiero que los usuarios no autenticados cambien ningún dato.No quiero que Bob pueda hacer cambios a Alice'sObjectA
No quiero que Alice intente señalarlaObjectA
en casa de BobObjectB
.No quiero que Bob intente cambiar elUser_Id
sobre suObjectA
ser aliciaLa solución para (1) es trivial; Me aseguraré de que mi método SaveChanges tenga un[Authorize]
atributo.
Puedo usar Fiddler fácilmente para crear una solicitud SaveChanges para reproducir los problemas 2 a 4; por ejemplo, puedo crear una solicitud que cambieAlice
El objeto A para apuntar a BobObjectB
. Este es el aspecto del contenido del mensaje:
"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"
}
}
}
],
Como es de esperar, cuando no se implementa seguridad en el lado del servidor, esto persiste el valor actualizado paraObjectB_Id
en la base de datos.
Sin embargo, también he confirmado que si no hay entrada paraObjectB_Id
en eloriginalValuesMap
, entonces incluso si cambio el valor deObjectB_Id
en el cuerpo principal del mensaje NO se actualiza en la base de datos.
Entonces, creo que esto significa que las reglas de seguridad generales que debo seguir en el servidor son:
[Editado el 4 de julio de 2013 - reescrito para mayor claridad]
En general:
No se puede confiar en nada en el mensaje: ni los valores en el originalValuesMap ni los valores supuestamente "sin cambios"La única excepción es la identidad de la entidad, que podemos asumir es correcta.Las propiedades supuestamente "sin cambios" pueden haberse alterado incluso si no están en el originalValuesMapPara las propiedades "Sin modificar" (propiedades que no están también en el OriginalValuesMap):
Cuando "use" cualquier propiedad "sin cambios", NO debemos usar el valor del mensaje; debemos recuperar el objeto de la base de datos y usar el valor de eso.por ejemplo, al verificar la propiedad de un objeto para garantizar que el usuario tenga permiso para cambiarlo, no podemos confiar en un UserId en el mensaje; debemos recuperar la entidad de la base de datos y usar el valor UserId de esePara cualquier otra propiedad "sin cambios", que no estemos usando de ninguna manera, no debemos preocuparnos si se ha manipulado porque, incluso si lo ha hecho, el valor manipulado no se conservará en la base de datos.Para propiedades modificadas (propiedades que también se encuentran en el originalValuesMap):
Las reglas comerciales pueden evitar que se modifiquen propiedades particulares. Si este es el caso, deberíamos implementar una verificación para cada regla.
Si se permite que se modifique un valor y es una clave externa, probablemente deberíamos realizar una verificación de seguridad para garantizar que la identidad de la sesión permita que el nuevo valor sea utilizado
No debemos usar ninguno de los valores originales en el originalValuesMap, ya que estos pueden haber sido manipulados.
[Fin de edición]
Implementando las ReglasSuponiendo que estas reglas son correctas, supongo que hay un par de opciones para implementar la seguridad en torno a las claves externas cambiadas:
Si las reglas de negocios no permiten cambios en un campo en particular, rechazaré la solicitud de SaveChangesSi las reglas comerciales SÍ permiten cambios en un campo en particular, verificaré que se permita el nuevo valor. Al hacer esto, NO PUEDES usar eloriginalValuesMap
; Tendré que ir a la base de datos (u otra fuente confiable, por ejemplo, cookie de sesión)Aplicando estas reglas a las preocupaciones de seguridad que mencioné anteriormente,
preocupación de seguridad (2). Tendré que verificar la identidad del usuario en la sesión con elUser_ID
sobre elObjectA
que se encuentra actualmente en la base de datos. Esto se debe a que no puedo confiar en el ID_de_usuario en la solicitud, incluso si no está en eloriginalValuesMap
.
preocupación de seguridad (3). Si las reglas de negocio permiten un cambio deObjectB
, Tendré que comprobar quién posee el nuevo valor deObjectB_Id
; Haré esto recuperando el ObjectB especificado de la base de datos. Si estoObjectB
no es propiedad deObjectA
Es probable que quiera rechazar los cambios.
preocupación de seguridad (4). Si las reglas de negocio permiten un cambio deUser
, esto ya está cubierto por (2).
Entonces, realmente, estoy buscando confirmación de que estoy pensando en la dirección correcta.
¿Son correctas mis reglas generales?¿Mi implementación de las reglas parece razonable?¿Me estoy perdiendo algo?¿Estoy sobre complicando las cosas?