Cómo evitar que el usuario cambie la URL para ver otros datos de envío Django

Soy nuevo en el mundo del desarrollo web, en Django y en las aplicaciones que requieren proteger la URL de los usuarios que cambian el foo / bar / pk para acceder a los datos de otros usuarios.

¿Hay alguna manera de prevenir esto? ¿O hay una forma integrada de evitar que esto suceda en Django?

Por ejemplo: foo/bar/22 se puede cambiar a foo/bar/14 y expone los datos de usuarios anteriores.

He leído las respuestas a varias preguntas sobre este tema y he tenido poca suerte en una respuesta que pueda explicar esto de manera clara y coherente y el enfoque para evitarlo. No sé mucho sobre esto, así que no sé cómo formular esta pregunta para investigarla correctamente. Por favor explícame esto como si yo fuera 5.

Hay algunas maneras en que puede lograr esto:

Si tiene el concepto de inicio de sesión, solo restrinja la URL a:

 /foo/bar/ 

y en el código, user=request.user y muestra datos solo para el usuario que ha iniciado sesión.

Otra forma sería:

 /foo/bar/{{request.user.id}}/ 

y en la vista:

 def myview(request, id): if id != request.user.id: HttpResponseForbidden('You cannot view what is not yours') #Or however you want to handle this 

Incluso puede escribir un middleware que redirija al usuario a su página /foo/bar/userid , o a la página de inicio de sesión si no ha iniciado sesión.

Simplemente verifique que el objeto recuperado por la clave primaria pertenezca al usuario solicitante. En la vista esto sería

if some_object.user == request.user: ...

Esto requiere que el modelo que representa el objeto tenga una referencia al modelo de usuario.

Recomendaría usar django-guardian si desea controlar el acceso por objeto. Así es como se vería después de configurar las configuraciones e instalarlas (esto es de los documentos de django-guardian ):

 >>> from django.contrib.auth.models import User >>> boss = User.objects.create(username='Big Boss') >>> joe = User.objects.create(username='joe') >>> task = Task.objects.create(summary='Some job', content='', reported_by=boss) >>> joe.has_perm('view_task', task) False 

Si prefieres no usar una biblioteca externa, también hay formas de hacerlo en las vistas de Django .

Así es como podría verse:

 from django.http import HttpResponseForbidden from .models import Bar def view_bar(request, pk): bar = Bar.objects.get(pk=pk) if not bar.user == request.user: return HttpResponseForbidden("You can't view this Bar.") # The rest of the view goes here... 

En django, el usuario que está conectado actualmente está disponible en sus vistas como el user de la propiedad del objeto de solicitud.

La idea es filtrar sus modelos por el usuario que inició sesión primero, y luego, si hay resultados, solo se muestran esos resultados.

Si el usuario está intentando acceder a un objeto que no les pertenece, no muestre el objeto.

Una forma de ocuparse de todo esto es usar la función de acceso directo get_object_or_404 , que generará un error 404 si no se encuentra un objeto que coincida con los parámetros dados.

Usando esto, solo podemos pasar la clave primaria y el usuario actual registrado a este método, si devuelve un objeto, eso significa que la clave primaria pertenece a este usuario, de lo contrario devolverá un 404 como si la página no existiera .

Es muy sencillo conectarlo a tu vista:

 from django.shortcuts import get_object_or_404, render from .models import YourModel def some_view(request, pk=None): obj = get_object_or_404(YourModel, pk=pk, user=request.user) return render(request, 'details.html', {'object': obj}) 

Ahora, si el usuario intenta acceder a un enlace con un pk que no les pertenece, se genera un 404.

Vas a querer ver la autenticación y autorización del usuario, que son provistas por el paquete Auth de Django . También hay una gran diferencia entre las dos cosas.

La autenticación es asegurarse de que alguien sea quien dice ser. Piense, inicie sesión. Usted consigue que alguien complete su nombre de usuario y contraseña para probar que son los dueños de la cuenta.

La autorización es asegurarse de que alguien pueda acceder a lo que intenta acceder. Por lo tanto, un usuario normal, por ejemplo, no podrá simplemente cambiar de PK.

La autorización está bien documentada en el enlace que proporcioné anteriormente. Comenzaría allí y ejecutaría algunos de los códigos de muestra. Espero eso conteste tu pregunta. Si no, espero que le proporcione suficiente información para volver y hacer una pregunta más específica.

En mi proyecto, para varios modelos / tablas, un usuario solo debería poder ver los datos que ingresó, y no los datos ingresados ​​por otros usuarios. Para estos modelos / tablas, hay una columna de usuario.

En la vista de lista, que es lo suficientemente fácil de implementar, simplemente filtre el conjunto de consultas pasado a la vista de lista para model.user = loggged_id.user.

Pero para las vistas de detalle / actualización / eliminación, al ver la PK allí en la URL, es posible que el usuario pueda editar la PK en la URL y acceder a la fila / datos de otro usuario.

Estoy usando las vistas basadas en clase incorporadas de Django.

Las vistas con PK en la URL ya tienen LoginRequiredMixin, pero eso no impide que un usuario cambie la PK en la URL.

Mi solución: “¿El usuario que inició sesión inició la combinación de esta fila” (DoesLoggedInUserOwnThisRowMixin): reemplaza el método get_object y prueba allí.

 from django.core.exceptions import PermissionDenied class DoesLoggedInUserOwnThisRowMixin(object): def get_object(self): '''only allow owner (or superuser) to access the table row''' obj = super(DoesLoggedInUserOwnThisRowMixin, self).get_object() if self.request.user.is_superuser: pass elif obj.iUser != self.request.user: raise PermissionDenied( "Permission Denied -- that's not your record!") return obj 

Voila!

Simplemente coloque la mezcla en la línea de definición de la clase de vista después de LoginRequiredMixin, y con una plantilla 403.html que emite el mensaje, está listo.