'_Token' was not found in request data in CakePHP3 after server migration - php

Edit:
A whole series of new insights gained after asking this question have taught me what the issue was, and it definitely did not have anything to do with the described server migration.
The two given answers show how to "fix" this for both CakePHP 2 and 3, though bear in mind this might pose a security risk. The CSRF component is an important security feature, and should not be disabled lightly.
Original question:
I migrated my CakePHP 3 project from XAMPP on my laptop to XAMPP on a server. Ever since when I activate the Security component, cake throws me an error. Here it is, directly from the Error log:
2016-05-21 20:32:01 Error: [Cake\Controller\Exception\AuthSecurityException] '_Token' was not found in request data.
Request URL: /Users/addUser
Referer URL: http://localhost/users/add_user
Stack Trace:
#0 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Component\SecurityComponent.php(324): Cake\Controller\Component\SecurityComponent->_validToken(Object(App\Controller\UsersController))
#1 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Component\SecurityComponent.php(130): Cake\Controller\Component\SecurityComponent->_validatePost(Object(App\Controller\UsersController))
#2 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventManager.php(386): Cake\Controller\Component\SecurityComponent->startup(Object(Cake\Event\Event))
#3 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventManager.php(356): Cake\Event\EventManager->_callListener(Array, Object(Cake\Event\Event))
#4 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventDispatcherTrait.php(78): Cake\Event\EventManager->dispatch(Object(Cake\Event\Event))
#5 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Controller.php(495): Cake\Controller\Controller->dispatchEvent('Controller.star...')
#6 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(109): Cake\Controller\Controller->startupProcess()
#7 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(87): Cake\Routing\Dispatcher->_invoke(Object(App\Controller\UsersController))
#8 C:\xampp\htdocs\webroot\index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#9 {main}
I found CakePHP security component blackholing login (data[_Token][key] field not generated), here on StackOverflow, but no other relevant information as to what's causing my problem. In my Appcontroller:
public function initialize()
{
parent::initialize();
$this->loadComponent('Security');
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');

The error is related to the _TOKEN. When we create a CakePHP form and then based on the input fields the CakePHP generates hidden field named _TOKEN.
For example:
<?= $this->Form->create(false, [
'id' => "ajaxForm",
'url' => [
'controller' => 'TPCalls',
'action' => 'add'
],
'class'=> "addUpdateDeleteEventForm"
]);
?>
<?= $this->Form->input('id', ['label' => false]); ?>
<?= $this->Form->input('start', ['label' => false]); ?>
<?= $this->Form->input('end', ['label' => false]); ?>
<?= $this->Form->input('title', ['label' => false]); ?>
<?= $this->Form->hidden('ADD', ['value' => 'true']); ?>
<?= $this->Form->end(); ?>
Now you should see _TOKEN value in the form when inspecting the HTML:
<input type="hidden" name="_Token[fields]" autocomplete="off" value="---HASH---">
If you do not have any visible fields then _Token will be empty. If you need to have invisible fields then simply add a hidden class on the form or the field.
Anyways, back to the main question. The error is caused by the _TOKEN field's absence. In above case, I would serialize my form before making the Ajax call.
//serializing the form
var ajaxdata = $("#ajaxForm").serializeArray();
//ajax
$.ajax({
url:$("#ajaxForm").attr("action"),
type:"POST",
beforeSend: function(xhr){
xhr.setRequestHeader("X-CSRF-Token", $('[name="_csrfToken"]').val());
},
data:ajaxdata,
dataType: "json",
success:function(response) {
console.log(response);
},
error: function(response) {
console.error(response.message, response.title);
}
});
Please note, in the ajax, I am using URL from the Cakephp form instead of hard coding it in the ajax. This way, it will be using cakephp url helper.

EDIT after #Invincible comment
Be careful when disabling csrf and security components, they provide protection against csrf and things like form-tampering, forcing ssl, http methods etc https://book.cakephp.org/3.0/en/controllers/components/security.html.
This answer shows only how to disable them, in case if you are sure you do not need them for that request.
Original Answer
In case of ajax requests you can can disable Security Component for that specific action (equivalent to making the action as unlocked in cake 2.x)
put this code in your controller's beforeFilter
$actions = [
'action1',
'action2'
];
if (in_array($this->request->params['action'], $actions)) {
// for csrf
$this->eventManager()->off($this->Csrf);
// for security component
$this->Security->config('unlockedActions', $actions);
}
disabling csrf component http://book.cakephp.org/3.0/en/controllers/components/csrf.html#disabling-the-csrf-component-for-specific-actions
disabling security component http://book.cakephp.org/3.0/en/controllers/components/security.html#disabling-security-component-for-specific-actions

UPDATE: Also, make sure you didn't forget echo $this->Form->end(); as it adds all the necessary tokens. Original answer below.
UPDATE: You may also run into this issue when submitting a form created separately via new \Cake\View\ViewBuilder()
The correct answer is indeed to invest some time into updating your code to where it's following the security component guidelines. Disabling the component, or unlocking a specific action, is a workaround, and not a solution.
Several not-so-obvious things about _Token.
Prerequisites: I have a placeholder <form> used for building repetitive, almost identical AJAX requests (one field is being constantly updated in a javascript loop and re-submitted; don't ask why).
_Token is linked to the form action URL. You can't have your placeholder form point to e.g. javascript:; and have actual requests, signed with it's token, go to some other endpoint
_Token is reusable. It is possible to issue multiple requests (i.e. submit the same form over and over again) signed with the same token, it's not a one-time token like I thought
So what I did was put my varying piece of information into a type="text" input and display:none it, instead of using a type="hidden" input, which would fall under the form tampering prevention. Then I serialize()'d the form and put it into the jQuery.ajax() data property.
// update the CSS-hidden type="text" input
document.forms.exampleForm.quantity.value=newValue;
// submit the form
jQuery.ajax({
url: document.forms.exampleForm.action,
type: document.forms.exampleForm.method,
data: jQuery(document.forms.exampleForm).serialize(),
complete: function(jqXHR) {
//
}
});
Surely one could take the easy unlockedActions route but you'll probably be glad if you don't.

#Invincible answer is good and all, but applying csrf that way seems like a nightmare to apply and maintenance, since we already have like 20 or so Ajax in our application.
So I use Cakephp 3 - element to help abstract some of the code. I am pasting my code here for other to reference if you also want to abstract the csrf token.
Here's the code:
The element: csrf_ajax_element.ctp
<?=
$this->Form->create(false, [
"id" => $name . "Form",
"url" => $url,
]);
?>
<? if(isset($params)): ?>
<? foreach($params as $param) : ?>
<?= $this->Form->input($param, ['label' => false, 'style' => 'display:none;']); ?>
<? endforeach; ?>
<? endif; ?>
<?= $this->Form->end(); ?>
<script type="text/javascript">
var csrfName = '<?=$name?>';
var url = '<?= $this->Url->build($url) ?>';
var csrf = { };
$.each($('#'+csrfName+'Form').serializeArray(), function() {csrf[this.name] = this.value;});
$("#"+csrfName).data('csrf', csrf);
$("#"+csrfName).data('url', url);
</script>
To add an ajax on a page, do the following:
some_page.ctp
<!-- At the top -->
<input id="myAjaxCsrfToken" type="hidden" data-csrf="" data-url="" />
<?= $this->element('csrf_ajax_element',
[
"name" => "myAjaxCsrfToken",
"params" => ['year'],
"url" => ["controller" => "Api", "action" => "myAjax", "_method" => "POST" ]
])
?>
<!-- When you need to use the ajax -->
<script type="text/javascript">
$.ajax({
url: $("#myAjaxCsrfToken").data('url'),
type: 'POST',
data: $.extend(
$("#myAjaxCsrfToken").data('csrf'),
{ year: 2019 }
),
complete: function() {
// things
}
});
</script>
Note: At the above, year is a custom param that need to pass to the ajax along with the token param, if you don't do it cakephp will output security error.

I also suffered from the same things, but it was solved
cakephp2.10.2
$this->Security->unlockedActions = array('action1', 'action2');

Related

CakePHP 3 : Cannot get JSON data from controller

I'm trying to return a query made in a controller to the view file so I can use that data in my form. But I am unable to successfully return the data without errors. I know the function is working because it returns the right data but has an error.
Here is my CustomersController fill function which is running the sql query.
public function fill(){
$layout = 'ajax';
$this->autoRender = false;
if ($this->request->is('post')) {
$id = $this->request->data['id'];
$query = $this->Customers->find()
->where([
'id' => $id
])->first();
echo json_encode($query);
}
}
and here is my blah.ctp which is the view file.
<?php use Cake\Routing\Router; ?>
<?= $this->Form->create(Null, ['type' => 'POST']) ?>
<?php
echo $this->Form->input('customer_id', ['options' => $customers, 'empty' => true,'id'=>'customers']);
?>
<?= $this->Form->end() ?>
<script>
document.getElementById('customers').addEventListener('change',function(){
var id = this.value;
var csrfToken = $('[name=_csrfToken]').val();
$.ajax({
type: "POST",
url: '<?php echo Router::url(array("controller" => "Customers", "action" => "fill")); ?>',
data: {'id' : id},
beforeSend: function(xhr){
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
},
success: function(data){
alert(data);
}
});
});
</script>
Currently this is what happens when I select a customer in my drop down box which triggers the script in the view.
As you can see it returns the array of data I need but also has the error cannot emit headers. I have tried solving this error following other questions on stack overflow but can't solve it.
I've tried using $this->set instead of echo json_encode but it always returns nothing. I'm not sure what other way to do this.
First of all, If you're selecting a single record by a unique ID, you can call
->get($id) on the table object directly, instead of building a query chain with ->find().
CakePHP 3.x should automatically handle converting your view to JSON by using the RequestHandlerComponent. Typically, you must enable it if your scaffolding or installation didn't.
1) Enable request handler component. (If not already enabled) https://book.cakephp.org/3.0/en/controllers/components/request-handling.html
2) Remove the echo json_encode($query) line; you should not echo anything as this will break the request/response cycle. This is the cause of your error.
Instead, you should serialize your data to the view. Assuming you have the fetched data in $data: $this->set(compact('data')). Then, make sure you add $this->set('_serialize', ['data']) (again, assuming the data is stored in variable name 'data').
3) Reference this doc for information on how you can request the json. You can use a file extension (.json, .xml).
Also, make sure you add the 'Accept: application/json' header to your request.(https://book.cakephp.org/3.0/en/development/routing.html#Cake\Routing\Router::extensions).
I apologize for the fuzzy response. There are many ways to achieve this with CakePHP3. Please see this doc page for more information: https://book.cakephp.org/3.0/en/views/json-and-xml-views.html

laravel 5.2 cannot sent large string via ajax and get method

i try to send a parameter with url to server with ajax in laravel 5.2 but when the length of url become more than 5000 char i face the error that Http not found in route
here is my ajax code :
$.ajax({
url: '/uploadQuestionMod5/',
type: 'GET',
data: {_token: CSRF_TOKEN , jcategory2:jcategory2 , jquestionText:jquestionText , jkind:jkind},
dataType: 'JSON',
success: function (data) {
}
});
and my controller is :
$jcategory2 = $request['jcategory2'];
$jquestionText = $request['jquestionText'];
$jkind = $request['jkind'];
$category2 = json_decode($jcategory2);
$questionText = json_decode($jquestionText);
$kind = json_decode($jkind);
$now = date("Y-m-d H:i:s");
$insertquestion = DB::table('questionsInfo')->insert(
['userId' => $userId ,'categoryId' => $category2 ,'questionKind' => $kind ,'questionText' => $questionText , 'questionFileUrl' => null ,'date' => $now]
);
i try to find the limit of url size in get method please help me thank you :)
Quick and Easy Solution try would be using AJAX POST not GET method.
Make your AJAX post and change your route (/uploadQuestionMod5/) in route file (/App/Http/routes.php) as
Route::match(['get', 'post'],'/uploadQuestionMod5', [
'uses'=>'YourController#method'
]);
Note: this route will accept both get and post ajax request.
If you only do post ajax then your route looks like:
Route::post('/uploadQuestionMod5', 'YourController#method');
This route will accept post request comming from ajax call or any toher source (Client)
Update: Token Mismatch Error means following
1. You have missed to submit CSRF tokens fields via AJAX post.
2. Tokens might have changed but you did not have refreshed your page, just making ajax request again and again.
It seems you are submitting your CSRF_TOKEN correctly, so refresh the page and try again to make ajax call.

securing a jquery ajax to php connection

my ajax code is:
$.ajax({
type: "POST",
url: "save.php",
data: {
name: $(this).attr('name'),
value: $(this).val(),
id: <?php if(!empty($_SESSION['user'])) echo $_SESSION['user'];?>
}
});
and in save.php i'm checking with this condition:
if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SESSION['user']!=$_POST['id']){
//then show an error
}
is this the correct method to prevent unauthorized call to save.php?
In general..
Anything at the client side is insecure. Therefore, any script may be
called from anybody at any time using any set of parameters.
Protecting specific script
Therefore, we need to prepare something at the server-side, that verifies something as valid at a later point of time.
Let's call it a security-token. A security-toke needs to be sufficiently long and random string. The security-token need to be non predictable. In this case, only the server-side application can be the source of this token.
Save this security-token to the user's session and pass it along to the client. Associate the security-toke with the script call to be protected. Your session might have this property:
$_SERVER[ 'sys$securityTokens' ]
= array(
'AHSsd67sdSJDH/D6wehsd'
=> array( 'script' => 'sensibleScript.php',
'params' => array( 'kid' => 3, 'var5' => 12 )
),
'KSD87sd78sdsfk(DDF/sd'
=> array( 'script' => 'someOhterSensibleScript.php',
'params' => array( 'value' => 'welcome!' )
)
);
Note, that this structure associates security-tokes with script-names and valid parameters to be called later on.
If client needs to call the script using JavaScript, it passes the security-token back to the server.
At the server side...
If a sensible script request comes in and the correct security-token is part of the request, remove the security-token from the session and execute the script.
If a sensible script request comes with no security-token, reject the request.
As i see what you are tying to do..
better do it this way:
$.ajax({
type: "POST",
url: "save.php",
data: {
name: $(this).attr('name'),
value: $(this).val(),
id: <?php if(!empty($_SESSION['user'])) echo $_SESSION['user'];?>
}
});
and in save.php check with this condition:
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest' || $_SESSION['user']!=$_POST['id']){
//here you can show an error
}
This new condition will also check weather the request made was through ajax or not ?
As you can see its not a real restriction of user. it would be better if you do it serverside. you can have a look at this Restrict direct page access
Its secure but there are more ways out there too..

CakePHP 1.3: passing an argument for an action

I have a dropdown form that with option values 1, 2, and 3. When this form is submitted ($this->Form->create('MyModel', array('action' => 'view'))), I'd like one of these options to be an argument variable that gets passed into the action view and go to controller/view/option_value so that I can load some appropriate data using option_value.
I can't seem to have this option_value passed as the action argument. It's a POST data, so I do have this option_value available to me in the controller, but I thought it would be good to have the value in the URL also because this view.ctp allows the user to update some things and I want the user to be redirected to the referrer easily.
Any ideas? I think this is a client-side issue.. but I'm not yet familiar with Javascripts.
Edit:
Still looking into how to do this.. but for now, I am just redirecting the form-submitted page, which does not include the option_value in the URL, to the same page with the option_value as the parameter. It works for what I need, but if anyone would like to add an answer, I'd appreciate it!
Using jQuery:
<?php
echo $this->Form->create('MyModel', array('id' => 'myForm', 'action' => 'view'));
echo $this->Form->input('MyField', array('id' => 'myField'));
echo $this->Html->scriptStart();
?>
$('#myForm').on('submit', null, null, function(evt) {
var form = $('myForm'),
field = $('#myField');
form.action += "/" + field.value;
form.submit();
evt.stopPropagation();
});
<?php
echo $this->Html->scriptEnd();
echo $this->Form->end(__('Submit', true));
?>
Try that, you might have to tweak some stuff to your needs. Cheers.
-Andrew

Call a function through jquery ajax

It's been 2 days now and still I can't get this problem to work.
So basically I am trying to call a php function through jquery ajax
but nothing is working, I am not sure what is the problem is...
here are my codes
the ajax
$(document).ready(function() {
$("#idCheck1").click(function(){
$.ajax({
url:'../../../../Controller/PostsController.php',
data: {action: 'update_checkbox'},
success:function(result){
// $("#dsa").html(result);
}
});
});
});
the view
<?php echo $this->Form->create("Posts", array("action" => "update_checkbox", "id" => "checkingBox")) ?>
<td>
<?php
echo $this->Form->input('Post.' . $i . '.id', array("type" => "hidden", "label" => false, "value" => $sum['posts']['id']))
?>
<?php
echo $this->Form->input('Post.' . $i . '.done', array("type" => "checkbox", "label" => false, "value" => "1", "id" => "idCheck1"))
?>
</td>
</tr>
<?php
$i++;
}
?>
</table>
<?php echo $this->Form->end(); ?>
the controller
public function update_checkbox() {
// debug($this->data);
$var = $this->Post->saveCheckBox($this->data);
$this->set("result", $var);
}
the model
public function saveCheckBox($checkbox) {
debug($checkbox);
$this->saveAll($checkbox['Post']);
}
The url
url:'../../../../Controller/PostsController.php'
looks wrong. Since this is an AJAX request that goes through the browser you can't use relative paths that try to go upwards in the folder hierarchy, as browsers url's don't work that way. You should be making that request so that it is passed through a web server, i.e. the url should look like one of the following:
url:'http://localhost/Controller/PostsController.php'
or
url:'/Controller/PostsController.php'
The first option is an absolute url, but this also makes the code a bit less flexible (suppose you change the domain from localhost to something else). The second option is a relative URL, but one that is relative to the domain root of your web server (i.e. in the example it would still resolve to localhost/Controller...).
In both cases based on what you've posted, your PHP file should live in a Controller/ folder in the document root of your site. The structure of your code however suggests that you are using a framework of some kind (e.g. Zend, Symfony or CodeIgniter)? If that is the case it would be helpful if you post information on what framework you're using as well as that might change the answer.
UPDATE
In the case of cakePHP, you should access the controller through the front controller, meaning the URL should look like this:
url:'/posts'
UPDATE 2
For the jquery side, a complete ajax request example could look like this:
$(".idCheck").click(
function(){
var idVal = $(this).val();
$.ajax({
url:'/posts/update_checkbox',
data: {id: idVal },
type: 'POST',
success:function(result){
$("#dsa").html(result);
}
});
});
Note that the URL already contains the update_checkbox action (i.e. the complete url to the action you want to execute), and the data object contains the value of the clicked element that you want to send to the server. If you want to send a complete form you could also use $('#myformselector').serialize() to convert all inputs in the form to a object suitable for the data property of the ajax request.

Categories