I'm creating multiple Django apps with vote possibilities, so I made an app vote
to handle all this votes. In my templates I'm including an ajax-function named vote
. To know on which model I'm liking I add app_name
and model_name
to the vote
function (I made some templatetags to get these values). In my views.py I use model = apps.get_model(app_name, model_name)
to get the model class. But now I'm worried a hacker could do something with the app_name
and model_name
values.
vote/ajax.html (only function):
function vote(bool){
$.ajax({
type: "post",
timeout: 8000,
url: '{% url 'ajax:vote' %}',
dataType: 'json',
data: {
'csrfmiddlewaretoken': getCookie('csrftoken'),
'model_name': "{{ model|get_model_name }}",
'app_name': "{{ model|get_app_name }}",
'voted': bool,
'id': "{{ model.id }}",
},
success: function(data) {
if (!data.error){
if (bool){
$(".half .fa-thumbs-up").removeClass("far").addClass("fas");
$(".half #count").text(parseInt($(".half #count").text()) + 1);
} else {
$(".half .fa-thumbs-down").removeClass("far").addClass("fas");
$(".half #count").text(parseInt($(".half #count").text()) - 1);
}
}
}
});
}
ajax/views.py:
def vote(request):
try:
app_name = request.POST.get("app_name")
model_name = request.POST.get("model_name")
id = request.POST.get("id")
votedFor = True if request.POST.get("voted") == "true" else False
except ValueError:
return JsonResponse({"error": True})
model = apps.get_model(app_name, model_name)
if model is None or id is None:
return JsonResponse({"error": True})
try:
usable_model = model.objects.get(id=id)
except model.DoesNotExist:
return JsonResponse({"error": True})
try:
usable_model.vote._meta.get_field("votes")
except FieldDoesNotExist:
return JsonResponse({"error": True})
usable_model.vote.vote(request, votedFor)
return JsonResponse({"error": False})
vote/functions:
def vote(self, request, votedFor):
if request.user.is_authenticated:
if not UserVoted.objects.filter(User=request.user, Vote=self).exists():
UserVoted.objects.create(User=request.user, Vote=self, votedFor=votedFor)
self._like_or_dislike(votedFor)
return True
return False
ip = get_client_ip(request)
if ip:
if not UserVoted.objects.filter(ip=ip, Vote=self).exists():
UserVoted.objects.create(ip=ip, Vote=self, votedFor=votedFor)
self._like_or_dislike(votedFor)
return True
return False
return False
def _like_or_dislike(self, votedFor):
if votedFor is not None:
Vote.objects.filter(id=self.id).update(votes=F('votes') + 1) if votedFor else Vote.objects.filter(id=self.id).update(votes=F('votes') - 1)
return True
return False
I already manipulated app_name
and model_name
and the server didn't crash but I don't know what a hacker can do. Can he crash my server when he manipulate these values? (maybe "ajax-injection" or something like this?)
-
5\$\begingroup\$ Your indentation is off. This being Python, indentation is very important. Remove your code, paste it in file-by-file and with every bit you paste in, select it, hit Ctrl + K. The question editor should do the rest. \$\endgroup\$Mast– Mast ♦2019年01月22日 21:21:27 +00:00Commented Jan 22, 2019 at 21:21
1 Answer 1
Question
But now I'm worried a hacker could do something with the
app_name
andmodel_name
values.Can he crash my server when he manipulate these values? (maybe "ajax-injection" or something like this?)
I'm not sure what the best approach to this would be. Perhaps it would be wise to define a list of the values that are acceptable for a user to pass there, though maybe that wouldn't be sufficient.
Other review points
function vote(bool){
The name bool
is not very descriptive. Given that it is used as the value for the voted
parameter a name like voted
would be more appropriate. And if your code complies with ecmascript-6 standards you could simply write
voted
in the list of parameters as a shorthand for voted: voted
The success handler looks like this:
success: function(data) { if (!data.error){ if (bool){ $(".half .fa-thumbs-up").removeClass("far").addClass("fas"); $(".half #count").text(parseInt($(".half #count").text()) + 1); } else { $(".half .fa-thumbs-down").removeClass("far").addClass("fas"); $(".half #count").text(parseInt($(".half #count").text()) - 1); }
There is quite a bit of redundancy in both cases. Also parseInt()
calls should pass a radix1 .
if (!data.error){
const thumbClass = '.fa-thumbs-' + bool ? 'up' : 'down';
$(".half " + thumbClass).removeClass("far").addClass("fas");
const toAdd = bool ? 1 : -1;
$(".half #count").text(parseInt($(".half #count").text(), 10) + toAdd);
In the vote
function in ajax/views.py there is this line:
votedFor = True if request.GET.get("voted") == "true" else False
This could be simplified to just:
votedFor = request.GET.get("voted") == "true"
-
1\$\begingroup\$ Wow thank you for your answer for my old question, that isn`t very common! I already found a better way. I register all models in a global dict by saving the model class with a random ID. In the frontend then I use the ID and send it, in the backend I then can easily get the model by the ID. \$\endgroup\$Myzel394– Myzel3942020年04月09日 20:21:07 +00:00Commented Apr 9, 2020 at 20:21
Explore related questions
See similar questions with these tags.