I'm using a django model form and I want to protect me from malicious user input (not just wrong input).
From my understanding django forms are enough secure: .is_valid() check user input, csfr protect from cross site forgery.
But this time my form isn't using action='/path/to/my/view' to call a django view, instead my submit button calls a javascript function and this function takes the data and calls a django view using ajax to access the database and then shows the results on screen.
So I don't think to be protected anymore (.is_valid() is not called, csfr is not sent). I'm right? and if so what I should do?
I think:
1) This is not a problem, the form is reasonable secure (why?)
2) Refactor the code and using a django view
3) Neither the django form validation is enough secure so anyway I should do something more (what?)
4) My javascript function is sending the data to a django view using ajax. I should use that data to instantializate a bound form and use .is_valid() on that, but anyway I'm not using csfr, right?
5) Using html validators (to me they don't look adapt to check against malicious input data)
6) Other?
Some code, to be complete but probabily you wont need it
my forms.py:
class NameListForm(forms.ModelForm):
class Meta:
model = Name
fields = ['namelanguage', 'nametype', 'gender']
widgets = {
'gender': forms.CheckboxSelectMultiple(),
}
My models.py:
class Name(models.Model):
name = models.CharField(_('nome'), max_length=50, default='')
namelanguage = models.ForeignKey(
NameLanguage, related_name='%(app_label)s_%(class)s_language',
verbose_name=_('linguaggio'), on_delete=models.PROTECT)
nametype = models.ForeignKey(
NameType, related_name='%(app_label)s_%(class)s_tipo',
verbose_name=_('tipo'), on_delete=models.PROTECT)
gender = models.ForeignKey(
Gender, related_name='%(app_label)s_%(class)s_gender',
verbose_name=_('sesso'), on_delete=models.PROTECT,
blank=True, null=True)
my template.html:
<form action="" method="post">
<div>
<div class="col-md-auto">
{{ name_list_form.namelanguage.label_tag }}<br />
{{ name_list_form.namelanguage }}
{{ name_list_form.namelanguage.errors }}
</div>
<div class="col-md-auto">
{{ name_list_form.nametype.label_tag }}<br />
{{ name_list_form.nametype }}
{{ name_list_form.nametype.errors }}
</div>
<div class="col-md-auto">
{{ name_list_form.gender.label_tag }}<br />
{{ name_list_form.gender }}<br />
{{ name_list_form.gender.errors }}
</div>
</div>
{{ name_list_form.non_field_errors }}
<div>
<button class="btn btn-primary" id='list_name' type="button" onclick="FilterBy()">{% trans "List Names" %}</button>
<button class="btn btn-primary" type="button" onclick="RandomNames()">{% trans "Random Names" %}</button>
</div>
{% csrf_token %}
</form>
My javascript.js:
function FilterBy() {
var language_id = document.getElementById("id_namelanguage").value;
var nametype_id = document.getElementById("id_nametype").value;
...
$.ajax({type: 'POST',
url: '/lists/get-list-name/',
data: {
language: language_id,
nametype: nametype_id,
...
},
success: function (lista) {
if (lista.result === 'OK') {
//do something
};
}
});
};
My views.py:
def GetListName(request):
if request.is_ajax():
language = request.POST.get('language')
nametype = request.POST.get('nametype')
# it makes sense to check validity here? and anyway I'm not using csfr, right?
# name_list_form = NameListForm({'namelanguage': language, 'nametype': nametype, etc})
# if name_list_form.is_valid():
...
return JsonResponse({'result': 'OK', 'data': my_dict})
CSRF and data validity are two different subject.
First, you can check for CSRF errors by sending the CSRF token in a request header check this.
Second, you can send your data the same way in JS than in a classic form.
// JS, don't forget to add your CSRF headers
$.ajax({
method: "post",
url: "...",
data: {
namelanguage: "foo",
nametype: "bar",
gender: "baz",
});
And just process your form as you do. You can throw an exception if you wan't to be sure you form was submitted from the JS script, but this won't assure you that it really does. Anybody can modify the client headers to make you think so.
# python
from django.http.response import HttpResponseBadRequest, HttpResponseNotAllowed
def GetListName(request):
if not request.is_ajax():
return HttpResponseNotAllowed()
form = NameListForm(data=request.POST)
if not form.is_valid():
return HttpResponseBadRequest()
# do stuff with your form
return JsonResponse({'result': 'OK', 'data': 'some data'})
Related
I am trying to save a record in the database of a model (Score) that is related to two other models (User and Exercise), I receive the form correctly or so I think, and using the save() instruction does nothing. I don't even get errors.
Here the code:
forms.py
class ScoreForm(forms.ModelForm):
#idUser = forms.IntegerField(widget=forms.HiddenInput(), label='idUser')
idExercise = forms.IntegerField(widget=forms.HiddenInput(), label='idExercise')
value = forms.FloatField(widget=forms.HiddenInput(), label='Value')
class Meta:
model = Score
fields = ['idExercise', 'value']
views.py
def save_exercise(request):
if request.method == 'POST' and request.is_ajax:
form_score = ScoreForm(request.POST, instance=request.user)
if form_score.is_valid():
form_score.save()
else:
print('Tenemos un error')
return HttpResponse('Respuesta')
JavaScript
document.getElementById('id_value').value = score;
document.getElementById('id_idExercise').value = idexer
// document.getElementById('id_idUser').value = iduser
let data = new FormData($('#scores').get(0));
Swal.fire({
title: '<strong>Tu resultado: </strong>',
icon: 'success',
html:
`<div class="progress" style="height: 30px;"><div class="progress-bar progress-bar-striped progress-bar-animated" style="width:${number}%">${number}%</div></div>` +
`Ganaste: ${score.toFixed(2)}`,
focusConfirm: false,
confirmButtonText:
'<i class="fa fa-thumbs-up"></i> Okey!',
preConfirm: () => {
$.ajax({
type: "POST",
url: "/polls/save/",
data: data,
processData: false,
contentType: false,
success: function(){
console.log('Funciona!')
show_exercise()
}
})
}
})
In html file
<div class="row d-flex justify-content-center">
<form method="POST" action="" novalidate id='scores'>
{% csrf_token %}
{{ form|crispy}}
</form>
</div>
All the inputs of the form are type hidden since after a couple of calculations I assign their respective values, the form is sent from a modal using AJAX
I can't find an answer, since I don't know what error to look for, could you please help me
The solution was the following, in the file views.py
def save_exercise(request):
scr = Score.objects.filter(idExercise=request.POST['idExercise'], idUser=request.user)
# print(serializers.serialize('json', scr))
exer = Exercise.objects.get(id=request.POST['idExercise'])
if request.method == 'POST' and request.is_ajax():
if scr:
scr.update(value=request.POST['value'])
else:
scr = Score(idUser=request.user, idExercise=exer, value=request.POST['value'])
scr.save()
return HttpResponse('Respuesta')
I was not taking into account the instances related to my Score model.
I am working on a django project in which I have gathered up all of my variables in js and am trying to initialize a form (inside a modal popup) on same page without any refresh. I have the form showing up inside my modal, but can't quite figure out the Ajax post method to get my js variables into the initial form fields. Any help would be much appreciated!
index.html - this function fires where I need it, but returns the page url instead of the data object I am trying to define.
Index.html
$.ajax({
method: "POST",
url: '',
data: {
geology: ('#geology').value,
latitude: ('latitude').value,
longitude: ('longitude').value,
csrfmiddlewaretoken: '{{ csrf_token }}',
},
success: function(data){
console.log(data.geology);
},
error: function(error_data){
console.log("error");
console.log(error_data);
}
});
views.py
def home_view(request):
geology = request.GET.get('geology')
latitude = request.GET.get('latitude')
longitude = request.GET.get('longitude')
form = MineralForm(initial={'mineral': mineral, 'geology': geology, 'latitude': latitude, 'longitude': "INITIAL", 'description': description}) # populates form with what user tried to submit...
UPDATE:
Here is an update on where I am at:
The modal window opens after an api call to gather some geology data. I am hoping to populate this form with the geology data, without refreshing the page. I am using ajax to create a post that get's my data object over to views.py (I am able to print the data upon POST from views.py into the terminal) but the form is not initializing with the data. Instead, it is initializing with the "PRE_POST" values, even after the api call to gather the data and post via ajax.
Any thoughts on what might be going on here, or how to resolve would be much appreciated!
matMap.html (snippets)
<!-- mineralForm Modal -->
<div class="modal fade draggable" id="mineralFormModal" role="dialog" style="height: 100%;">
<!-- Modal content-->
<div class="modal-header">
<button id ="mineralFormModalClose" type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title" align="center" style="font-family: Lobster">Add New Mineral</h4>
</div>
<div class="mineralFormModal_body" id="mineralFormModal_body" style="position:absolute; top:0">
<h3>loading before modal clears</h3>
</div>
<br>
{{ form }}
<br>
<button id="modalSave" type="button" class="btn btn-default" data-dismiss="modal">Save</button>
<div class="modal-footer red lighten-3">
<button id="modalClose" type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
(...)
$.ajax({
method: "POST",
url: '',
data: {
geology: geology,
latitude: x,
longitude: y,
csrfmiddlewaretoken: '{{ csrf_token }}',
},
success: function(data){
console.log("AJAX POST SUCCESS!"); //grabs this entire page and all variables.
},
error: function(error_data){
console.log("error");
console.log(error_data);
}
});
views.py
def home_view(request):
geology = request.POST.get('geology')
latitude = request.POST.get('latitude')
longitude = request.POST.get('longitude')
if request.method == "POST":
print("!!!! POSTING from HOME !!!!" + geology + " " + latitude + " " + longitude)
form = MineralForm(request.POST, initial={'geology': "POST_VALUE",'latitude': "POST_VALUE", 'longitude': "POST_VALUE"})
print("NOW WE ARE POSTING: ")
print(form)
return render(request, 'blog/matMap.html', {'form': form})
else:
print("LOADING HOME: INITIAL STATE (BEFORE POST)")
form = MineralForm(initial={'geology': "PRE_POST",'latitude': 'PRE_POST', 'longitude': 'PRE_POST'}) # populates form with what user tried to submit...
return render(request, 'blog/matMap.html', {'form': form})
And here is my terminal output, from initial page load through after the ajax post:
LOADING HOME: INITIAL STATE (BEFORE POST)
[13/Feb/2019 13:36:07] "GET / HTTP/1.1" 200 41261
Not Found: /js/scripts.js
[13/Feb/2019 13:36:07] "GET /js/scripts.js HTTP/1.1" 404 3210
Not Found: /js/scripts.js
[13/Feb/2019 13:36:08] "GET /js/scripts.js HTTP/1.1" 404 3210
[13/Feb/2019 13:36:08] "GET /api/chart/data/ HTTP/1.1" 200 4039
!!!! POSTING from HOME !!!!Miocene -119.48709 44.85346
NOW WE ARE POSTING:
<tr><th><label for="id_mineral">Mineral:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="mineral" required id="id_mineral"></td></tr>
<tr><th><label for="id_description">Description:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><textarea name="description" cols="40" rows="10" required id="id_description">
</textarea></td></tr>
<tr><th><label for="id_geology">Geology:</label></th><td><input type="text" name="geology" value="Miocene" required id="id_geology"></td></tr>
<tr><th><label for="id_latitude">Latitude:</label></th><td><input type="text" name="latitude" value="-119.48709" required id="id_latitude"></td></tr>
<tr><th><label for="id_longitude">Longitude:</label></th><td><input type="text" name="longitude" value="44.85346" required id="id_longitude"></td></tr>
[13/Feb/2019 13:36:30] "POST / HTTP/1.1" 200 41593
However, even after this, when I call up my modal form, it looks like this:
THANK YOU!!!
You're sending data via POST, so initialize your form with it:
form = MineralForm(request.POST or None)
You can not access your POST body data by request.GET use request.POST. And check this doc
def home_view(request):
geology = request.POST.get('geology')
latitude = request.POST.get('latitude')
longitude = request.POST.get('longitude')
form = MineralForm(initial={'mineral': mineral, 'geology': geology, 'latitude': latitude, 'longitude': "INITIAL", 'description': description}) # populates form with what user tried to submit...
I want to validate the entered username and password entered in the textboxes present in a form without Postback/Refresh. I know I am gonna need Javascript or AJAX for this purpose, but somebody please guide me through this like refer me to any tutorial or please explain me the code here.
My present code without this feature looks like this:
#using(Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(u => u.PPNumber, new { #class = "form-control", type = "number", placeholder = "Enter Number",#min="1" })
#Html.ValidationMessageFor(u => u.PPNumber)
#Html.TextBoxFor(u => u.Password, new { #class = "form-control", type = "Password", placeholder = "Password" })
#Html.ValidationMessageFor(u => u.Password)
<input type="submit" value="Login" class="btn btn-primary btn-block" />
}
You can use ajax.
When user submits the form, you need to hijack that event in javascript and stop that(prevent the normal full page form submit) and instead make an ajax call to the action method with the form data. This action method should have code to verify the user credentials and can return a JSON data structure which has a status property which says whether the credential validation is successful or not. You can inspect this response in your ajax call's success/done callback and act accordingly.
Here is a quick sample using jQuery $.post method.
Give an Id to your form so that we can use that to wire up the submit event.
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id="loginForm"))
{
#Html.TextBoxFor(u => u.PPNumber)
#Html.TextBoxFor(u => u.Password, new { #class = "form-control", type = "Password"})
<input type="submit" value="Login" class="btn btn-primary btn-block" />
}
and the javascript code to hijack the submit event and do an ajax post instead.
$(function () {
$("#loginForm").submit(function(e) {
e.preventDefault();
$.post($(this).attr("action"), $(this).serialize())
.done(function(response) {
if (response.status === 'success') {
alert("Login successful.Do something else now");
} else {
alert("Login failed");
}
});
});
});
Assuming your Login action method in AccountController will return a Json response with a status property.
public ActionResult Login(string PPNumber,string password)
{
if(PPNumber=="thisIsDemo" && password=="ButDoTheActualCheck")
{
return Json(new { status = "success" });
}
return Json(new { status = "failed" });
}
Here i just hard coded the usrename /password check to 2 static values. You can change that to check it against your db table/whatever your credentials checking mechanism is.
I am trying to implement auto complete functionality into my laravel 5.4 project and so far I have been successful in displaying only a single db field (first name). Here is the code
routes
Route::get('test',array('as'=>'search','uses'=>'SearchController#search'));
Route::get('autocomplete',array('as'=>'autocomplete','uses'=>'SearchController#autocomplete'));
controller
public function autocomplete(Request $request)
{
$data = customer::select("first_name as name")->where("first_name","LIKE","%{$request->input('query')}%")->orderBy('first_name')->take(6)->get();
return response()->json($data);
}
view
<form method="POST" action="/home/customer/{{$id}}">
{{ csrf_field() }}
<div class="input-group input-medium " style="float: right; padding-top: 3px; padding-right: 15px; ">
<input type="text" class="typeahead form-control" required>
<span class="input-group-btn">
<button class="btn green-haze" type="submit"> <i class="fa fa-search"></i></button>
</span>
</form>
//js
<script type="text/javascript">
var path = "{{ route('autocomplete') }}";
$('input.typeahead').typeahead({
source: function (query, process) {
return $.get(path, { query: query }, function (data) {
return process(data);
});
}
});
</script>
So I have two figure out two things:
The above code as of now only shows first_name as autocomplete suggestions
I would like them to be in the format id - first_name last_name
I also want to fetch the selected users id from autocomplete so that I can pass it in my form action /home/customer/{$id}
I am new to javascript and jquery so I dont yet understand how to do this. Please help me
The reason you're only getting the first_name is because you're only selecting the first name, select("first_name as name"), remove the select from the query and you'll be returned a collection with all of the fields
e.g.
$data = customer::where("first_name","LIKE","%{$request->input('query')}%")->orderBy('first_name')->take(6)->get();
Then within your javascript callback, data will have access to each of the records found and their fields.
EDIT
With Typeahead you define a callback so that you can use the data which is returned, e.g:
$('input.typeahead').typeahead({
source: {
groupName: {
// Ajax Request
ajax: {
url: "{{ route('autocomplete') }}"
}
}
},
callback: {
onResult: function (node, query, result, resultCount, resultCountPerGroup) {
console.log(node, query, result, resultCount, resultCountPerGroup);
}
}
});
i am working on for a basic feedback system. I just want to submit feedback with ajax, for example if fields are empty ajax will return some error msg and update the div at the same page with no redirection. if everything is ok it will update the feedbacks div with the new one...
Here is my code
models.py
from django.db import models
# Create your models here.
class FeedBack(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
def __unicode__(self):
return self.title
class Meta:
ordering = ["-title"]
views.py
import json
from django.shortcuts import *
from django.template import RequestContext
from submission.forms import *
def feedback(request):
if request.method == "POST":
form = FeedBackForm(request.POST)
message = 'Oops!'
if(form.is_valid()):
message = request.POST['title']
return HttpResponse(json.dumps({'message': message}))
return render_to_response('index.html',
{'form':FeedBackForm()}, RequestContext(request))
urls.py
from django.conf.urls import patterns, include, url
from submission.views import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^feedback/', feedback, name='feed_back'),
)
index.html
<html>
<head>
<script type="text/javascript" src="../static/helper.js"></script>
</head>
<body>
<h1>Leave a Suggestion Here</h1>
<div class="message"></div>
<div>
<form id="feed_form" action="" method="POST">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit Feedback" />
</form>
</div>
</body>
</html>
helper.js
$(document).ready(function() {
$('#feed_form').submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(), // get the form data
type: $(this).attr('method'), // GET or POST
url: '', // the file to call
success: function(data) { // on success..
$('.message').html(data); // update the DIV
},
error: function(e, x, r) { // on error..
$('.message').html(e); // update the DIV
}
});
return false;
});
});