0

In my application we have many users who are all related to different call centers. In the URL there is a backwards hack where they can enter call_center=number after the ? and it will direct them to a different call centers data page. What is the best way to cut off that possibility? I want to redirect them to the current page they are on if they try to edit the URL. What approach should I use to fix this loophole?

Dash.js

checkLockSave();
        var month = $('div.top').attr('data-month');
        var sellertype = $('div.top').attr('data-sellertype'); 
        var call_center = $('div.top').attr('data-callcenter');
        var full_call_center = $('#cancel-full-chart').attr('data-callcenter');
        var forecast_url = call_center ==='' ? "/forecast/data" : "/forecast/data?call_center="+call_center;
        var cancel_url = call_center === '' ? "/contracts/cancelpercent" : "/contracts/cancelpercent?call_center="+call_center;
        var cancel_full_url = full_call_center === '' ? "/contracts/cancelpercentfull" : "/contracts/cancelpercentfull?call_center="+full_call_center;
        var contract_status_url = call_center === '' ? "/contracts/contractstatus" : "/contracts/contractstatus?call_center="+call_center;


        if (sellertype && sellertype.length >= 0) {
            if (contract_status_url.indexOf('?') >= 0) {
                contract_status_url = contract_status_url + "&sellertype=" + sellertype;
            }
            else {
                contract_status_url = sellertype === '' ? "/contracts/contractstatus" : "/contracts/contractstatus?sellertype="+ sellertype;
            }
        }

        if (month && month.length >= 0) {
            if (contract_status_url.indexOf('?') >= 0) {
                contract_status_url = contract_status_url + "&month=" + month;
            }
            else {
                contract_status_url = month === '' ? "/contracts/contractstatus" : "/contracts/contractstatus?month="+ month;
            }
        }
        $('select.sellertype_select').on('change', function(){
            base_url = $('.service-container').data('base_url');
           // window.location.href = base_url+"/?month="+$(this).val();
            if ($(this).val() == "call_center") {
                newUrl = urlBuilder("sellertype", null);
            } else {
                newUrl = urlBuilder("sellertype", $(this).val());
            }
            window.location.href = newUrl;
        });


        $('select.company_select').on('change', function(){
             base_url = $('.service-container').data('base_url');
            // window.location.href = base_url+"/?call_center="+$(this).val();
            var newUrl = window.location.href;
            if ($(this).val() == "call_center_0") {
                newUrl = urlBuilder("call_center", null);
            } else {
                newUrl = urlBuilder("call_center", $(this).val());
            }
            window.location.href = newUrl;
        });


        $('select.month_select').on('change', function(){
            base_url = $('.service-container').data('base_url');
           // window.location.href = base_url+"/?month="+$(this).val();
            if ($(this).val() == "twentyfour") {
                newUrl = urlBuilder("month", null);
            } else {
                newUrl = urlBuilder("month", $(this).val());
            }
            window.location.href = newUrl;
        });
                //function url builder add or update query param with specified query param value
                function urlBuilder(param, paramValue){
                    base_url = $('.service-container').data('base_url');
                    var fullurl = window.location.href;
                    var urlStart = fullurl.split("?").length > 1 ? fullurl.split("?")[0] : null;
                    var urlQuery = fullurl.split("?").length > 1 ? fullurl.split("?")[1] : null;

                    if (urlQuery) {
                        var queryParts = urlQuery.split('&');
                        var len = queryParts.length;
                        var removeAt = -1;
                        var paramFound = false;
                        for (var i = 0; i < len; i++)
                        {
                            console.debug('query part: ', queryParts[i]);
                            skip = false;
                            if (queryParts[i].startsWith(param)) {
                                paramFound = true;
                                if (!paramValue) {
                                   removeAt = i;
                                } else {
                                    queryParts[i] = param + "=" + paramValue;
                                }
                            }
                        }

                        if (!paramFound) {
                            queryParts.push(param + "=" + paramValue);
                        }

                        if (removeAt >= 0) {
                            queryParts.splice(removeAt, 1);
                        }

                        var rtnUrl = urlStart + "?" + queryParts.join("&");
                        console.log('rtnUrl', rtnUrl);
                        return rtnUrl;
                    } else {
                        return base_url + '?' + param + '=' + paramValue;
                    }
                }

DashboardController.php

     $input = Input::all();

        $user_id = Auth::user()->id;
        $role_id = User::UserRoleData()->where('user_id', '=', $user_id)->first();
        $call_center = Auth::user()->call_center;
        $call_center = ($call_center == null ? '' : $call_center);
        $call_center = !empty($input['call_center']) ? $input['call_center'] : $call_center;
        $month = !empty($input['month']) ? $input['month'] : 'twentyfour';
        $sellertype = !empty($input['sellertype']) ? $input['sellertype'] : 'seller';
        $companies = Company::DistinctCompanies()->orderby('name')->get();
        $date = date('Y-m-d', strtotime('now -24 months'));
if($sellertype == 'dealership')
        {
        $pending->where("type", "=", 'dealership');
        $active->where("type", "=", 'dealership');
        $cancelled_contracts->where("type", "=", 'dealership');
        $late_contracts->where("type", "=", 'dealership');
        $back_out_contracts->where("type", "=", 'dealership');
        $pending_late->where("type", "=", 'dealership');
        $pending_cancellation->where("type", "=", 'dealership');
        }

        if($month == 'twentyfour' || $month != 'all')
        {
            $pending->where("sold_date", ">=", $date);
            $active->where("sold_date", ">=", $date);
            $cancelled_contracts->where("sold_date", ">=", $date);
            $late_contracts->where("sold_date", ">=", $date);
            $back_out_contracts->where("sold_date", ">=", $date);
            $pending_late->where("sold_date", ">=", $date);
            $pending_cancellation->where("sold_date", ">=", $date);
        }
 if($role_id['id'] == '5' || $role_id['id'] == '6' || $role_id['id'] == '7' || $role_id['id'] == '8' || $role_id['id'] == '9' || $role_id['id'] == '10')
        {
            {
                $pending->where('contracts.call_center', '=', $call_center);
                $active->where('contracts.call_center', '=', $call_center);
                $cancelled_contracts->where('contracts.call_center', '=', $call_center);
                $late_contracts->where('contracts.call_center', '=', $call_center);
                $back_out_contracts->where('contracts.call_center', '=', $call_center);
                $pending_late->where('contracts.call_center', '=', $call_center);
                $pending_cancellation->where('contracts.call_center', '=', $call_center);
            }
        }
        else
        {
            if($role_id['id'] == '2' && !empty($input['call_center']))
            {
                $call_center = $input['call_center'];
                {
                    $pending->where('contracts.call_center', '=', $call_center);
                    $active->where('contracts.call_center', '=', $call_center);
                    $cancelled_contracts->where('contracts.call_center', '=', $call_center);
                    $late_contracts->where('contracts.call_center', '=', $call_center);
                    $back_out_contracts->where('contracts.call_center', '=', $call_center);
                    $pending_late->where('contracts.call_center', '=', $call_center);
                    $pending_cancellation->where('contracts.call_center', '=', $call_center);
                }
            }
        }


        $pending = $pending->get()->first();
        $active = $active->get()->first();
        $cancelled_contracts = $cancelled_contracts->get()->first();
        $late_contracts = $late_contracts->get()->first();
        $back_out_contracts = $back_out_contracts->get()->first();
        $pending_late = $pending_late->get()->first();
        $pending_cancellation = $pending_cancellation->get()->first();


        View::share('active_nav', 'Dashboard');
        return view('dash.dash')
            ->with('pending_contracts', number_format($pending->total))
            ->with('month', $month)
            ->with('sellertype', $sellertype)
            ->with('active', number_format($active->total))
            ->with('companies', $companies)
            ->with('call_center', $call_center)
            ->with('year', date("Y"))
            ->with('cancelled_contracts', number_format($cancelled_contracts->total))
            ->with('late_contracts', number_format($late_contracts->total))
            ->with('back_out_contracts', number_format($back_out_contracts->total))
            ->with('pending_late', number_format($pending_late->total))
            ->with('pending_cancellation', number_format($pending_cancellation->total));
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
LDrizzy
  • 53
  • 5
  • "_users who are all related to different call centers_" so how do you control what data the user has access too? – Scuzzy Sep 05 '19 at 21:28
  • 1
    Hi, perhaps use a middleware to verify the user has access to that page https://stackoverflow.com/questions/29733709/laravel-5-middleware-owner – IronMan Sep 05 '19 at 21:30
  • Redirecting is trivial, what you need is a way to detect that it's the wrong call center. The only way to do that is to either use their IP address somehow, if applicable, or have them login and save their call center in the user database table along with their login data. –  Sep 05 '19 at 21:40

2 Answers2

1

There's 3 ways to fix this, that I can think of off the top of my head.

First

Give your call centers a random or semi-random value that isn't a sequential number. That way, it'll be harder for people to go from call center 2 to 3. If they have the time to figure out that there is a call center 3RG5, they have more time on their hands than their wait time, unless they build a script to start trying different combinations. At that point, it might be easy to spot their tries and maybe add a message to the next page they load, something like a lockout message or that they've tried too many times in that minute.

Second

If you are POSTing your navigation within the site, use the body instead of the URL.

PHP to get the body of a POST:

# Get JSON as a string
$json_str = file_get_contents('php://input');

# Get as an object
$json_obj = json_decode($json_str);

https://davidwalsh.name/php-json

JavaScript for an AJAX call using the POST body:

var xhr = new XMLHttpRequest();
  xhr.open(form.method, form.action, true);
  xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');

  // send the collected data as JSON
  xhr.send(JSON.stringify(data));  // This is the body of the POST

  xhr.onloadend = function () {
    // done
};

POST data in JSON format

Users can still fake this, but it's more involved and less likely to be "just anyone" who does it.

Third

Make sure the user has access to that page, by validating the user, if they are a logged in user. If they aren't logged in, then back to the other 2 options.

Community
  • 1
  • 1
computercarguy
  • 2,173
  • 1
  • 13
  • 27
1

You mentioned that you have some users, all directed to different call centers. If you store your users somewhere and it is stored that the user is linked to which call center, then you can simply load the call center of the user on server-side and if it has a different id from the one which was given as a parameter, then redirect to the correct call center's page.

If the call center of the user is not stored, then store it.

If you do not have user authentication, then you can have a formula involving the IP address on the server-side or the localStorage on the client-side.

If a single call center is allowed per user, then it makes no sense to have the call center's id as a URL parameter. It does more harm than good.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175