Yii Ajax request CSRF can not be verified - ajax

I use a few different AJAX calls in one of my pages through a couple of different methods.
CHtml::link()
CHtml::ajax()
Within CGridview
Since enabling CSRF I'm having difficulty verifying the token. What is the correct way? I've read a few posts, but struggling to implement. For instance in CHtml::link() I've tried:
'data' => "Yii::app()->request->csrfTokenName = Yii::app()->request->csrfToken",
and also within CGridview:
data: {
Yii::app()->request->csrfTokenName => Yii::app()->request->csrfToken
},

1- Fot ajax you have ajax, ajaxLink and ajaxButton not just link.
2- CSRF token works when you use POST request
3- Add CSRF token like this:'data'=>array('YII_CSRF_TOKEN' => Yii::app()->request->csrfToken)`

Related

Django - AJAX - Why do I need url parameter?

It's my first time using AJAX and I don't understand why I need to specify url parameter in a JS Ajax call.
{% block javascript %}
<script>
$("#id_username").change(function () {
$.ajax({
url: '/some_new_url/',
data: {
'something': ...
},
success: function (data) {
if (data.is_taken) {
alert("Data is already in DB");
}
}
});
});
</script>
{% endblock %}
To my understanding, AJAX is used to do something on the server side without refreshing a page. So it shouldn't redirect to a new url upon sending a data to the server, and stay on the same url. And yet AJAX call requires url parameter.
And I don' really like this, because setting a new url means I have to add another url pattern in my app/urls.py.
re_path(r'^create/$', views.Some_View.as_view(), name='create'),
And as a consequence, make another view in my views.py
class Some_View(ListView):
model = SomeModel
fields = '__all__'
But, I already have a CBV that generates form fields on the user side and accepts user inputs. I only want to make my existing CBV to save data to DB using AJAX call.
Since I don't understand what the purpose of the url is, I don't know how to set up my new url pattern, and CBV. Can I get some explanation here?
++ This is just a bonus question, but my ultimate goal is to generate multiple form fields, and multiple Submit buttons that sends the respective form input data to the server using AJAX. If there's any advice on how to tweak AJAX code, I would appreciate it.
An AJAX request is just a regular HTTP request to a url on the server. The only difference between an AJAX request and a request made by an ordinary browser GET or POST is that with AJAX, the results that come back from the server are returned to your javascript function and then you get to decide what to do with those results.
So there's no automatic updating of anything.
If you want to save something on the server, you need a view there on the server which is capable of understanding the data you are sending in the AJAX request, saving it, and then sending back a response which, again, your javascript code needs to be able to understand.
But if you already have a view which is capable of doing what you want, you can use it for your AJAX request, you just have to send a request with everything in it that the view requires.

Should a Laravel SPA still use a CSRF token for security?

I ran into multiple difficulties with CSRF when building a small SPA with Laravel and Vue.js:
I use index.html as the only view, the rest is handled by vue-router using single file components (i.e. .vue files)
Because I'm not using PHP or Blade on the front, I can't inject csrf_token() into my view. Even if I did, the token would eventually expire, yet because the app has no (or very few) page refresh(es), it wouldn't know if the token changed, and it would eventually fail to make AJAX requests with the old token
Some answers suggest to pass the token in a cookie and then retrieve it with JS. This approach suffers from the same problem as above -- the SPA is never notified when the token changes
I could dig in the internal workings of Laravel and throw an event every time the token changes; the front-end could use Laravel Echo to listen to the changes, but then the question raises, is it even worth to bother?
Lastly, I was suggested to use JWT; however, as I understand, JWT is used for authentication purposes, while CSRF -- for every single request regardless of the HTTP verb or intent.
With the last two points in mind, do you think it is necessary/advisable to use a CSRF token in a Laravel SPA? And if so, what would be the best implementation (cookie with the token, dedicated route returning the token, or other)? If not, what are the alternatives?
Comments don't have enough space, so I'm adding this as an answer, but this is just a concept as I have extremely low experience with Vue.
From the docs
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
So the concept would be something like this:
Set a custom header from Laravel.
When building/starting up your Vue application, get the custom header and set it somewhere global.
When making a request, intercept it and add the CSRF token from the global storage
axios.interceptors.request.use(function (config) {
// Get your token from the place you stored it and add to the request
});
Intercept the response and store a new token
axios.interceptors.response.use(function (response) {
// Store the new CSRF token in the same place you stored the 1st one.
});
Loop forever

Registering routes with Laravel but make them unaccessible

I am trying to make a single page CRUD application with Laravel. I will use ajax to create, edit and delete my entity, and also to render partial views. The corresponding controller methods will process the information and return the views.
I want to register the routes so I can call the different methods when necessary. I don't see any other way:
However, registering them so I can do something like this {{ Form::open(['route' => ['cities.store', $city->id]]) }} will allow access via the URL, and I only want to make those routes accessible through the tools I am going to create in that one page CRUD.
I can only think of applying a before filter, but what would be the filter? Also, any other ideas on how I should approach this situtation?
I've had to do something similar with a web service I created. Basically, I wanted only my app to be able to access the routes I created.
What I ended up doing was adding a hashed key to each request being sent, then checking for this key value in the controller. So, only if the key is present and matches the one sent would you then process the request.
Or, if you're using forms, you could do something like the following:
//check if request was sent from our form
if ( Session::token() !== Input::get( '_token' ) ) {
return Response::json( array(
'msg' => 'Unauthorized access attempt'
) );
}
Hope this helps.
another way that doesnt need tokens but is less secure, you got to know what you need,
is using laravels request information
if (Request::ajax())
{
//your action
}else{
//error
}
note this only works when your application always uses ajax you could even type this in your before filter and add it to all needed routes

Django Rest Framework, ajax POST works but PATCH throws CSRF Failed: CSRF token missing or incorrect

I am porting my project to Django Rest Framework to make a proper REST Api for my project, I think it helps a lot designing the API and making it robust but I am running into a problem :
I have a entry model and associated ListCreateAPIView and RetrieveUpdateDestroyAPIView views.
I can successfully post a new entry instance in the list through an ajax request and providing the csrfmiddlewaretoken as I would do in regular Django View.
POST entries/
Now I am trying to apply a patch to an existing instance using the same csrfmiddlewaretoken like so:
PATCH entries/3
The response status code is then 403 FORBIDDEN vith error CSRF Failed: CSRF token missing or incorrect although I checked in firebux that csrfmiddlewaretoken is in the request data.
I don't what is wrong and I cannot find out where in the code is the request rejected.
Note: I can patch the object with the Django Rest Framework browsable api.
I hope someone can help.
Thanks.
Olivier
EDIT
I was digging into the code to see where the rejetion of the PATCH request occurs and I found in django.middleware.csrt.py the following:
if csrf_token is None: #<--- csrf_token is defined
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
# Check non-cookie token for match.
request_csrf_token = ""
if request.method == "POST": #<--- This fails but request_csrf_token is in request.DATA
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX,
# and possible for PUT/DELETE.
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
The second test fails because it is not a POST request but the information required is in request.DATA.
So it seems that django is not keen to accept PATCH request.
What do you think would be the best way to go around this?
Would you recommend using a different authentication system (there are some in Django-rest-framework documentation)?
EDIT2
I found out a solution:
I observed that the browsable api is actually sending a POST request but with a parameter _method="PATCH", so I did the same with my ajax request and it works fine.
I don't know if it is the right way to do, any feedback and opinion is welcome!
EDIT3
So, after more reading, I discovered (I already kind of knew..) that because some browsers do not support requests like PUT, PATCH, DELETE, the way to go is to send a post request with X-HTTP-Method-Override in the header.
So the good way to go I think is to do the following:
$.ajax({
headers: {
'X-HTTP-Method-Override': 'PATCH'
},
type : "POST",
...
});
I also meet the same question, I learn from #overlii solution problem method. I use django rest framework web interface to do put/patch, and I find the HTTP Request Headers Info like below:
HTTP Request Headers
From this image, we can find X-CSRFTOKEN header, so I set ajax header info like below:
$.ajax({
headers: {
'X-CSRFTOKEN': '{{ csrf_token }}'
},
type: "PATCH",
dataType: "json",
url: "/api/path/",
data: "",
success: function(data){
}
});
I use this way to send patch request and find this can run rightly!
I finally add this as an answer.
So, after more reading, I discovered (I already kind of knew..) that because some browsers do not support requests like PUT, PATCH, DELETE, the way to go is to send a post request with X-HTTP-Method-Override in the header.
So the good way to go I think is to do the following:
$.ajax({
headers: {
'X-HTTP-Method-Override': 'PATCH'
},
type : "POST",
...
});
This isn't a direct solution to your problem but this should provide some context and provide a possible solution.
Django does not support the HTTP PATCH method and throws away all data including the CSRF token. A possible workaround is to change the method to POST, force Django to reprocess the request and change the method again. This is a bit dirty but works, example code as used by Django Piston is provided here:
def coerce_put_post(request):
"""
Django doesn't particularly understand REST.
In case we send data over PUT, Django won't
actually look at the data and load it. We need
to twist its arm here.
The try/except abominiation here is due to a bug
in mod_python. This should fix it.
"""
if request.method == "PUT":
# Bug fix: if _load_post_and_files has already been called, for
# example by middleware accessing request.POST, the below code to
# pretend the request is a POST instead of a PUT will be too late
# to make a difference. Also calling _load_post_and_files will result
# in the following exception:
# AttributeError: You cannot set the upload handlers after the upload has been processed.
# The fix is to check for the presence of the _post field which is set
# the first time _load_post_and_files is called (both by wsgi.py and
# modpython.py). If it's set, the request has to be 'reset' to redo
# the query value parsing in POST mode.
if hasattr(request, '_post'):
del request._post
del request._files
try:
request.method = "POST"
request._load_post_and_files()
request.method = "PUT"
except AttributeError:
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = 'PUT'
request.PUT = request.POST
I have successfully used this fix (with some modifications) and although it feels a bit dirty it seems to be a very usable solution.
Alternatively you could use do method overloading in the POST data. Again not the prettiest solution but very workable.
I would love for someone to propose a better solution.

Zend_Form csrf validation for ajax queries

Here is how I add the csrf to the form
$this->addElement('hash', 'csrf', array('ignore' => false));
When this happens the session is created, Then when the user sends an ajax request, the values in the request are validated by creating an instance of the form, and the form is always valid for the first ajax request since the beginning of initial request which created the html output,
When the ajax request has been sent for the second time something different happens,
That instance of the form has a different csrf value than the originally made one, and when my code is done, the originally created session is destroyed as well, so there is no session to check the received the values against, and hence the form doesn't validated and the following error occurs.
No token was provided to match against
Any ideas at which event, the csrf values of the form are automatically stored in the session?
The hash value is generated at render time and invalidated after the each request.
If you want to continue using Zend_Form_Element_Hash in your AJAX form where the form may submit several times, your AJAX response should include the new hash value. Upon receiving the response, you should update the form data.
There s a solution without any to render in the view : Totaly ajax !
How to use Zend Framework Form Hash (token) with AJAX

Resources