Django-tastypie: Necesita un valor predeterminado mucho más seguro: `DjangoAuthorization` no debería permitir el acceso de` lectura` a todos los objetos del modelo

Creado en 6 ene. 2016  ·  10Comentarios  ·  Fuente: django-tastypie/django-tastypie

Mirando tastypie/authorization.py (aproximadamente como 133 - rama maestra el 1/4/2016), el valor predeterminado, tanto read_list como read_details bypass user.has_perm() cheque, que es bastante inseguro y es un valor predeterminado incorrecto.

El valor predeterminado de administrador de Django no es convencional. Entonces, pude ver cómo se malinterpretó.

https://docs.djangoproject.com/es/1.9/topics/auth/default/#permissions -and-autorización

The Django admin site uses permissions as follows:
*   ... 
* Access to view the change list, view the “change” form and change an object is limited to users with the “change” permission for that type of object.

Básicamente, "change_xyz" es el código de permiso tanto para "leer" como para "actualizar". Creo que el mejor valor predeterminado sería seguir al administrador de Django:

diff --git a/tastypie/authorization.py b/tastypie/authorization.py
index 1d6f5aa..44b2d56 100644
--- a/tastypie/authorization.py
+++ b/tastypie/authorization.py
@@ -151,22 +151,14 @@ class DjangoAuthorization(Authorization):
         return model_klass

     def read_list(self, object_list, bundle):
-        klass = self.base_checks(bundle.request, object_list.model)
-
-        if klass is False:
-            return []
+        # By default, follows `ModelAdmin` "convention" to use `app.change_model`
+        # `django.contrib.auth.models.Permission` for both viewing and updating.
+        # https://docs.djangoproject.com/es/1.9/topics/auth/default/#permissions-and-authorization

-        # GET-style methods are always allowed.
-        return object_list
+        return self.update_list(object_list, bundle)

     def read_detail(self, object_list, bundle):
-        klass = self.base_checks(bundle.request, bundle.obj.__class__)
-
-        if klass is False:
-            raise Unauthorized("You are not allowed to access that resource.")
-
-        # GET-style methods are always allowed.
-        return True
+        return self.update_detail(object_list, bundle)

bug immediate

Comentario más útil

Cuando realiza cambios que rompen la producción, ¿puede al menos reflejar eso en el control de versiones?
El control de versiones x.y.z Tastypie se parece mucho a lo que la mayoría de la gente espera , que suele ser:

Dado un número de versión MAJOR.MINOR.PATCH, incremente:
..
PARCHE la versión cuando realiza correcciones de errores compatibles con versiones anteriores.

Pasé una hora muy incómoda rastreando esto durante una nueva implementación de producción.

Todos 10 comentarios

Aprecio que esto ya se haya fusionado, pero todavía tengo problemas con él. Antes de abrir una nueva solicitud, aquí está mi preocupación:

Desafortunadamente, este cambio rompe el código existente y, por lo tanto, es incompatible con versiones anteriores (anteriormente, todas las solicitudes GET pasarían DjangoAuthorization). Para arreglar el código de uno, las únicas dos opciones son otorgar a todos los usuarios el permiso de 'cambio' (malo), o subclase DjangoAuthorization para una implementación personalizada de las acciones de lectura (potencialmente mucho trabajo). ¿Fue esto intencionado?

Aprecio el razonamiento detrás de esto en el contexto de administración de django, pero dudo que establecer 'leer' como el equivalente a 'cambiar' en un contexto de API sea una suposición predeterminada sensata, ya que no es el comportamiento esperado. Después de todo, si GET es un método permitido junto con DjangoAuthorization, ¿por qué rechazaría el acceso basándose en el hecho de que el usuario no tiene el permiso change ?

Propongo la siguiente implementación alternativa:

_para leer_detalle_

  • si el modelo tiene un permiso view , verifique que (= mejora para aquellos que lo necesitan)
  • si no hay permiso view , siempre permita (= compatible con versiones anteriores)

_para read_list_

  • si el modelo tiene un permiso list , verifique que (= mejora para aquellos que lo necesitan)
  • si no hay permiso list , siempre permita (= compatible con versiones anteriores)

De esta manera, deliciouspie agrega un valor predeterminado que es compatible con los valores predeterminados de permisos de Django al tiempo que brinda una ruta fácil para mejorar agregando permisos de vista y lista para aquellos que lo necesitan, sin escribir una implementación personalizada de DjangoAuthorization.

Al menos debería haber una forma de especificar el permiso predeterminado:

DjangoAuthorization(read_permission='view', # applies to both list, detail if not spec'd
                    read_list_permission='view', # list only
                    read_detail_permission='view') # detail only
# while we're at it, why not add the other permissions too
DjangoAuthorization(change_permission='change',
                    delete_permission='delete',
                    add_permission='add', 
                    # ... add options as per above for each
                    # <action>_<level> permission where 
                    # action is `change,delete,add,read`,  level is `list,detail`)

@SeanHayes será quien decida.

Solo mi opinión aquí, creo que fue un problema de seguridad permitir la lectura de forma predeterminada. No lo esperaba en absoluto, hasta que estuve muy cerca de la producción. Creo que romper la compatibilidad con versiones anteriores es necesario en este contexto.

Parece que su sugerencia requiere cambiar el paso de parámetros a DjangoAuthorization a su código existente, en ese caso, creo que es más claro llamar a la clase de otra manera.

Considere, read_permission='view' caso, esto podría ser exactamente lo que necesita. No veo por qué es menos óptimo que

class ModifiedDjangoAuthorization(DjangoAuthorization):
    READ_PERM_CODE = 'view'

Si desea cambiar otro, simplemente anule los métodos. El cambio en realidad se diseñó para que sea muy fácil de realizar.

class ModifiedDjangoAuthorization(DjangoAuthorization):
    def delete_list(self, object_list, bundle):
        return self.perm_list_checks(bundle.request, 'del', object_list)

    def delete_detail(self, object_list, bundle):
        return self.perm_obj_checks(bundle.request, 'del', bundle.obj)

El cambio tuvo en cuenta las modificaciones y las facilitó. No creo que deba hacerse como init params.

Creo que fue un problema de seguridad permitir la lectura de forma predeterminada.

No cuestiono la intención del número original. Solo señalar que la implementación fusionada está rompiendo el código y los supuestos existentes de las personas sin decirlo y sin una opción eficiente para revertir.

Parece que su sugerencia requiere cambiar el paso de parámetros a DjangoAuthorization a su código existente,

Si miras mi solución propuesta nuevamente, lo que estoy defendiendo es brindar a las personas opciones con un valor predeterminado seguro y compatible con versiones anteriores. En este caso, no serían necesarios cambios en el código del usuario.

Creo que romper la compatibilidad con versiones anteriores es necesario en este contexto.

No estoy de acuerdo. El cambio, tal como se fusionó actualmente, no solo rompe la compatibilidad con versiones anteriores, sino que también introduce un problema de seguridad potencial mucho mayor, ya que la forma obvia (implícita en la implementación actual) es asignar el permiso de cambio a todos los usuarios a los que se les debería permitir GET.

Francamente, no veo cómo el permiso change para acciones de lectura aumenta la seguridad, ya que al mismo tiempo este permiso también permite PUT. Combinar permisos para diferentes acciones no parece una buena opción.

Desafortunadamente, su ModifiedDjangoAuthorization no funcionará a menos que realmente agregue el permiso view al modelo, por lo que nuevamente está rompiendo la compatibilidad con versiones anteriores. Al menos requiere cambiar el código, por lo que rompemos la compatibilidad con versiones anteriores y obligamos a los usuarios a reelaborar su base de código.

Por supuesto, anular es siempre una opción para lograr los requisitos específicos de uno, sin embargo, creo que la idea general en deliciouspie es proporcionar valores predeterminados sensibles y seguros que no requieran agregar un código personalizado ...

En resumen, creo que este cambio debería revertirse para una mejor implementación.

El permiso change proviene del propio Django. Es el valor predeterminado de Django, así es como se configura la aplicación Django Admin. La opción view no lo es. Yo personalmente nombré el mío como read .

No estoy seguro de lo que quiere decir al marcar model . Si te refieres a comprobar el _meta, es posible que esté incompleto. Si te refieres a ir a la base de datos, lo encontré innecesariamente caro.

En mi opinión, lo que propuso parece estar en el lado de "demasiada magia" para una autorización predeterminada. Simplemente tener un valor predeterminado seguro, fácilmente reemplazable, parece ser suficiente. Pero esa es solo mi opinión.

El cambio se documentó aquí: https://github.com/django-tastypie/django-tastypie/blob/master/docs/release_notes/v0.13.2.rst

Francamente, no veo cómo el permiso de cambio para acciones de lectura aumenta la seguridad, ya que al mismo tiempo este permiso también permite PUT. Combinar permisos para diferentes acciones no parece una buena opción.

Sabemos que si un usuario puede cambiar algo, entonces puede leerlo, así es como hace las cosas el administrador de Django.

Esta versión es más segura porque obliga al desarrollador a pensar en lo que está haciendo. Si en lugar de inventar su propio permiso de "lectura", el desarrollador elige deliberadamente otorgar permisos de "cambio" a todos cuando solo deberían tener permisos de "lectura", ese es su problema; No puedo evitar que otros desarrolladores hagan cosas estúpidas deliberadamente, estoy aquí para evitar que Tastypie haga cosas estúpidas. El objetivo de este cambio era evitar los permisos de lectura globales en cualquier recurso que usara DjangoAuthorization, lo que los desarrolladores podrían no esperar; el nuevo comportamiento está en línea con lo que los desarrolladores experimentan en el administrador de Django.

Si quieres el comportamiento anterior:

  1. No actualice a 0.13.2.
  2. O anule los métodos read_list and read_detail`.

Si cree que la documentación podría mejorarse, no dude en enviar un PR.

Agradecemos sus comentarios. gracias por el enlace al documento, es justo, mi culpa por perderlo (tenga en cuenta que el problema está asignado a v0.13.4 mientras que los documentos están en v0.13.2).

Déjame hacer algunas observaciones finales desde mi punto de vista:

Sabemos que si un usuario puede cambiar algo, entonces puede leerlo, así es como hace las cosas el administrador de Django.

El administrador de Django usa el permiso change porque la interfaz de administración _es_ sobre _cambiar_ objetos. Tiene sentido ahí. La solicitud GET en una API REST, por definición, se trata de _lectura / visualización_. Creo que la mayoría de los desarrolladores simplemente no esperan que DjangoAuthorization rechace la lectura basándose en un permiso faltante para cambiar.

Esta versión es más segura porque obliga al desarrollador a pensar en lo que está haciendo.

Una de las características anunciadas de deliciouspie es proporcionar _ valores predeterminados razonables_. ¿No sería bastante razonable suponer que los métodos GET (por definición: lectura) y PUT (cambio) de una API, al ser operaciones diferentes en todos los propósitos y propósitos, también requieren permisos diferentes?

Estaré feliz de contribuir con un PR en la línea de lo que escribí si ustedes piensan que es una adición valiosa.

El permiso de cambio proviene del propio Django. (...) La vista de opciones no lo es.

hay un PR pendiente en Django para agregar un view permission por lo que usé la vista.

No vamos a permitir operaciones de lectura públicas / globales de forma predeterminada. Eso es definitivo. Y no vamos a intentar adivinar cómo los desarrolladores tienen configurados sus permisos cuando actualmente no existe una forma estándar de hacerlo. Ni siquiera estamos seguros de si llamarlo "leer" o "ver", y no quiero que un montón de gente venga aquí diciendo "Lo hago de esta manera" o "la nueva versión de Django lo llama de otra manera". .

Si y cuando Django admita un permiso de lectura / visualización listo para usar, lo cambiaremos. Por ahora, los desarrolladores solo tendrán que escribir un código personalizado para manejar la forma personalizada en que manejan los permisos.

Sé que este problema está cerrado, pero solo quiero intervenir y decir que este cambio básicamente me ha impedido migrar un proyecto existente a 0.13.x.

@miraculixx Creo que tiene toda la razón con respecto al hecho de que requerir permisos de cambio simplemente para ver datos es un poco loco. Supongo que podemos culpar a Django por de alguna manera, aún así, no tener el concepto de un permiso de visualización (lo que arruina el mío en otro nivel, creo que es una locura que un proyecto que incluye un componente de administración CRUD simplemente no tenga un permiso para la parte READ de CRUD).

Cuando realiza cambios que rompen la producción, ¿puede al menos reflejar eso en el control de versiones?
El control de versiones x.y.z Tastypie se parece mucho a lo que la mayoría de la gente espera , que suele ser:

Dado un número de versión MAJOR.MINOR.PATCH, incremente:
..
PARCHE la versión cuando realiza correcciones de errores compatibles con versiones anteriores.

Pasé una hora muy incómoda rastreando esto durante una nueva implementación de producción.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

bmihelac picture bmihelac  ·  40Comentarios

bastbnl picture bastbnl  ·  10Comentarios

lordi picture lordi  ·  6Comentarios

hashemian picture hashemian  ·  6Comentarios

adamzap picture adamzap  ·  18Comentarios