Using PyroCMS, I send a POST request that returns a HTTP 505. If I send GET request on same url is is working.
This is my route file code.
$route['admin/pms(/:any)?'] = 'admin$1';
This is url i send.
http://domain.com/index.php/admin/pms/index/2?
Why doesn't POST work?
You have to include the CSRF hash name in your POST request:
$.post(
SITE_URL + 'module/controller/function',
{
data: data,
otherdata: somemoredata,
csrf_hash_name: $.cookie('csrf_cookie_name')
},
function() { console.log('Yay'); }
);
Check out system/cms/config.php, setting $config['csrf_cookie_name'], to see what your cookie name is. 'csrf_cookie_name' is the default.
Other "solution" would be to turn off CSRF protection.
Related
I am trying to send an HTTP PUT request with "Content-Type": "multipart/form-data" to a Laravel application. When I change the method to POST it works.
$a = $request->all(); // With PUT this is empty but with POST it works fine.
The client-side executes the following code:
axios({
method: "post", // when I try method:"PUT" and change the content type
url: "/api/offer",
data: fd,
headers: {"Content-Type": "multipart/form-data"} // here change to "x-www-form-urlencoded" it the $a array on backend is empty!
}).then(response => {
console.log("/offer/" + response.data)
if (response.data)
window.location.replace("/offer/" + this.offer.id);
else {
console.log("show a message that something went wrong! ")
}
}).catch(function (error) {
})
I could not find anywhere in the docs that PUT can't send "multipart/form-data"
So, can PUT send "multipart/form-data" or only POST can do that in general or it is only a PHP / Laravel Issue?
Edit:
Also, what difference does it make to use PUT instead of POST other than to comply with HTTP protocol and CRUD operation properly?
Laravel (HTML Forms) do not work great with Put requests so you'll need to spoof a POST request as if it was a PUT or PATCH request. On Axios you use the .post verb but within your form data you append
_method: "put"
Information from the official documentation:
https://laravel.com/docs/8.x/routing#form-method-spoofing
Excerpt from the documentation:
HTML forms do not support PUT, PATCH, or DELETE actions. So, when defining PUT, PATCH, or DELETE routes that are called from an HTML form, you will need to add a hidden _method field to the form. The value sent with the _method field will be used as the HTTP request method
I ran into this issue a few weeks ago myself with a Symfony 5.3 project. It only worked with POST Requests, not with PUT. Here's an issue from the Symfony GitHub that explains it in more detail.
To my understanding the issues lies within the PHP implementation of those requests. The HTTP standard "PUT" supports it, but PHP does not. Here's also a link to the bug from the PHP bugtracker.
In laravel documentation, for ajax based applications, there is CSRF protection via HTML meta tag and cheking header request.
Why this method needed and why not check ajax request as usual request? I mean, if for ajax whe use POST method, then send CSRF token as usual parameter (for example as csrf_token) and then check in server side (PHP) :
if ( !isset($_POST['csrf_token']) OR $_POST['csrf_token'] !== $_SESSION['csrf_token'] ) {
// incorrect csrf token, stop processing
}
Cheking header request have some advantage than this method ?
If you are doing POST request, CSRF doesn't go through the header it goes through the http message body, what Laravel has is some kind of default middleware for csrf protection, where is the issue in that?
If you go into assets/js folder you can see the CSRF token in bootstrap.js.
You can fetch a CSRF token from a global Javascript variable and send it through ajax post request body.
I'm building a CMS-like web application using Laravel(back-end) and ReactJS with JQuery(front-end).
I decide to put the existing Web API into a separate domain(api.test.com), and my user interface is on the different domain(test.com).
On test.com, I launch an ajax request to api.test.com to modify some resource on the server:
$.ajax({
url: "api.test.com",
method: 'POST',
data: {...}
success: function (no) {
// ...
}
});
And of course it's illegal due to security problem. However, I can configure my web server:
For Nginx:
add_header Access-Control-Allow-Origin http://test.com;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
add_header Access-Control-Allow-Headers X-Requested-With,X-CSRF-TOKEN,X-XSRF-TOKEN;
The Access-Control-Allow-Origin problem is solved perfectly but another problem occurs due to Laravel's CSRF protection...
Laravel requires a CSRF token included in the request(POST,PUT...which will modify the resource) by default.
In my case, I must generate csrf_token on api.test.com rather than test.com because different domain do not share the token.
I followed the User Guide of Laravel and added these code to my front-end:
$.ajax({
url: "api.test.com/token", // simply return csrf_token();
method: "GET",
success: function (token) {
// Now I get the token
_token = token;
}.bind(this)
});
and modify the previous request implementation:
$.ajax({
url: "api.test.com",
method: 'POST',
headers: {
"X-CSRF-TOKEN": _token // Here I passed the token
},
data: {...}
success: function (no) {
// ...
}
});
But Laravel responses a status code of 500. Then I checked the VerifyCsrfToken.php:
protected function tokensMatch($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
// Log::info($request->session()->token() . " == $token");
return Str::equals($request->session()->token(), $token);
}
The $token which I 'POST' to is different from what it was ($request->session()->token()).
I found that the validation tokens on server are different when calling $.ajax.
I try to put the two requests in the same session(by changing the cookie), but it's impossible.
I spent a lot of time to solve this problem but didn't work it out.
Have any idea or solution?
Thanks,
Micooz
Thank you for answering my question. I've considered disabling the CSRF protection to some URIs but I don't want to take these risk.
The key point of my question is that the $.ajax forgets carrying cookies before request, and resulting token validation failed.
Now I setup the JQuery Ajax, let it carry cookies before make a request.
$.ajaxSetup({
xhrFields: { withCredentials: true }
});
and Nginx conf:
add_header Access-Control-Allow-Credentials true;
BTW, it's not necessary to include the token in the data:{}(Form).
All the problems are settled and it works perfectly for me.
Laravel expects the token as a data variable, included on your fields, the name of the var needs to be _token try to change it.
Another solution is including the token in your data not in the headers.
$.ajax({
url: "api.test.com",
method: 'POST',
data: { _token : _token }
success: function (no) {
// ...
}
});
You can follow this url
http://laravel.io/forum/11-14-2014-disabling-the-csrf-middleware-in-laravel-5
In this link, you need to wrap up VerifyCsrfToken class with new one class where you specify actions on which you want not use csrf_token
I have an application that is using angular js to make an $http GET request to a server. One page particularly has a form which gets a csrf token embedded into it as follows
<input type="hidden" ng-model="token" value="{{{ Session::getToken() }}}">
In my controller I have the following code:
public function getMethod($arg, $token)
{
/*Check for csrf token validity*/
if ($token != Session::token()) {
return Response::json(
array(),
403
);
}
........
}
From the client side I make a request like this:
var arg = $scope.arg;
var get_url = '/url/routing/to/controller/arg/' + arg + '/' + $scope.token;
$http({method: 'GET', url: get_url})
.success(function(data, status, headers, config) {
//Do stuff after success
.............
})
.error(function(data, status, headers, config) {
//Handle error
.......
});
I am not exactly sure how the GET request can be integrated with csrf tokens but when I make a GET request to the registered URL, I get a token mismatch. Basically a new token is generated every time an ajax request is sent to the server, therefore the initial token extracted in the form input element does not match when I am comparing it in the controller. Could anyone tell me how csrf validity can be done in this case?
Thanks
You should not be adding/modifying resources through GET, therefore you do not need a token on a get request. CSRF tokens are needed only in methods that change or add resources to your application using the currently logged in user's credentials.
In my PHP application, I get a page by Ajax request and load it into a specific div element of the page. I am sending some headers from server in response too.
How to store that headers in browser?
actually as i had mentioned above , through ajax i am loading page and browser url remains unchanged during process , and when i am clicking back button of browser it is redirecting me to very first page of application. all i am trying to do is to store name of requested page by ajax in that browser history which helps back button to navigate functionality .
what i am thinking is possible logically?
The success function called by your jQuery $.ajax request will have a jqXHR object as its third argument. You can use this object's getResponseHeader() method to get the response headers from the server's http response. See the docs for more info: http://api.jquery.com/jQuery.ajax/#jqXHR
For example:
$.ajax({
url: "...",
success: function(data, responseText, jqXHR) {
var headers = jqXHR.getResponseHeader();
// Do what you will with the headers here
}
}