Unable to validate CSRF at controller in codeIgniter with ajax - php

I am new to CSRF and codeigniter. However, I have referred so many solution here and after applying I am able to go some extent but unable to validate the CSRF at controller end. I am getting the error
The action you have requested is not allowed.
I am posting my entire code here. Though I am using this for registration but I am not using Form to submit the request but with the help of ajax.
Config
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrfName';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
View
I am not going to post All the fields but few. Firstname, lastname, email, password.. etc are there
<?php
$csrf = array(
'csrfName' => $this->security->get_csrf_token_name(),
'csrfHash' => $this->security->get_csrf_hash()
);
?>
<input type="hidden" class="csrfName" value="<?php echo $csrf['csrfName'];?>" />
<input type="hidden" class="csrfHash" value="<?php echo $csrf['csrfHash'];?>" />
<div class="row">
<div id="join_btn" onclick="email_signup('joinBox')">SIGN UP</div>
</div>
JS
There are other fields as well but i am posting here important one
var csrfValue = $(".csrfHash").val();
var csrfName = $(".csrfName").val();
var callParameters = "call=email_signup&csrfHash="+csrfValue+"&csrfName="+csrfName;
$.ajax({
url:ajaxcalls.php,
data:callParameters,
type:"POST",
cache:false,
dataType:"json",
success: function(resp){
}
});
Controller
function email_signup($params){
$csrfName = $this->security->get_csrf_token_name();
$csrfHash = $this->security->get_csrf_hash();
$result['flag'] = "success";
echo json_encode($result);
}

Well, it's because you're doing it all wrong.
Mistake 1:
Your type="hidden" input elements have no name=... attributes.
Mistake 2:
The input element that holds the csrfName i.e (name="<?php echo $csrf['csrfName'];?>") expects a csrfHash as it's value=....
In addition, both the csrfName and csrfHash need to be defined in one input HTML element. Not two.
Cross-site request forgery (CSRF)
So:
<!-- Instead of: -->
<input type="hidden" class="csrfName" value="<?php echo $csrf['csrfName'];?>" /> ❌
<input type="hidden" class="csrfHash" value="<?php echo $csrf['csrfHash'];?>" /> ❌
<!-- Use this: -->
<input type="hidden" name="<?php echo $csrf['csrfName'];?>" class="csrfName" value="<?php echo $csrf['csrfHash'];?>" /> ✅
Suggestions
Since you're using jQuery already, to prepare your POST data, the .serializeArray() method would sound sufficient instead of manually building 'query strings'. i.e:
// ...
let $formData = $("form").serializeArray()
// ...
You can then append any extra data you intend to pass along with the HTML form elements if you wish..i.e:
// ...
let $formData = $("form").serializeArray()
$formData.push({name: 'call', value: 'email_signup'})
$.ajax({
// ...
data: $formData,
// ...
});
Alternatively, you can build the POST data using FormData().
If you choose this option, don't forget to add processData: false and contentType: false to your $.ajax({...}) object argument. i.e:
let $formData = new FormData($("form").get(0))
$formData.append('call', 'email_signup')
$.ajax({
// ...
data: $formData,
processData: false,
contentType: false,
// ...
});

If you are using Codeigniter vertion 3.0, add this in config:
$config['csrf_regenerate'] = FALSE;

Related

$_SESSION is conflicting with $_FILES

I have a form that had a file type input to upload images, but it would always show up the "notice: undefined index" and I saw that the $_FILE was coming empty, but none of the answers that I found online have helped me.
I have a project where after the user registers he has to login and his first page he confirms his data and insert some new ones, one of this new ones is a picture that he can upload, as I said, my initial problem was that the $_FILES was always coming up empty, I searched online and this is a fairly common error but none of the answers have helped me, after a lot of trying I found out what the problem is, but I still have no Idea why, or how to fix it.
At the start of the page that I talked about there's this bit off code here:
if(isset($_SESSION['logado'])){
$dados = $_SESSION['dadosUsu'];
}else{
unset($_SESSION['dadosUsu']);
session_destroy();
header("Location: homeLandingPage.php");
}
to stop unnauthorized people into the page or create a variable with the user data so that it can be used to fill out some of the inputs with the info that the user had already registered.
I found that if I removed this bit off code, the $_FILES global started working and the code runned fine, but if the code was there the problems continued, I have no ideia why this is happening and I couldn't find anything online that talk about a session conflicting with the $_FILES global.
after that there's this bit of ajax:
$(function(){
$('.form').submit(function(){
$.ajax({
url: 'cod_alterarAcc.php',
type: 'POST',
data: $('.form').serialize(),
success: function(data){
if(data != ''){
$('.recebeDados').html(data);
document.getElementById('visor1').value = '<?= $dados['nomeUsu']; ?>';
document.getElementById('visor2').value = '<?= $dados['emailUsu']; ?>';
document.getElementById('visor3').value = '<?= $dados['emailUsu']; ?>';
document.getElementById('visor4').value = '';
document.getElementById('visor5').value = '';
document.getElementById('visor6').value = '';
}
}
});
return false;
});
});
I don't know if it's this but here it's the preview function that is called onChange on the input type file:
function previewImagem() {
var imagem = document.querySelector('input[name=img]').files[0];
var preview = document.querySelector('img[id=dup]');
var reader = new FileReader();
reader.onloadend = function () {
preview.src = reader.result;
}
if (imagem) {
reader.readAsDataURL(imagem);
} else {
preview.src = "";
}
}
here is the form that Im using:
<form enctype="multipart/form-data" class='form' method='post' action='cod_alterarAcc.php'>
<div id="img-perfil">
<img src="imagens/perfil.png" id="dup"/>
<label for="selecao-arquivo" class="selecionar-img">+</label>
<input id="selecao-arquivo" type="file" name="img" class="botao-img" onchange="previewImagem()" />
</div>
<label>Confirme seu nome:</label>
<input type="text" id="visor1" name="nome_usu" value="<?= $dados['nomeUsu']; ?>" />
<label>Altere seu email:</label>
<input type="email" id="visor2" name="email" value="<?= $dados['emailUsu']; ?>" />
<label>Confirme seu email:</label>
<input type="email" id="visor3" name="confirmaEmail" value="<?= $dados['emailUsu']; ?>" />
<label>Sua senha:</label>
<input type="password" id="visor4" name="senha" />
<label>Confirme sua senha:</label>
<input type="password" id="visor5" name="confirmaSenha" />
<label>Insira seu cpf:</label>
<input type="text" id="visor6" name="cpf_usu" />
<div class='recebeDados'></div>
<input type="submit" value="confirmar" />
</form>
on the cod_alterarAcc.php page has some validations using filter_input_array() and then I have this:
$extensao = strtolower(substr($_FILES['img']['name'], -4));
$novo_nome = sha1(time()) . $extensao;
$diretorio = "imgsBanco/";
$ext = strtolower(substr($_FILES['img']['name'], -3));
$tipos = array("png","jpg","gif");
$imagem = $diretorio.$novo_nome;
I have some more validation regarding the other inputs and then this, it's the first time that I use the session in this page:
if (in_array($ext, $tipos)) {
if (move_uploaded_file($_FILES['img']['tmp_name'], $imagem)) {
$codAcesso = $_SESSION['dadosUsu']['codAcesso'];
$codUsu = $_SESSION['dadosUsu']['codUsu'];
$senhaEncript = Bcrypt::hash($infoPost['senha']);
after that I do an update on everything in the database, including the image url which is the $imagem variable, after that I only look at the session one more time to send the user to another page depending on it's user type.
I've also tested for most of the possibilities in here Why would $_FILES be empty when uploading files to PHP? and it didn't worked
Like I said, apparently the only thing that it's messing with the $_FILES it's that part where I use the session on the first page, if someone could help me fix this problem and explain why it's happening in the first place I would appreciate it a lot.
It seems that the default action of the form is conflicting with your AJAX, but if you want to use only AJAX in uploading your file then you want to prevent the default action of the form. Your AJAX request should be look like this.
$('.form').submit(function(e){
e.preventDefault(); // Preventing the default action of the form
var formData = new FormData(this); // So you don't need call serialize()
$.ajax({
url: 'cod_alterarAcc.php',
type: 'POST',
data: formData,
success: function (data) {
if(data != ''){
$('.recebeDados').html(data);
document.getElementById('visor1').value = '<?= $dados['nomeUsu']; ?>';
document.getElementById('visor2').value = '<?= $dados['emailUsu']; ?>';
document.getElementById('visor3').value = '<?= $dados['emailUsu']; ?>';
document.getElementById('visor4').value = '';
document.getElementById('visor5').value = '';
document.getElementById('visor6').value = '';
}
},
cache: false,
contentType: false,
processData: false
});
});
also put your form into FormData and specify your ajax request type.
EDIT
Try to confirm if PHP able to get the data with
print_r($_POST);
print_r($_FILES);
And in your AJAX success function
console.log(data);
EDIT
Forgot to put the parameter e on form.submit

jQuery - send multidimensional form data to PHP script

I have a jQuery script that adds hidden inputs into a form whenever a certain .class input undergoes a change. Depending on user input, it generates values for other uneditable columns which also get pushed into a form as hidden inputs.
The form output looks like this:
<input type="hidden" name="[1008016BSTL][1][part]" value="1008016BSTL" />
<input type="hidden" name="[1008016BSTL][1][price]" value="123" />
<input type="hidden" name="[1008016BSTL][1][priceExVat]" value="102.50" />
<input type="hidden" name="[1008016BSTL][1][fee]" value="10.53" />
<input type="hidden" name="[1008016BSTL][1][profit]" value="68.41" />
This is just one set of data I'm trying to capture, but it's the same for the others, save the original key and sub-key.
My form wrapper looks like this:
<form method="post" id="submit-form" enctype="multipart/form-data">
<input type="submit" value="Save" />
</form>
With my AJAX looking like:
$('form#submit-form').submit(function(e)
{
e.preventDefault();
let data = $('form#submit-form').serializeArray();
$.ajax({
url: '/save-pricing.php',
data: {data: JSON.stringify(data)},
type: 'post',
success: function(res)
{
console.log(res)
},
error: function(res)
{
alert('Error! I won\'t tell you what it is. But, I\'ll give you a clue: 21');
console.log(res)
}
})
})
I've also tried (for setting data):
let data = $('form#submit-form').serialize();
data = JSON.stringify(data);
$.ajax({
...
data: {data: data}
...
})
As well as omitting the .stringify() function.
This comes through to PHP like this:
<?php
echo '<pre>'. print_r($_POST, 1) .'</pre>';
/**
* Below is for .serialize() -> output is an empty array
*
* parse_str($_POST['data'], $postData)
* echo '<pre>'. print_r($postData, 1) .'</pre>';
*/
simplified output (just removing the other sets) with .serializeArray():
Array
(
[data] => [
{"name":"[1008016BSTL][1][part]","value":"1008016BSTL"},
{"name":"[1008016BSTL][1][price]","value":"123"},
{"name":"[1008016BSTL][1][priceExVat]","value":"102.50"},
{"name":"[1008016BSTL][1][fee]","value":"10.53"},
{"name":"[1008016BSTL][1][profit]","value":"68.41"}
]
)
This is OK I guess, I could probably group by name and merge into an array, but there feels like it should already do this with .serialize() on jQuery-side and then parse_str() on the PHP side.
However, as I've mentioned, parse_str() and .serialize() yield an empty array, which I can't use.
so my question is: How do I successfully send multi-dimensional form data to PHP via jQuery?
Edit
Added:
dataType: 'json'
with .serialize() and then JSON.stringify(data), removed parse_str() and it outputs:
Array
(
[\"] => Array
(
[1008016BSTL] => Array
(
[1] => Array
(
[part] => 1008016BSTL
)
)
)
)
Input fields names with brackets are not treated nicely by serializeArray. This below code will create a proper multidimentional array you can send back to the server.
$('form#submit-form').submit(function(event)
{
event.preventDefault();
//Prevent the form from submitting
var fields = {};
//This is where you're gonna store your form fields
$.each($('form#submit-form').serializeArray(), function(i, field) {
//Get values, even from multiple-selects
if (Array.isArray(fields[field.name])) {
fields[field.name].push(field.value);
} else if (typeof fields[field.name] !== 'undefined') {
var val = fields[field.name];
fields[field.name] = new Array();
fields[field.name].push(val);
fields[field.name].push(field.value);
} else {
fields[field.name] = field.value;
}
});
//Now all the fields are in the fields object
//You're now going to translate "key[subkey]" string to key[subkey] object
for (var key in fields) {
var parts = key.split(/[[\]]{1,2}/);
parts.length--;
if (parts.length) {
var val = fields[key];
delete fields[key];
addToTree(fields, parts);
setToValue(fields, val, parts);
}
//input field array names (with brackets) are mistakenly treated as strings, this fixes it
}
$.ajax({
url: '/save-pricing.php',
data: JSON.stringify(fields),
contentType: 'application/json',
type: 'post',
success: function(res) {
console.log(res)
},
error: function(res) {
alert('Error! I won\'t tell you what it is. But, I\'ll give you a clue: 21');
console.log(res)
}
})
});
/**
* Adds values to a tree.
* #link https://stackoverflow.com/questions/3663096/how-to-convert-array-to-tree
*/
function addToTree(tree, array) {
for (var i = 0, length = array.length; i < length; i++) {
tree = tree[array[i]] = tree[array[i]] || {}
}
}
/**
* Sets object values.
* #link https://stackoverflow.com/questions/13719593/how-to-set-object-property-of-object-property-of-given-its-string-name-in-ja
*/
function setToValue(obj, value, path) {
for (i = 0; i < path.length - 1; i++) {
obj = obj[path[i]];
}
obj[path[i]] = value;
}
with the PHP side using json_decode:
$data = json_decode(file_get_contents('php://input'), true);
echo '<pre>'. print_r($data, 1) .'</pre>';
For your particular issue you can the jquery.serializeJSON
Here is the link of their github https://github.com/marioizquierdo/jquery.serializeJSON
This will create the correct json object.
This is simplest solution I have for this case.
<?php if(isset($_POST["data"])) {
$post_data = urldecode($_POST["data"]);
parse_str($post_data, $form_data);
// this will give you first element of array by eliminating double quote key ('') in post data array, which is also desired
$form_data = reset($form_data);
echo '<pre>'; print_r($form_data); echo '</pre>'; exit;
} else { ?>
<form method="post" id="submit-form">
<input type="hidden" name="[1008016BSTL][1][part]" value="1008016BSTL" />
<input type="hidden" name="[1008016BSTL][1][price]" value="123" />
<input type="hidden" name="[1008016BSTL][1][priceExVat]" value="102.50" />
<input type="hidden" name="[1008016BSTL][1][fee]" value="10.53" />
<input type="hidden" name="[1008016BSTL][1][profit]" value="68.41" />
<input type="submit" value="Save" />
</form>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$("#submit-form").on('submit', function(e){
e.preventDefault();
var form_data = $("#submit-form").serialize();
$.ajax({
type: "POST",
data: {data: JSON.stringify(form_data)},
success: function(res){
console.log(res);
}
});
});
</script>
<?php } ?>

Updating multiple records in PHP using ajax

I'm needing some help with the following code. I am trying to update multiple rows in my database using an ajax call. I am not receiving any error messages and nothing is appearing in my browser log either. I can see that the click function is executing but the database query is not being executed. Can someone point me in the right direction?
Thanks for any help you can give.
Update - The code below has been updated to work. I have commented out the original, non-working code.
My input fields:
<input type="hidden" id="Opportunity_Id" name="Opportunity_Id" value="<?php echo $opportunity->Opportunity_Id; ?>"/>
<input type="hidden" id="Class_Numbers[]" name="Class_Numbers" value="<?php echo $Class_Number; ?>"/>
<input type="text" id="Class_Dates[]" name="Class_Dates" value="<?php echo $Class_Date; ?>"/>
<input type="text" id="Start_Times[]" name="Start_Times" class="col-xs-12 col-sm-11" value="<?php echo $Preferred_Start_Time; ?>"/>
<input type="text" id="End_Times[]" name="End_Times" class="col-xs-12 col-sm-11" value="<?php echo $Preferred_End_Time; ?>"/>
My jQuery function:
/*$("#test").click(function() {
$("#validation-form").ajaxForm({url: 'schedule/schedule_opportunity', type: 'post'});
var form_data = {
Opportunity_Id: $('#Opportunity_Id').val(),
Class_Number: $('#Class_Numbers').val(),
Class_Date: $('#Class_Dates').val(),
Start_Time: $('#Start_Times').val(),
End_Time: $('#End_Times').val(),
ajax: '1'
};
$.ajax({
url: "<?php echo site_url('schedule/schedule_opportunity'); ?>",
type: 'POST',
data: form_data,
dataType: 'json',
cache: false
});
return false;
})*/
$('#validation-form').submit(function(e){
e.preventDefault();
var form = $(this);
$.ajax({
url: "<?php echo site_url('schedule/update_classes'); ?>",
method: form.prop('method'),
data: $('#validation-form').serialize(),
success: function(){
alert('Success');
}
});
})
My controller:
function update_classes() {
$Opportunity_Id = $this->input->post('Opportunity_Id');
$Class_Numbers = $this->input->post('Class_Numbers');
$Class_Dates = $this->input->post('Class_Dates');
$Start_Times = $this->input->post('Start_Times');
$End_Times = $this->input->post('End_Times');
$this->ion_auth_model->update_classes($Opportunity_Id, $Class_Numbers, $Class_Dates, $Start_Times, $End_Times);
}
My model:
function update_classes($Opportunity_Id, $Class_Numbers, $Class_Dates, $Start_Times, $End_Times) {
foreach($Class_Numbers as $key => $Class_Number) {
//$Opportunity_Id = $Opportunity_Id[$key];
$Class_Date = $Class_Dates[$key];
$Start_Time = $Start_Times[$key];
$End_Time = $End_Times[$key];
$Class = array(
'Class_Date' => $Class_Date,
'Start_Time' => $Start_Time,
'End_Time' => $End_Time
);
$this->db->where('Opportunity_Id', $Opportunity_Id);
$this->db->where('Class_Number', $Class_Number);
$this->db->update('Classes', $Class);
}
}
Well, I think it's time for you to do some debugging. What's very unclear to me is how you plan to get multiple values for all the fields from the front-end, but that being said, these are the problems in your code that I see at this point:
Opportunity_Id: $('#Opportunity_Id').val() => I see no field with the ID "Opportunity_Id". (Fixed by question edit)
Class_Number: $('#Class_Numbers').val(), etc. => There is no field with the ID "Class_Numbers". There is a "Class_Numbers[]", but that will not be returned in your lookup. Same goes for all the other fields.
You seem to be under the impression that JQuery will automatically create some sort of array from your textbox values and pass them to the server. That's not going to happen.

How to use post() jquery method with codeigniter form_validation

I want to validate a form without refreshing the page using the .post() jQuery method.
I use codeigniter for validation. Could you please tell me how to make it right? I find it pretty confusing ...
Here is the jQuery code:
$(document).ready(function(){
$(".form_errors").hide();
$("#send").on("click",function(){ //the submit button has the id="send"
$(".form_errors").hide(); //these are <p> for each input to show the error
var user=$("input.box");
var data={};
var names=$("input.box").attr("name");
for(i=0;i<user.length;i++){
name=names[i];
value=user[i].val();
data.name=value;
}
$.post('ksite/account',
data,
function(result){
$("div.answer").html(result);
for(i=0;i<user.length;i++){
error_message=<?php echo form_error("?>names[i]<?php ");?>;
$("p#error_"+names[i]+".form_errors").html(error_message).show();
}
}
return false;});
});
form_error is a CodeIgniter function. (I suppose someone who used ci is familiar with).
The form:
<p id="error_user" class="form_errors"></p>
<input type="text" class="box" name="user">
<p id="error_password" class="form_errors"></p>
<input type="password" class="box" name="password">
<input type="submit" id="send">
Is the form tag neccessary ? And if yes,do i have to mention action and method ?
Do I have to specify the type of the response?
And in ksite/account I do:
/* ...... */
if (!this->form_validation->run(''account")) {
echo "The account couldn't be made";
} else {
echo "The account was successfully created ";
}
P.S.Although you may not be familiar with codeigniter, I would appreciate if someone could tell me if the code is correct and what improvements could be made.
Here is what I did.
You have to Ajax for getting data without refreshing the page.
HTML Page
$form = $(form);
var url = $form.attr('action');
dataString = $form.serialize();
$.ajax({
type: "POST",
url: url,
data: dataString,
dataType: "json",
success: function(data) {
$(data).each(function(j,details){
var status = details.status;
var message = details.message;
$('#message_ajax_register').show();
$('#message_ajax_register').html('<div class="alert alert-success">'+message+'</div>');
});
}
});//end of $.ajax**
I am first setting up the rules in my controller method and then validating it.
Controller
public function update_fest()
{
if($this->input->post())
{
$this->form_validation->set_rules('txtgpluswebsite', 'Google Plus Page URL', 'trim|xss_clean|prep_url');
$this->form_validation->set_error_delimiters('<div class="error">', '</div>');
if($this->form_validation->run() == false){
$message = validation_errors();
$data = array('message' => $message,'status'=>0);
}
else{
$message = $this->add_fest_database();
$data = $message;
}
}
else{
$message = "Fest details are required";
$data = array('message' => $message,'status'=>0);
}
$this->output->set_content_type('application/json');
$json = $this->output->set_output(json_encode($data));
return $json;
}
If validation run is not false, then go to add_fest_database(other function). In that function,
function add_fest_database()
{
$youtubeWebsite = $this->input->post('txtyoutubewebsite');
$gplusWebsite = $this->input->post('txtgpluswebsite');
$this->load->model('model_fest');
$data = array("fest_youtube"=>$youtubeWebsite,"fest_gplus"=>$gplusWebsite);
return data;
}

Code Igniter Bonfire ajax request 500 internal server error

So I am trying to submit just an email address using ajax to get people to register and I have no idea why I am getting a 500 internal server error. I am new to ajax calls.
I have tried to follow the following tutorial: http://www.youtube.com/watch?v=TZv5cgua5f0
However I have done as they have said and still if I do a post with values I do not get to the desired controller method. If I do add data to the post then I get an internal server error.
javascript:
$('#email_submit').click(function()
{
var form_data =
{
users_email: $('#users_email_address').val()
};
$.ajax
({
url: 'http://localhost/myZone/NewsLetter/submit',
type: 'POST',
data: form_data,
success: function(msg)
{
alert(msg);
}
});
return false;
});
HTML
<div id="email_newsletter_signup" class="ajax_email_block_signup" >
<h3>Sign up to the newsletter:</h3>
<?php echo form_error('signup_email','<div id="email_error" class="error">','</div>');?>
<h3>email: <input id="users_email_address" type="email" name="signup_email" value="<?php echo set_value('signup_email'); ?>" placeholder="Your email"/> </h3>
<input id="email_submit" type="submit" name="submit"/>
</div>
contoller
public function index()
{
Assets::add_module_js('newsletter','email.js');
//If included will be added
Template::set_block('email_block','email_block');
Template::render();
}
public function submit($email)
{
$success = $this->newsletter_model->set_unverified_email($email);
// if($success === FALSE)
// {
// Template::set_block('newsletter_error','newsletter_error');
// }
// else
// {
// Template::set_block('newsletter_success','newsletter_success');
// }
// Template::render();
return;
}
I have a breakpoint inside the submit and it just wont be hit when I do a post
Thanks
Found my solution. Nothing to do with bonfire but with the codeigniter. It was the CSRF token.
Here is an excellent post about sorting the issue:
http://aymsystems.com/ajax-csrf-protection-codeigniter-20
add csrf token to data before posting
$.ajax({
type: "POST",
url: url,
data: {'<?php echo $this->security->get_csrf_token_name(); ?>':'<?php echo $this->security->get_csrf_hash(); ?>'}
})
csrf token needs to be send along every request so it needs to be specified by the above echo statements

Categories