Http 406错误的JavaScript AJAX后。 接受标题集以接受所有

当我尝试创build一个我无法弄清楚的AJAX请求时,服务器出现错误。 这是一个HTTP 406错误。 据我所知,这意味着服务器不能以Accept头指示的格式发送响应。 但从我所看到的,我Accept */* (通配符)。

你可以在这个图像中看到我的HTTP请求的细节。 http://i.stack.imgur.com/fTfBf.png 我的HTTP请求

这是我的页面的PHP代码:

 <body> <div id="container"> (snip - menu and header) <div id="notify-saved"> <div class="width"> <span>Saved!</span> </div> </div> <div id="confirm-delete"> <div id="confirm-delete-content" class="width"> </div> </div> <div id="nav-separator"> </div> <div id="body" class="width"> <section id="content"> <br /> <form method="POST" id='template-form' action="post-actions.php" > <input type='hidden' name='id' value="15" /> <input type='text' name='name' value="default-scripting" placeholder='Name' /> <br /> <br /> <input type='checkbox' name='major' id="majorminor" /> <textarea name='value' style="width:100%;">&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js&quot;&gt;&lt;/script&gt;</textarea> <br /> <br /> <button name='action' value="Update Template" id='update-button' >Update</button> </form> </section> </div> </div> </body> 

使用Javascript:

 // Attach a submit handler to the form $( "#update-button" ).click( function( event ) { // Stop form from submitting normally event.preventDefault(); // Get some values from elements on the page: var $form = $('#template-form'); var template_id = $form.find( "input[name='id']" ).val(); var template_name = $form.find( "input[name='name']" ).val(); var template_value = $form.find( "textarea[name='value']" ).val(); var template_is_major = 'minor'; if( $( "input[name='major']" ).prop('checked') ){ template_is_major = "major"; } var url = $form.attr( "action" ); // Send the data using post var posting = $.post( url, { id: template_id, name: template_name, value: template_value, major: template_is_major, action: "Update Template" } ); // Show that the request has completed successfully posting.done(function( data ) { $('#notify-saved').slideDown(200).delay(500).slideUp(500); }); }); 

另一个相关的事实是,这个错误只发生在一些时间 ,例如,如果提交的模板值

&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js&quot;&gt;&lt;/script&gt;

那么会出现错误,但是如果省略前面的<那么就没有错误:

script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js&quot;&gt;&lt;/script&gt;

接收PHP看起来像这样(post-actions.php):

 <?php // ugly-@$$ anti-magicquotes hackk if (get_magic_quotes_gpc()) { $process = array(&$_GET, &$_REQUEST, &$_COOKIE, &$_REQUEST); while (list($key, $val) = each($process)) { foreach ($val as $k => $v) { unset($process[$key][$k]); if (is_array($v)) { $process[$key][stripslashes($k)] = $v; $process[] = &$process[$key][stripslashes($k)]; } else { $process[$key][stripslashes($k)] = stripslashes($v); } } } unset($process); } require_once('database.php'); $dbo = new Database(); if( isset( $_REQUEST['action'] ) ){ switch( $_REQUEST['action'] ){ (snip - many irrelevant cases) case "Update Template": $dbo->UpdateTemplate( $_REQUEST[DB_TEMPLATES_ID], $_REQUEST[DB_TEMPLATES_NAME], $_REQUEST[DB_TEMPLATES_VALUE], $_REQUEST[DB_TEMPLATES_MAJOR] ); break; } } 

和数据库类:

 <?php require_once ("config.php"); require_once ("database-contract.php"); class Database { protected $db; protected $pdo; function __construct(){ $this->db = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if($this->db->connect_errno > 0){ die('Unable to connect to database [' . $this->db->connect_error . ']'); } $this->pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8', DB_USER, DB_PASS ); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); } function __destruct(){ $this->db->close(); $this->pdo = null; } $query = 'INSERT INTO `'.DB_TEMPLATES.'` ( `'.DB_TEMPLATES_NAME.'` ) VALUES ( :name )'; $stmt = $this->pdo->prepare($query); $stmt->bindValue(":name", $name, PDO::PARAM_INT); $stmt->execute(); } function updateTemplate($id, $name, $value, $major ){ if( $major == "major" ){ $major = 1; } else{ $major = 0; } $query = 'UPDATE `'.DB_TEMPLATES.'` SET `'.DB_TEMPLATES_NAME.'`=:name, `'.DB_TEMPLATES_VALUE.'`=:value, `'.DB_TEMPLATES_MAJOR.'`=:major WHERE `'.DB_TEMPLATES_ID.'`=:id'; $stmt = $this->pdo->prepare($query); $stmt->bindValue(":name", $name, PDO::PARAM_STR ); $stmt->bindValue(":value", $value, PDO::PARAM_STR ); $stmt->bindValue(":major", $major, PDO::PARAM_INT ); $stmt->bindValue(":id", $id, PDO::PARAM_INT ); $stmt->execute(); } $query = ' DELETE FROM '.DB_TEMPLATES.' WHERE '.DB_TEMPLATES_ID.'=:id'; $stmt = $this->pdo->prepare($query); $stmt->bindValue(":id", $id, PDO::PARAM_INT); $stmt->execute(); } (snip - irrelevant functions) } 

在某些方面,我曾认为这可能是一个字符编码问题,但我不再想这个了。 也许这是一些奇怪的字符转义问题? SO上的其他post似乎表明这是apache的mod_security的configuration问题/(function?)。 我尝试禁用一些不同的东西,但是这不起作用,现在我只是在想什么。

谢谢你的帮助。

事实上,你有一个406,而且这是基于脚本之前的存在,直接导致一个安全过滤器的行动。 所以mod_securityapache配置文件比js / php代码更有用,以查找根阻塞规则。

这看起来像一个反xss过滤器。 也许是一个非常简单的:

 SecFilter "<( |\n)*script" 

如果你可以改变这个mod_security配置,那么做…或者不要。 这关乎安全 。 也许你应该保留这些过滤器,以保护你的应用程序真正不受欢迎的脚本注入。 也许真正的解决方案是你的官方应用程序表单应该管理与服务器的通信,而不会对mod_security产生怀疑 。 而在这里你的POST太像一个跨站脚本攻击的工具,只读流,并检测到内部的JavaScript调用。

例如你可以在POST的值上添加一个base64编码(来自js),并在PHP端解码结果,一些base64编码js库在github上可用。

事实上,您可以对您的数据应用任何转换,使得这些数据无HTML(和无Javascript),至少对于HTTP过滤工具。 Base64通常是解决方案,但是检查使用的库如何管理utf-8字符的编码, 真正的 base64只是ascii。

我有同样的问题,我会告诉你如何调试和修复它。

在我的情况下,我可以通过写一些非常具体的内联css指令(在HTML标签中的样式属性内)来重现禁止的响应。

我分析了apache的错误日志文件(在我的情况下是在/usr/local/apache/logs/error_log并且只能通过我得到的响应代码进行过滤)。

 grep 406 error_log 

解释说这是ModSecurity的规则,这就是它说的:

 [Mon Jan 02 13:21:10.394376 2017] [:error] [pid 12827:tid 139784907347712] [client 255.255.255.255] ModSecurity: Access denied with code 406 (phase 2). Pattern match "(?: (?:height|width) ?(?:=|\\\\:) ?[0-9] ?px|overflow ?: ?(?:auto|hidden)|style ?= ?\\"? ?display ?: ?none ?)" at ARGS:body. [file "/usr/local/apache/conf/modsec2/30_asl_antispam.conf"] [line "174"] [id "300076"] [rev "29"] [msg "Atomicorp.com WAF AntiSpam Rules: Hidden Text Detected"] [data " height:8px"] [severity "CRITICAL"] [hostname "my.website.mx"] [uri "/index.php"] [unique_id "WGqoJqwQ25oAADIbgkcAAAGP"] 

我调查了如何禁用这个,事实证明你可以添加这个conf文件中:

 <Location "/"> SecRuleRemoveById 300076 </Location> 

问题解决了我。 请注意,如果您想禁用多个规则,则可以用空格分隔id。