Plugging fundamental security vulerabilities in a Django web app - django

I have a Django app which users congregate to, use as a forum and gain reputation points. Majority of the users belong to underserved communities and use primitive, non-js feature phones with proxy browsers such as Opera mini, over low bandwidth internet. Essentially I'm a "Next Billion" digital non-profit.
There are some security vulnerabilities that users can exploit in my forum - I need advice in plugging those. Here are the facts.
There's no SSL certificate installed - all comm. takes place over HTTP. To post in the forum one uses the following piece of code in the django template:
<form action="{% url 'private_group_reply' slug=unique %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="unique" value="{{ unique }}">
<br><span style="color:green;">Image: </span>{{ form.image }}<br>
<br><span style="color:green;">Comment:</span>{{ form.text }}
<br>
<input class="button" type="submit" value="OK" id="id_submit">
</form>
unique is a uuid that identifies which group the comment is to be posted in.
The relevant url pattern is: url(r'^group/(?P<slug>[\w.#+-]+)/private/$', auth(PrivateGroupView.as_view()), name='private_group_reply')
In views.py, the relevant class-based view's method is:
def form_valid(self, form):
if self.request.user_banned:
return redirect("profile", slug=self.request.user.username)
else:
f = form.save(commit=False)
text = f.text
if text == self.request.user.userprofile.previous_retort:
redirect(self.request.META.get('HTTP_REFERER')+"#sectionJ")
else:
self.request.user.userprofile.previous_retort = text
self.request.user.userprofile.score = self.request.user.userprofile.score + 2
self.request.user.userprofile.save()
#print "image: %s" % f.image
if f.image:
image_file = clean_image_file(f.image)
if image_file:
f.image = image_file
else:
f.image = None
else:
f.image = None
#print "image:%s" % f.image
which_group = Group.objects.get(unique=self.request.POST.get("unique"))
reply = Reply.objects.create(writer=self.request.user, which_group=which_group, text=text, image=f.image)
GroupSeen.objects.create(seen_user= self.request.user,which_reply=reply)
try:
return redirect(self.request.META.get('HTTP_REFERER')+"#sectionJ")
except:
return redirect("private_group_reply", slug=reply.which_group.unique)
One particular user is, I think, running a script bot and flooding the group he's a part of, garnering multiple points (each posting is point-incentivized).
Though I'm new to software development, I now have a thriving community on this forum and am thus ramping up my skill-set. I've been reading a ton about SSL security, and rules of thumb any respectable web app ought to follow. What someone can help with currently is this particular case of the flooding user I've described, how to curtail such behavior within my set up. Also any general guidelines to follow will be highly appreciated as well.
Thanks in advance, and please ask for any information you need.

I'm not sure if its the answer you're looking for but rather than trying to patch up this one part of your code, you probably need to look into creating some rules/regulations on your site along with penalties for the users that break this.
If you can prove that a user is doing something against your rules/guidelines, you apply your set penalty (score rollback/bans/whatever) and this would remove the incentive for most to even try doing this.
Yes, ssl may help but who knows, and your users will always have the chance to bypass your safeguards.
In terms of the actual code? I wouldn't save the score to the model until everything else has been done. You might also want to make it an atomic transaction to roll back db changes if something goes awry when saving

Related

data How to keep form when user gets redirected back to the form when they fail a validation (Python, Django)?

I know this might be a duplicate question, but the previous one was an older question and those questions uses a form instance which doesn't really help me.
How do I keep my form data after a failed validation? I have multiple dropdowns and input fields and I hate to see my users re-do and re-type everything when they fail validation in the backend. Let's say I have this very simple form:
HTML:
<form class="" action="/register" method="post">
<label for="">First Name</label>
<input type="text" name="" value="">
<label for="">Last Name</label>
<input type="text" name="" value="">
<label for="">Password</label>
<input type="password" name="" value="">
</form>
views.py:
def register(self):
.....
if errors:
for err in errors
messages.errors(request, err)
return redirect('/')
else:
messages.success(request, "Welcome User!")
return redirect('/dashboard')
Most examples that I came across were using the form instance which uses form.save() etc. I opted out on that one. Is there a way to auto-populate my form with the data that the user submitted if they were to fail validation? Thanks!
Django form classes are the way to go. Form validation and rendering are the tasks they were build for. I would strongly recommend using them, because they also take care of security aspects, when it comes to passing user input back to the browser (all input from user land is evil!).
If you really need to achieve this without form classes, you need to add the form values to your rendering context manually - this allows you to use them in your template.
The main problem with your approach is, that you want to redirect in case of validation error. A redirect is a response to the browser that tells: I have nothing for you, please go to this location. Usually the browser does not post the data send in the first request also to the second one (which is generally a good behavior). You may work around that by answering with status code 307 instead of 302. Read e.g. Response.Redirect with POST instead of Get? for more information. Alternatively you may encode your form data into the target location using get parameters.
Again: You should have a very good reason to not just use the django approach of one view that acts on GET and POST different and handles the form properly using the form instance.

How do I pass multiple variables from one handler to another in GAE?

I want to redirect users to a confirmation page that will display both subject and content (if there is any) if they enter a valid subject, but stay on the same page and display an error if the subject is either blank or over three hundred characters.
Here is my backend code:
def post(self):
subject = self.request.get('subject')
content = self.request.get('content')
a, b = self.validSubject(subject)
if a == True and b == True:
self.redirect('/confirm')
else:
if a == False:
error = "Title cannot be blank!"
if b == False:
error = "Title cannot be over 300 characters."
self.render("newpost.html", subject = subject, content = content, error = error)
Here is the code for the newpost.html template:
<h2>New Question</h2>
<hr>
<form method="post">
<label>
<div>Title</div>
<input type="text" id="subject" name="subject">
</label>
<label>
<div>
<textarea name="content" id="postcontent"></textarea>
</div>
</label>
<b><div class="error">{{error}}</div></b>
<input type="submit">
</form>
I've tried adding action="/confirm" to the POST form, but that redirects to /confirm even if there is an error. I've looked at the webapp2 documentation but couldn't find anything on how to pass variables on a redirect. (https://webapp-improved.appspot.com/api/webapp2.html#webapp2.redirect)
I'm using webapp2 and jinja2. Thanks for any help in advance, I've been looking at this piece of code for quite a while :(
The pattern you're trying to write doesn't work within http, irrespective of what backend platform or language you're using. Your HTML is posting to the server and the GAE code is handling the post. At that point in the interaction, the browser has already submitted and is awaiting a response from the server. You can't stop the submission at that point since it's already happened.
You should consider validating the input in Javascript before the form is even submitted to the server. That way you can suppress the submission of the form in the first place if your data isn't valid.
Take a look at the following question to see an example of this:
JavaScript code to stop form submission

Django redirect page does not update the view

I'm using the Django Framework on Google App Engine.
I have multiple forms on the same view, to submit to different URL.
Trouble is after I get a form submitted: even if the called method update the datastore and some data, the previous page (where the forms are put in) is not refreshed, showing the updated data.
I could solve this problem using jQuery or some javascrip framework, appending dinamically content returned by the server but, how to avoid it?
Suggestions?
Am I wrong somewhere?
A part of "secure.html" template
<form action="/addMatch" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
Matches:
<br />
{% for m in matches%}
{{m.description}} ---> {{m.reward}}
{% endfor%}
the "/addMatch" URL view:
def addMatch(request):
form = MatchForm(request.POST)
if form.is_valid():
user = User.all().filter('facebookId =', int(request.session["pbusr"]))
m = Match(user=user.get(),description =form.cleaned_data["description"],reward=form.cleaned_data["reward"])
m.save()
return HttpResponseRedirect("/secure/")
else:
logging.info("Not valid")
return HttpResponseRedirect("/secure")
The view method whose seems not working:
#auth_check_is_admin
def secure(request):
model={}
user = User.all().filter('facebookId =', int(request.session["pbusr"]))
u = user.get()
if (u.facebookFanPageId is not None and not u.facebookFanPageId == ""):
model["fanPageName"] = u.facebookFanPageName
model["form"] = MatchForm()
model["matches"] = u.matches
else:
....
return render(request,"secure.html",model)
Francesco
Based on what you posted, it seems like you're redirecting properly and are having database consistency issues. One way to test this would be to look at the network tab in the Google Chrome developer tools:
Click on the menu icon in the upper right
Click on "Tools"
Click on "Developer Tools"
Click on "Network" in the thing that opened up at the bottom of the screen.
Now, there will be a new entry in the network tab for every request that your browser sends and every response it receives. If you click on a request, you can see the data that was sent and received. If you need to see requests across different pages, you might want to check the "Preserve log" box.
With the network tab open, go to your page and submit the form. By looking at the network tab, you should be able to tell whether or not your browser issued a new GET request to the same URL. If there is a new request for the same page but that request has the old content, then you have a datastore consistency issue. If there was NOT a new request that yielded a response with the data for the page, then you have a redirect issue.
If it turns out that you have a datastore consistency issue, then what's happening is the data is being stored, but the next request for that data might still get the old data. To make sure that doesn't happen, you need what's called "strong consistency."
In a normal App Engine project, you get strong consistency by putting entities in the same entity-group and using ancestor queries. I'm not certain of what database/datastore you're using for Django and how the different database layers interact with App Engine's consistency, so this could be wrong, but if you can give your users the right key and then fetch them from that key directly (rather than getting all users and filtering them by key), you might get strong consistency.

Django/Haystack - best option to have a listview with search capability

I have an app with a Restaurant model. I'd like to understand what is the best way to put together a view that displays the list of restaurant objects, but also has a search form above that a user could enter parameters to filter the results displayed. If no parameters are entered, all the restaurants should be shown. I'm already using haystack and have a search form, but currently it is on a standalone search.html template. I also have an ListView on a separate template, and I guess I'm looking for an end result that combines these.
I did some reading on line and it's unclear what the best way to do it is:
using just listview from Django with some queryset filtering
combining haystack SearchView with django class based views?
this is my best bet so far - creating a customized version of SearchView from Haystack
ideally, ultimately the search capabilities would include things like allow autocompleting the user's inputs, and dynamically filter the results as the user types.
any thoughts on what the best way is to go about this and any examples out there?
There probably is out there some package that gives you everything automatically, displaying the queryset list and allowing simple adding of a search bar and so on (like the admin site). But I take it you are still a beginner to web development, so I'd strongly suggest you drop the listviews, drop haystack, drop everything, and do it all yourself.
See, they're not bad approaches or anything, but they're like shortcuts. And shortcuts are good only if they shorten the original way, which in your case isn't all that long.
To give you an example, here's a simple approach to displaying a list of items:
views.py
def restaraunt_list(request):
restaraunts = Restaraunt.objects.all().order_by('name')[:100] # Just for example purposes. You can order them how you'd like, and you probably want to add pagination instead of limiting the view number arbitrarily
context = {'restaraunts': restaraunts}
return render_to_response('index.html', context)
index.html:
<ul>
{% for rest in restaraunts %}
<li>{{ rest }}</li>
{% endfor %}
</ul>
That's it. Now it displays it in a list. Now to add a search filter all you need to do is this:
index.html (add this anywhere you want it)
<form>
<input type='text' name='q' id='q'></input>
<input type='submit' value='search!'></input>
</form>
When a user sends the data from 'q' it is sent through GET, and then on server side you add this:
def restaraunt_list(request):
restaraunts = Restaraunt.objects.all()
# filter results!
if request.method == 'GET':
q = request.GET['q']
if q != '':
restaraunts = restaraunts.filter(name__contains=q)
# ordering is the last thing you do
restaraunts = restaraunts.order_by('name')[:100]
context = {'restaraunts': restaraunts}
return render_to_response('index.html', context)
Now, this will work, but from what you wrote I understand you want the search to be live the moment a key is pressed. For that, you'd need to use AJAX (go learn how it works, I'm not gonna delve into it here). As for autocomplete, you should check out jQuery UI.
But, like I said, before jumping ahead and using all those stuff, I suggest you first learn the basics. Go through the django tutorial (if you haven't already), and use the amazingly detailed django-docs every step of the way. When some specific things won't work and you're stuck, come here to Stackoverflow and someone will surely help you. good luck!

Django forms: custom error list format

New to python and django. Using the forms module and going through the errors one by one (so not just dumping them at the top)
I noticed this solution to be able to set a custom format for errors, in my case, it'd be:
<dd class="error">errstr</dd>
And more or less copying the example provided, I have the following:
forms.py ( I expanded it slightly just for my sake)
class DefinitionErrorList(forms.util.ErrorList):
def __unicode__(self):
return self.view_as_dd()
def view_as_dd(self):
if not self:
return u''
return u'<dd class="error">%s</dd>' % '<br />'.join([u'<span>%s</span>' % e for e in self])
main.py
from poke.forms import PokeForm, DefinitionErrorList
def create_new(response, useless):
if response.method == 'POST':
# They posted something, so collect it (duh)
f = PokeForm(response.POST, error_class=DefinitionErrorList)
if f.is_valid():
cd = f.cleaned_data
template for reference
<dt>A small message to remind yourself</dt>
{{ form.message.errors }}
<dd>
<span class="input_border" style="width: 75%;"> {{ form.message }}</span>
<span class="tooltip_span">{{ tooltip.message }}</span>
</dd>
The problem is is that with the above, if a field has an error, it still uses the default format (the ), and no matter what I try I can't get my one to be used. I'm pretty sure I missed something small or misunderstood some instructions.
Thanks for any help and I'm sorry if I forgot any information!
Edit: I'm using Django 1.1, if that helps any. And to make it (possibly) clearer, the errors are displaying fine, they just aren't looking the way I want them to.
there has been a long lasting ticket on this. http://code.djangoproject.com/ticket/6138
latest update says it's fixed. see if it works in trunk. if not, submit your bug. if it does, get the patch or wait til the next release. (although i thought the latest release was after the date of the last update on the ticket).