Что вызывает mod_security 406 Недопустимо при размещении данных? - PullRequest
0 голосов
/ 26 июня 2019

У меня есть статья на моем сайте с содержанием (уценки):

# PHP Proper Class Name
Class names in PHP are case insensitve. If you have a class declaration like:
```php
class MyWeirdClass {}
```
you can instantiate it with `new myWEIRDclaSS()` or any other variation on the case. In some instances, you may want to know, what is the correct, case-sensitive class name. 

### Example Use case
For example, in one of my libraries under construction [API Doccer](https://github.com/ReedOverflow/PHP-API-Doccer), I can view documentation for a class at url `/doc/class/My-Namespace-Clazzy/` and if you enter the wrong case, like `/doc/class/my-NAMESPACE-CLAzzy`, it should automatically redirect to the proper-cased class. To do this, I use the reflection method below as it is FAR more performant than the `get_delcared_classes` method


## Reflection - get proper case
Credit goes to [l00k on StackOverflow](https://stackoverflow.com/a/35222911/802469)
```php
$className = 'My\caseINAccuRATE\CLassNamE';
$reflection = new ReflectionClass($className);
echo $reflection->getName();
```
results in `My\CaseInaccurate\ClassName`;  
Running the benchmark (see below) on localhost on my laptop, getting the proper case class name of 500 classes took about 0.015 seconds, as opposed to ~0.050 seconds using the `get_declared_classes` method below.

## get_declared_classes - get proper case
This was my idea, as I hadn't even considered using reflection, until I saw [l00k's answer on StackOverflow](https://stackoverflow.com/a/35222911/802469). Guessing it would be less efficient, I wrote the code and figured it out anyway, because it's fun!
```php
$wrongCaseName = 'Some\classy\THIng';
class_exists($wrongCaseName); //so it gets autoloaded if not already done
$classes = get_declared_classes();
$map = array_combine(array_map('strtolower',$classes),$classes);
$proper = $map[strtolower($wrongCaseName)];
```
results in `$proper = 'Some\Classy\Thing'`;  
Running the bencmark (see below) on localhost on my laptop, getting the proper case class name of 500 classes took about 0.050 seconds, as opposed to ~0.015 seconds with reflection (above).


## Benchmark:
I used the following code to do the benchmark, removing the `classes` directory between each run of the benchmark. It's not perfect. At all. But it gets the job done well enough, I think:
```php
<?php

$times = [];
$times['begin'] = microtime(TRUE);

spl_autoload_register(function($className){
    if (file_exists($name=__DIR__.'/classes/'.strtolower($className).'.php')){
        include($name);
    }
});
if (is_dir(__DIR__.'/classes'))return;

mkdir(__DIR__.'/classes');

function generateRandomString($length = 10) {
    $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

$times['start_file_write'] = microtime(TRUE);
$names = [];
for ($i=0;$i<500;$i++){
    $className = generateRandomString(10);
    $file = __DIR__.'/classes/'.strtolower($className).'.php';
    if (file_exists($file)){
        $i = $i-1;
        continue;
    }
    $code = "<?php \n\n".'class '.$className.' {}'."\n\n ?>";
    file_put_contents($file,$code);
    $names[] = strtoupper($className);
}

$times['begin_get_declared_classes_benchmark'] = microtime(TRUE);
$propers = [];

// foreach($names as $index => $name){
//     $wrongCaseName = strtoupper($name);
//     class_exists($wrongCaseName); //so it gets autoloaded if not already done
//     $classes = get_declared_classes();
//     $map = array_combine(array_map('strtolower',$classes),$classes);
//     $proper = $map[strtolower($wrongCaseName)];
//     if ($index%20===0){
//         $times['intermediate_bench_'.$index] = microtime(TRUE);
//     }
//     $propers[] = $proper;
// }

// the above commented lines are the get_declared_classes() method. 
// the foreach below is for reflection.

foreach ($names as $index => $name){
    $className = strtoupper($name);
    $reflection = new ReflectionClass($className);
    if ($index%20===0){
        $times['intermediate_bench_'.$index] = microtime(TRUE);
    }
    $propers[] = $reflection->getName(); 
}

$times['end_get_declared_classes_benchmark'] = microtime(TRUE);

$start = $times['begin'];
$bench = $times['begin_get_declared_classes_benchmark'];

$lastTime = 0;
foreach($times as $key => $time){
    echo "\nTime since begin:".($time-$start);
    echo "\nTime since last: ".($time-$lastTime)."   key was {$key}";
    echo "\nTime since bench start: ".($time - $bench);
    $lastTime = $time;
}

print_r($times);
print_r($propers);
exit;
```

### Results
```
// get_declared_classes method
//Time since bench start: 0.052499055862427 is total time for processing get_declared_classes w/ $i=500
//Time since bench start: 0.047168016433716
// last bench time Time since begin:0.062150955200195
// 100 intermediate bench: Time since bench start: 0.0063230991363525
// 200                   : Time since bench start: 0.015070915222168
// 300 intermediate bench: Time since bench start: 0.02455997467041
// 400 intermediate bench: Time since bench start: 0.033944129943848
// 480                   : Time since bench start: 0.044310092926025


//reflection method:
//Time since bench start: 0.01493501663208
//Time since bench start: 0.017416954040527
// 100 intermediate:  Time since bench start: 0.0035450458526611
// 200 intermediate:  Time since bench start: 0.0066778659820557
// 300 intermediate:  Time since bench start: 0.010055065155029
// 400 intermediate:  Time since bench start: 0.014182090759277
// 480 intermediate:  Time since bench start: 0.01679801940918
```

#### Results' notes
- "Time since bench start" is the entire time it took to run all the iterations. I share this twice above.  
- "100 Intermediate" (200, 300, etc) are actually the results at 120, 220, etc... I messed up in copy+pasting results & didn't want to do it again. Yes. I'm lazy :)
- The results would of course vary between runs of the code, but it's pretty clear that the reflection option is significantly faster.
- All was run on a localhost server on an Acer laptop.
- PHP Version 7.2.19-0ubuntu0.19.04.1 (from `php info()`)

Как показано выше, я могу отправить статью, и все работает как положено - сохраняет в БД и все. В самой последней строке, если я изменю php info() на phpinfo() (удаляя пробел), я получаю эту ошибку:

Недопустимо!

Не удалось найти соответствующее представление запрошенного ресурса на этом сервере. Эта ошибка была сгенерирована Mod_Security.


Когда я пытаюсь отправить с phpinfo() (без пробела), мой PHP не выполняется вообще, и я получаю только эту ошибку. Вкладка сети в Firefox показывает «406 Недопустимо» для кода состояния. Ничего не записывается в мой журнал ошибок в $_SERVER['DOCUMENT_ROOT'].'/error_log', куда все равно записываются все ошибки PHP. В моей домашней папке есть папка logs, но она остается пустой. Нет записей в /etc/ или /etc/my_website_name.com.

Что может быть причиной этой проблемы? Есть ли в PHP.ini что-то, что я мог бы изменить? Может ли .htaccess повлиять на это вообще?

По крайней мере, как мне устранить эту проблему?

Устранение неполадок

  • Я могу представить статью, которая содержит только - PHP Version 7.2.19-0ubuntu0.19.04.1 (из `phpinfo ()`) в теле.
  • Если я удалю phpinfo () и добавлю больше содержимого в тело сообщения (больше отправленных данных), это сработает
  • вставка пробела, например php info(), заставляет его работать, и именно так пост существует в настоящее время.
  • Я не знаю, что еще делать

Я сейчас использую Simple MDE , но это происходило во многих других случаях, прежде чем я начал использовать Simple MDE. Это было только с относительно большими сообщениями, которые также содержат код.

Я использую общий хостинг с HostGator, используя HTTPS: // и PHP 7.2.19

1 Ответ

0 голосов
/ 27 июня 2019

Я связался с HostGator. Они добавили что-то в белый список, но не дали мне интимных подробностей. Это исправило проблему.

Первый агент занял некоторое время, не смог решить проблему и преждевременно отключился.

Второй агент был достаточно быстр и решил проблему, заявив, что у меня не должно быть этой проблемы с подобными типами запросов POST, которые содержат код.

...