0

I was looking for some examples of automatic locale detection as I needed to do the same in my PHP app. Most of the answers I found were similar, only differing in the parsing methods. An example 'pseudo-code':

$supportedLocales = ['en, 'bg', 'pl'];
$locale = 'en'; // default fallback locale

$userLangs = preg_split('/,|;/', $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
foreach($userLangs as $lang)
{
    if(in_array($lang, $supportedLocales))
    {
        $locale = $lang;
        break;
    }        
}

But, after a while of experimenting with the Accept-Language header in my browser to see if everything works correctly, I noticed that I could actually make the header very long, causing the server to do a lot of looping because the code above loops through entire $supportedLocales array for every key in $userLangs array. I could actually easily send an forged Accept-Language header with over 25000 fake languages that were not supported by the app, therefore generating over 100000 iterations in total. Not including the iterations used to compare the string values.

Even here on Stack websites, I could send over few thousand languages before getting a '400 Bad Request' error. If this can be considered an attack, how can one protect his website from such forged headers? I know I could just limit the amount of languages, but still, what amount can be considered normal?

2

Looks like your situation is quite unique, as, according to this answer, most web-servers won't let you to send 25000 fake languages, but only rather something around 2500.

Given your code is not optimal and having it changed this way

$supportedLocales = array_fill_keys($supportedLocales, 1);
foreach($userLangs as $lang)
{
    if(isset($supportedLocales[$lang]))
    {
        $locale = $lang;
        break;
    }        
}

will keep it at humble 2500 iterations.

  • The main change here being the conversion of $supportedLocales into a key/value map instead of an array. Key/value lookups usually take constant time where array scanning takes linear time. – Mr. Llama Oct 24 '18 at 13:43
  • The size of the header was probably so "unlimited" because of using Laravel's built-in php server on a local machine. Great optimisation, I haven't even thought about checking it this way! I already implemented it into my code. Thanks! – hazelnutek Oct 24 '18 at 13:56
4

If this can be considered an attack, how can one protect his website from such forged headers?

Forging requests that take a long time to process on the server is a common technique for a Denial-Of-Service (DOS) attack. This is why you always sanitise your input. Not just POST and GET values, but also the headers if you use them.

For the HTTP_ACCEPT_LANGUAGE header, I'd say anything more than about 30 characters or 10 languages would not be a sane value for most applications. So sanitse the header. You can either limit the number of results in preg_split or limit the length of the HTTP_ACCEPT_LANGUAGE header

$userLangs = preg_split('/,|;/', $_SERVER["HTTP_ACCEPT_LANGUAGE"], 10);

or

$userLangs = preg_split('/,|;/', substr($_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 30))

edit: The answer by user 'Your common sense' was accepted, but please realise that optimising a loop is not the right way to protect yourself from any forged input. You're just reducing the impact, instead of solving the source of the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.