Codeigniter CSRF valid for only one time ajax request - php

I want to upload image on the server on change event of jQuery but using codeigniter csrf I am able to upload image only one time. How can I upload images using ajax for multiple requests.Please keep in mind when I set this
config['csrf_protection'] = FALSE;
then I am able to send multiple request jQuery onchange event but when csrf_protection is going to be false then I think there is no advantage of csrf. so the question is how can I send multiple requests using ajax while csrf_protection is enable. My jquery code is following
$("#avatar").change(function(){
var link = $("#avatar").val();
$.ajax({
url : "<?php echo base_url('main/test'); ?>",
type: 'post',
data: {'<?php echo $this->security->get_csrf_token_name(); ?>':'<?php echo $this->security->get_csrf_hash(); ?>',"id":"hello","link":link},
success : function(data)
{
alert(data);
}
});
});
My controller:
public function test()
{
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = 500;
$config['max_width'] = 260;
$config['max_height'] = 260;
$this->load->library('upload', $config);
if (!$this->upload->do_upload('link')) {
echo "error";
} else {
$data = array('upload_data' => $this->upload->data());
$image_name = $data['upload_data']['file_name'];
echo $image_name;
}
}

In my opinion you should try to recreate your csrf token each request
Try this code example...
For the js funcion
var csrfName = '<?php echo $this->security->get_csrf_token_name(); ?>',
csrfHash = '<?php echo $this->security->get_csrf_hash(); ?>';
("#avatar").change(function(){
var link = $("#avatar").val();
var dataJson = { [csrfName]: csrfHash, id: "hello", link: link };
$.ajax({
url : "<?php echo base_url('main/test'); ?>",
type: 'post',
data: dataJson,
success : function(data)
{
csrfName = data.csrfName;
csrfHash = data.csrfHash;
alert(data.message);
}
});
});
and for the controller
public function test() {
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = 500;
$config['max_width'] = 260;
$config['max_height'] = 260;
$reponse = array(
'csrfName' => $this->security->get_csrf_token_name(),
'csrfHash' => $this->security->get_csrf_hash()
)
$this->load->library('upload', $config);
if (!$this->upload->do_upload('link')) {
$reponse['message'] = "error";
}
else {
$data = array('upload_data' => $this->upload->data());
$image_name = $data['upload_data']['file_name'];
$reponse['message'] = $image_name;
}
echo json_encode($reponse);
}
Let me know and good luck
Note: When someone ask you for posting more data to the question, don't post it as a comment or answer, it's better to edit the question itself and adding the stuff

You can set this in config.php
$config['csrf_regenerate'] = FALSE;
so the csrf protection is valid during all the session time it will solve your problem.
If you set
$config['csrf_regenerate'] = true; then CI generate new csrf token every request so your old csrf token not match with new generated csrf token

$config['csrf_regenerate'] = TRUE;
keep auto generate to true it will be more safer.
In similar case when csrf is expired in first request. What i have implemented
$(document).ajaxComplete(function (event, xhr, settings) {
let response = xhr.responseText,
let obj = JSON.parse(response),
let csrfData = obj.csrf;
document.querySelector('input[name="' + csrfData.name + '"]').value = csrfData.hash;
}); //Also here you can update any other non input element
In every ajax response we are passing csrf data in which latest csrf data will be replaced with current one
Sample response from request
{
csrf : {
name : 'csrf_name',
hash : 'qw76sd7s6f78sdfs8dfs9df8cx9'
}
}
I update csrf token in every ajax request. Also don't choose this method if you are working with multi tab environment

Each time you make a request, the csrf_token is being updated by CI. That's why the CSRF only work once. So everytime we make a request we need to update the csrf_token too. I solve this problem by doing this.
Conroller: get the updated csrf using this code.
public function update_csrf()
{
$data['csrf_hash'] = $this->security->get_csrf_hash();
echo json_encode($data);
}
AJAX: replace the old value of your csrf name="csrf_token_name"
var jqXHR = $.ajax({
url: $(this).attr('action'),
type: 'POST',
data: $(this).serialize(),
dataType: 'json',
})
jqXHR.done(function(response) {
$('input[name=csrf_token_name]').val(response.csrf_hash); //update the csrf to the form
})
jqXHR.fail(function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
});
Important!: use dataType: 'json'
So now each time you have a successful request, the csrf_token is updated too and you are now free from 403 (Forbidden) error.

All you need to do is reload the CSRF token in your AJAX response. It is that simple!.

Maybe you can try using jquery cookie
First you need to add this
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
then change your code to this
$("#avatar").change(function(){
var link = $("#avatar").val();
$.ajax({
url : "<?php echo base_url('main/test'); ?>",
type: 'post',
data: {csrf_test_name: $.cookie('csrf_cookie_name'),"id":"hello","link":link},
dataType : "JSON",
success : function(data)
{
alert(data);
}
});
Finally you can try to set your csrf_protection to true
[csrf_protection] = TRUE;

add this at a js file which is loaded every page (I put this at the end of jquery.js )
$.ajaxSetup({
beforeSend:function(jqXHR, Obj){
var value = "; " + document.cookie;
var parts = value.split("; csrf_cookie_name=");
if(parts.length == 2)
Obj.data += '&csrf_token='+parts.pop().split(";").shift();
}
});
(notice that in every ajax request you can not have empty data to send)
"csrf_cookie_name" at top defined in config.php
$config['csrf_cookie_name'] = 'csrf_cookie_name';

Please try like my code. its working my application
your view file
$token_name = $this->security->get_csrf_token_name();
$token_hash = $this->security->get_csrf_hash();
<input type="text" id="search-text" name="parent_name" placeholder="Search" value="" >
<input type="hidden" id="csrf" name="<?php echo $token_name; ?>" value="<?php echo $token_hash; ?>" />
after set jquery post method like below
// Get Keyup
jQuery( "#search-text").keyup(function() {
// Get Data
var val = jQuery("#search-text").val();
var hashValue = jQuery('#csrf').val();
// Get jquery post for ajax task
jQuery.post(
'<?php echo $base_controler; ?>',
{val:val,'<?php echo $this->security->get_csrf_token_name(); ?>':hashValue},
function(data)
{
// Get return data to decode
var obj = jQuery.parseJSON(data);
// Get csrf new hash value
var new_hash = obj.csrfHash;
// Set csrf new hash value update
jQuery('#csrf').val(new_hash);
}
);
});
please set your controller like below
$reponse = array(
'csrfName' => $this->security->get_csrf_token_name(),
'csrfHash' => $this->security->get_csrf_hash()
);
echo json_encode($reponse);
above all code recreate your csrf token each request.

Edit the config:
$config['csrf_exclude_uris'] = ['controller/method'];
Array can include all whitelisted controllers/methods you want the csrf protection to be disabled for.
The array can also handle regular expressions such as:
$config['csrf_exclude_uris'] = array(
'api/record/[0-9]+',
'api/title/[a-z]+'
);
For more information visit Codeigniter Documentation - Security Class

Related

I am unable to post data using ajax on url friendly blog detailing page where I am already using url value in Query

I know that it may be so tricky!
In detail:
on the blog detailing page(blog-single.php/title) I have a subscription form this subscription form is working fine on another page with the same PHP action file and ajax
and blog-single.php/title is also working fine until I did not submit the form
On this page in the starting, I have bellow query
<?php
$query_head="SELECT * FROM blog_post WHERE friendly_url = '{$_GET['url']}' ";
$result_head= $fetchPostData->runBaseQuery($query_head);
foreach ($result_head as $k0 => $v0)
{
//----- some echo
}
?>
and my subscription form code:
<form action="" method="post" class="subscribe_frm" id="subscribe_frm2">
<input type="email" placeholder="Enter email here" name="email" id="subscribe_eml2">
<button type="button" id="subscribe2">Subscribe</button>
</form>
and ajax code is bellow:
$(document).ready(function() {
$("#subscribe2").click( function() {
subscribe_frm_val2 = false;
/*email validation*/
var emailReg2 = /^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/;
if ($("#subscribe_eml2").val().length <= 0) {
$("#subscribe_eml_Err2").html("Required field");
//console.log("Required");
subscribe_frm_val2 = false;
}
else if(!emailReg2.test($("#subscribe_eml2").val()))
{
$("#subscribe_eml_Err2").html("Enter a valid email");
}
else{
$("#subscribe_eml2").html("");
subscribe_frm_val2 = true;
//console.log("final else");
if(subscribe_frm_val2 == true)
{
console.log("frm true");
var form = $('#subscribe_frm2')[0];
var data = new FormData(form);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "updation/subscribe_action.php",
data: data,
processData: false,
contentType: false,
cache: false,
timeout: 6000000,
beforeSend: function(){
// Show image container
$("#submition_loader").show();
//alert ("yyy");
},
success: function (data) {
// console.log();
$(document).ajaxStop(function(){
$("#subscribe_eml_Err2").html(data);
});
},
complete:function(data){
// Hide image container
$("#submition_loader").hide();
}
});
}
else{
alert('Please fill all required field !');
}
}
});
});
When I submit my form above the first query is giving a warning like below:
Warning: Invalid argument supplied for foreach() in D:\xamp\htdocs\my\bootstrapfriendly\category.PHP on line 13
and after warning page doing misbehave
I think the error because of URL passing but I am not sure how to solve it
Please help me with solving it.
Thank's
I got the solution
its very simple just converted my relative URL into an absolute URL
I just created a PHP function for base URL
function base_url(){
if(isset($_SERVER['HTTPS'])){
$protocol = ($_SERVER['HTTPS'] != "off") ? "https" : "http";
}
else{
$protocol = 'http';
}
return $protocol . "://" . $_SERVER['HTTP_HOST'];
}
and then using this base URL function inside script like this
$.ajax({
----
url: "<?php echo base_url()?>/updation/subscribe_action.php",
-----
});

Jquery file upload not working in Laravel

I have tried majority of other questions here and other solutions and nothing has worked so far.
What I am trying to accomplish is upload images before Laravel's validation takes place, obviously I can't use the create function because it wont be hit until validation succeeds so I have made a custom function to do the file saving server side and trying to use Ajax to call that function every time a file is selected.
Current issue: doesn't seem like my Ajax is running on debugging its being skipped over,
second issue: I have a csrf token in my master template do i still need to add the ajax setup? if so is the way i am doing it correct.
Route:
Route::post('/upload', 'UploadController#uploadSubmit');
View:
<div>
<input type="file" id="fileupload" name="photos[]" data-url="/upload" multiple />
<br />
<div id="files_list"></div>
<p id="loading"></p>
<input type="hidden" name="file_ids" id="file_ids" value="" />
</div>
Ajax call:
$(document).ready(function(){
$("input").change(function(){
alert('triggered');
debugger;
$('#fileupload').fileupload({
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $(meta[name="csrf-token"]).attr('content')
}
dataType: 'json',
add: function (e, data) {
$('#loading').text('Uploading...');
data.submit();
},
done: function (e, data) {
$.each(data.result.files, function (index, file) {
$('<p/>').html(file.name + ' (' + file.size + ' KB)').appendTo($('#files_list'));
if ($('#file_ids').val() != '') {
$('#file_ids').val($('#file_ids').val() + ',');
}
$('#file_ids').val($('#file_ids').val() + file.fileID);
});
$('#loading').text('');
}
});
});
});
});
Controller:
public function uploadSubmit(Request $request){
$files = [];
dd(request());
foreach($learnerFiles as $key => $learnerFile){
if(count($learnerFile) > 0){
$path = $learnerFile->storeAs('public/uploads/learners', request('idNumber').'_'.$key.'.'.$learnerFile->extension());
$search = 'public/' ;
$trimmed = str_replace($search, '', $path) ;
//dd($learnerFiles);
$file = FileUpload::create([
'user_id' => $learner->id,
'file_name' => $key,
'path' => $trimmed
]);
}
else{
}
$file_object = new \stdClass();
$file_object->name = $key;
$file_object->size = round(Storage::size($path) / 1024, 2);
$file_object->fileID = $learner->id;
$files[] = $file_object;
}
return response()->json(array('files' => $photos), 200);
}
I'm using the following method to upload images using Ajax call and Laravel back-end.
var uploader = $('#image-uploader[type="file"]');
var data = new FormData();
$.each(uploader.files, function() {
data.append('image[]', this);
});
data.append('_token', $('[name="csrf-token"]').attr('content'));
var url = '/upload'; //Or any target path with post method
$.ajax({
url: url,
method: 'POST',
data: data,
processData: false,
contentType: false,
success: function(data) {
alert('succeed');
}
});
Consider you can access to image files in server-side using $_POST['image] array.
Hope this helps you.

Uploading Image using Ajax, jQuery and Codeigniter

I'm trying to add a feature where I can attach an image in the form using Codeigniter, Ajax, jQuery. The form submits and when I check the database for the image file, it seems that it doesnt have anything on it. Usually they use FormData but, I already started using this method. I was wondering if there is anyway I could do it this way.
Here are my codes.
jQuery
$('#addForm').submit(function(event){
var emp_id = $("#agentNames").val();
var campaign = $("#addCampaign").val();
var k_type = $("#addKudosType").val();
var c_name = $("#addCustomerName").val();
var p_number = $("#addPhoneNumber").val();
var e_add = $("#addEmailAdd").val();
var comment = $("#addCustomerComment").val();
var supervisor = $("#addSupervisor").val();
var file = $("#addFile").val();
var p_reward = $("#addPrefReward").val();
var pfrd = $("#addProofreading").val();
var k_card = $("#addKudosCard").val();
var r_status = $("#addRewardStatus").val();
dataString = "emp_id="+emp_id+"&campaign="+campaign+"&k_type="+k_type+"&c_name="+c_name+"&p_number="+p_number+"&e_add="+e_add+"&comment="+comment+"&supervisor="+supervisor+"&file="+file+"&p_reward="+p_reward+"&pfrd="+pfrd+"&k_card="+k_card+"&r_status="+r_status;
$.ajax({
type: "POST",
url: "<?php echo base_url(); ?>index.php/Kudos/addKudos/",
data: dataString,
cache: false,
success: function(html)
{
alert("Succesfully Added!");
location.reload();
}
});
event.preventDefault();
});
Controller
public function addKudos()
{
$emp_id= $this->input->post('emp_id');
$campaign= $this->input->post('campaign');
$k_type= $this->input->post('k_type');
$c_name= $this->input->post('c_name');
$p_number= $this->input->post('p_number');
$e_add= $this->input->post('e_add');
$comment= $this->input->post('comment');
$supervisor= $this->input->post('supervisor');
$file= $this->input->post('file');
$config['upload_path'] = "uploads/images/";
$config['allowed_types'] = "jpg|png";
$config['file_name'] = $_FILES['addFile']['name'];
$this->load->library('upload', $config);
$this->load->initialize($config);
if($this->upload->do_upload('addFile')){
$uploadData = $this->upload->data();
$picture = $uploadData['file_name'];
} else {
$picture = '';
}
$p_reward= $this->input->post('p_reward');
$pfrd= $this->input->post('pfrd');
$k_card= $this->input->post('k_card');
$r_status= $this->input->post('r_status');
$this->KudosModel->add_kudos($emp_id,$campaign,$k_type,$c_name,$p_number,$e_add,$comment,$supervisor,$picture,$p_reward,$pfrd,$k_card,$r_status);
}
Model
function add_kudos($emp_id,$campaign,$k_type,$c_name,$p_number,$e_add,$comment,$supervisor,$picture,$p_reward,$pfrd,$k_card,$r_status)
{
$emp_id1 =mysqli_real_escape_string($this->db->conn_id,trim($emp_id));
$campaign1 =mysqli_real_escape_string($this->db->conn_id,trim($campaign));
$k_type1 =mysqli_real_escape_string($this->db->conn_id,trim($k_type));
$c_name1 =mysqli_real_escape_string($this->db->conn_id,trim($c_name));
$p_number1 =mysqli_real_escape_string($this->db->conn_id,trim($p_number));
$e_add1 =mysqli_real_escape_string($this->db->conn_id,trim($e_add));
$comment1 =mysqli_real_escape_string($this->db->conn_id,trim($comment));
$supervisor1 =mysqli_real_escape_string($this->db->conn_id,trim($supervisor));
$file1 =mysqli_real_escape_string($this->db->conn_id,trim($picture));
$p_reward1 =mysqli_real_escape_string($this->db->conn_id,trim($p_reward));
$pfrd1 =mysqli_real_escape_string($this->db->conn_id,trim($pfrd));
$k_card1 =mysqli_real_escape_string($this->db->conn_id,trim($k_card));
$r_status1 =mysqli_real_escape_string($this->db->conn_id,trim($r_status));
$query = $this->db->query("insert into tbl_kudos(emp_id,acc_id,kudos_type,client_name,phone_number,client_email,comment,uid,file,reward_type,proofreading,kudos_card,reward_status,is_given) values('$emp_id1','$campaign1','$k_type1','$c_name1','$p_number1','$e_add1','$comment1','$supervisor1','$file1','$p_reward1','$pfrd1','$k_card1','$r_status1',now())");
}
Thanks.
The database doesn't have any file because you aren't submitting any. You didn't show any HTML but you must have something like:
<input type="file" id="addfile">
But in jquery, you are retrieving the file using:
var file = $("#addFile").val();
Which will return the file name or an empty string because you won't get file contents (or image) using val(). You need to look for another method to upload form data with file contents using AJAX. Check this out: How can I upload files asynchronously?

Ajax send no data [ Codeigniter ]

i have a problem in ajax, indeed, i try to send value with ajax to my upload function before submit.
But when i check the $_POST array in my php code, there is only the value of the form, and not from the ajax, and I don't know why.
Here is my code :
HTML:
<button id="btn_saisie" class="btn btn-app saver adddocu" ><i class="fa fa-save whiter"></i></button>
<form action="/uploader/adddocu" id="form_saisie" class="form_saisie" method="POST" enctype="multipart/form-data">
<input type="file" name="document" class="val_peage form-control form_num" id="document" data-rest="document" placeholder="Document">
<input type="text" name="description" class="val_parking form-control form_num" id="description" data-rest="description" placeholder="Description">
JS :
$( ".adddocu" ).click(function() {
if ($('#document').val() != "" && $('#description').val() != ""){
api_sendvalue_adddoc();
}
if ($('#document').val() == "")
alert('test');
else if ($('#description').val() == "")
alert('test2'); });
function api_sendvalue_adddoc(){
user = JSON.parse(sessionStorage.getItem('user'));
pays = localStorage.getItem("pays");
magasin = localStorage.getItem("magasin");
$.ajax({
type: 'POST',
url: '/uploader/adddocu',
data: {pays:pays, magasin:magasin},
success: function(data){
alert(data);
$("#form_saisie").submit();
console.log(data);
},
error: function(xhr){
alert(xhr.responseText);
console.log(xhr.responseText);
}
}); }
PHP:
public function adddocu(){
$path = './asset/upload/pdf/';
$path2 = '/asset/upload/pdf/';
$config['upload_path'] = $path;
$config['encrypt_name'] = false;
$config['file_ext_tolower'] = true;
$config['allowed_types'] = 'pdf';
// die(var_dump($_POST));
$this->load->library('upload', $config);
foreach($_FILES as $id => $name)
{
$this->upload->do_upload('document');
$upload_data = $this->upload->data();
$url = $path2 . $upload_data['file_name'];
$data = array('nom' => $upload_data['raw_name'], 'description' => $_POST['description'], 'url' => $url, 'user_id' => '17');
$this->db->insert('pdf', $data);
}
redirect("/login/docu");
}
So, when I var_dump the $_POST array, I only have the value of "description", and not of "pays" and "magasin".
Can you help me please?
Thanks for your time.
Seems like you are accessing localstorage value , you are posting it somewhere and then submiting the form.
More you are submiting the form which dont have this pays & magasin so i have a trick using which you can achieve it.
Create two hidden inputs inside your HTML form like
<input type="hidden" name="pays" id="pays">
<input type="hidden" name="magasin" id="magasin">
Now before ajax call give them values after getting it from local storage, like this.
user = JSON.parse(sessionStorage.getItem('user'));
pays = localStorage.getItem("pays");
magasin = localStorage.getItem("magasin");
$("#pays").val(pays);
$("#magasin").val(magasin);
$.ajax({ .... });
Continue your code and enjoy.
Hopefully it will work for you.
The issue is because you are not preventing the form from being submit normally, so the AJAX request is cancelled. Instead of using the click event of the button, hook to the submit event of the form and call preventDefault(). Try this:
$('#form_saisie').submit(function(e) {
e.preventDefault();
if ($('#document').val() != "" && $('#description').val() != ""){
api_sendvalue_adddoc();
}
if ($('#document').val() == "")
alert('test');
else if ($('#description').val() == "")
alert('test2');
});
EDIT:
Here is a working example of a ajax post to codeigniter:
View
<script>
$( document ).ready(function () {
// set an on click on the button
$("#button").click(function () {
$.ajax({
type: 'POST',
url: "[page]",
data: {pays: "asd", magasin: "dsa"},
success: function(data){
alert(data);
$("#text").html(data);
console.log(data);
},
error: function(xhr){
alert(xhr.responseText);
console.log(xhr.responseText);
}
});
});
});
</script>
Controller
<?php
// main ajax back end
class Time extends CI_Controller {
// just returns time
public function index()
{
var_dump($_POST);
echo time();
}
}
?>
Output
array(2) {
["pays"]=>
string(3) "asd"
["magasin"]=>
string(3) "dsa"
}
1473087963
Working example here
So you should check the request that you're making from AJAX, on dev console. There you should get the response with the var_dump($_POST).
to debug try to make your controller return only the $_POST data, comment the rest. and same thing on javascript side, test only the ajax post and data received.

Form submission in JSON format via AJAX

I'm trying to submit a HTML form to a server and it fails. I have several issues here.
I'm making a CORS request. It is a request by the client and I can't do anything about it.
( My app resides in localhost:3000/login.html & My server resides in http://localhost:3001/Login/auth )
The server in localhost:3001 accepts JSON requests and give back a JSON Responce.
(I have disabled the web-security in the browser to allow the CORS Requests)
So, I have created the HTML Form and tried to submit it with AJAX. Here you can see the code.
$(function(){
$("#loginform").submit(function(e){
e.preventDefault();
// var data = $("#loginform").serialize();
var data = {"username" : $("#username").val(),"password" : $("#password").val()};
alert(JSON.stringify(data));
$.ajax({
type: "POST",
url: "http://localhost:3001/Login/auth",
data: data,
contentType: "application/json",
crossDomain : true,
dataType: "json",
success: function(data) {
alert("success" + JSON.stringify(data));
},
error:function(data){
alert('error : '+JSON.stringify(data));
}
});
});
});
I have created the mock server in PHP (codeigniter). Here is the code for that.
public function auth(){
$input_data = json_decode(trim(file_get_contents('php://input')), true);
$debug_export = var_export(file_get_contents('php://input'), true);
file_put_contents('new.txt',$debug_export . PHP_EOL, FILE_APPEND);
$user = 'admin';
$pass = 'admin';
$token = '1';
foreach($input_data as $key=>$value) {
if ($key === 'username') {
$this_user = $value;
}
if ($key === 'password') {
$this_password = $value;
}
}
if(isset($this_user) && isset($this_password)){
if($this_password === $pass && $this_user === $user){
echo json_encode(array('token'=>$token));
}else{
echo json_encode(array('token'=>'null'));
}
}
return 0;
}
When I submit a form, I get a response like this.
....
<p>Severity: Warning</p>
<p>Message: Invalid argument supplied for foreach()</p>
<p>Filename: controllers/Login.php</p>
<p>Line Number: 77</p>
....
My 'new.txt' file has the log details as following.
''
'username=admin&password=admin'
I have really no idea what I'm doing wrong here. Can anyone help me out here?

Categories