处理来自多个XHR实例的多个PHP脚本调用

时间:2014-01-08 20:19:00

标签: php jquery apache validation jquery-file-upload

之前的样式问题:http://pastebin.com/cr432nLQ

我决定重新解释有关此问题的整个问题,并尽可能彻底。您可以在上面的粘贴中阅读之前提出的问题。

首先,感谢大家的回应,但不幸的是,他们让我无处可去。我仍然需要帮助解决我的问题。

所以,我正在使用一个名为 blueimp jQuery File Upload 的jQuery插件,它非常好地处理异步文件上传,它包含很多选项。该插件对我的目的来说足够好,但是我需要执行此服务器端检查以便从滥用用户中保存自己。 我说的是限制一次发生的上传次数

默认情况下,插件的工作方式如下:用户选择多个文件(或将它们放到放置区),每个文件创建一个单独的XHR实例,然后调用我的upload.php脚本,该脚本负责存储文件。 PHP上传脚本处理XHR请求,好像它只是一个图像,所以基本上,每个需要上传的图像都会单独调用upload.php脚本。

我允许上传用户的是图片。但是,我希望对免费用户的上传次数设置为 10 ,对高级用户设置 20 。这可以通过Javascript完成,是的,但我真的不相信Javascript这种验证,因此我正在寻求服务器端验证。但是,怎么样?这是完全不可能(至少对我来说)安全地完成。我需要以某种方式确保在我的上传脚本中用户未超过 10 (或20)图片上传限制。

我发现这个jQuery插件有一个名为singleFileUploads的选项:

  

默认情况下,选择的每个文件都是使用个人上传的   请求XHR类型上传。将此选项设置为false以上载文件   每个请求中的选择。

     

注意:使用一个请求上传多个文件需要multipart   选项设置为true(默认值)。

     
      
  • 类型:布尔值
  •   
  • 默认:true
  •   

这完全符合我的需要 - 发送一个完全填充的$_FILES数组,其中包含所有提交的图像上传内容。但我仍然看到这个问题。这仍然是由Javascript本身决定的。用户是否可以修改脚本的这一部分并再次将singleFileUploads设置为false?我想他们可以!如果他们不能,那么现在就回答这个问题 - 但我不知道。

下面有一个答案表明使用Apache的mod_qos来限制图片上传数量,但我不知道如何使两种不同的用户类型(免费和高级)不同。

请注意,客人也可以上传图片,并且免费用户限制应适用于他们。


那么我对这一切做了什么尝试?

我尝试使用会话创建一个解决方案,以跟踪用户上传的内容,并且对于每次新上传,会话的值将增加一个 - 一个不够安全的解决方案,如下所示,因为Cookie可以清除。除了这个尝试,我无法应用以下答案中的任何内容。


问题

嗯,问题从一开始就非常明确:如何将来宾/免费用户的并发图片上传数量限制为 10 ,将 20 限制为高级用户安全

编辑:我想值得一提的是,每个上传的图片都会在数据库中获得自己的行,上传时间和名称。也许这可能会有所帮助!

谢谢。

11 个答案:

答案 0 :(得分:3)

根据您的评论,我将您的问题理解为:“我想限制每个客户端的同步上传连接数量(例如:每个客户端最多同时上传10个内容)”

首先关闭:我没有在你的javascript插件中查看详细信息,但我认为它已经允许了。至少应该有一个限制同时请求数量的选项 [编辑]找到它:limitconcurrentuploads [/ edit]

更一般地说:PHP不是这项工作的工具。在服务器端,文件上传(即:通过http连接接收数据并将其存储在服务器驱动器上的文件中的过程)由apache(或nginx,或其他)处理之前调用PHP脚本。当您的PHP脚本启动时,文件已经上传,您无法从脚本内部中断传输。

在服务器端,您应该查看php.ini / httpd.conf选项或apache / .htaccess设置。 php.ini提供了有关File uploads (doc)的一些选项。

最好的方法是在客户端的javascript中编写此限制。

如果你真的想要证明你的服务器,你应该研究如何配置你的apache服务器来限制从同一个IP到给定URL的同时连接数(我没有虽然找到了一种快速,标准的方法。


最后一部分:我在ServerFault上发现了这些问题

Apache2: Limit simultaneous requests & throttle bandwidth per IP/client? (2011)
URL-based request rate limiting in Apache (2011)

第一个问题的接受答案指向以下apache模块:
mod_qos

我还没试过。

答案 1 :(得分:2)

您可以在生成主页(发出xhr调用的页面)时创建激活码(即随机数),并将其作为页面内的变量传递给脚本。然后,您可以修改js脚本,将激活代码作为get参数传递给处理文件上载的脚本。这样,您可以使用激活代码作为db表的键来存储和跟踪使用该代码执行的上载次数。

请注意,几乎所有内容都可以通过HTTP / TCP(而不仅仅是会话)进行操作,因此您应该问问自己,如果他/她设法利用您的协议,攻击者可以获得什么。

您没有写太多关于此问题的背景信息,但如果您关注的是资源的利用,您可以要求您的用户在您的服务中开设一个帐户并设置每小时/每日/每周最大上传数量 - 每个帐户的津贴。

希望它有所帮助。

PS。你确定你真的需要限制每个上传会话的上传次数吗?由于每次上传都是通过单独调用上传脚本单独处理的,因此如果认为允许用户上传他想要的图像是安全的。

答案 2 :(得分:0)

也许我不理解你的目标,但你不能只将会话变量分配给另一个,在每个循环之前重置会话变量,并使用$ _SESSION计数和“之前”的总和进行比较计数?您是否试图阻止每个用户上传超过10次,或者只是一次阻止连续10次上传?

// Before starting your loop, assign your session variable to another.
$previously_uploaded = $_SESSION['images_session_uploaded'];

// Reset $_SESSION['images_session_uploaded'] at onset
$_SESSION['images_session_uploaded'] = 0;

// ... Do yo' thang.

// Make a comparison at the end of each loop for whatever you're checking against.
if (($_SESSION['images_session_uploaded'] + $previously_uploaded) >= MAX_FREEUSER_UPLOADS_SESSION)
{
  // User has uploaded more than ten images total, but not necessarily more than ten in this round of uploads. 
}

答案 3 :(得分:0)

首先使用会话会再次保护普通用户,但如果有人知道他们在做什么,他们可以简单地擦除他们的Cookie,只要您的服务器可以告诉他们是新用户

也许您可以通过让每个用户登录或跟踪他们的IP地址或浏览器签名来唯一地识别每个用户?这取决于你的申请。

此处的关键问题似乎是您使用了未定义的短语上传会话什么是上传会话?我假设这是一个30分钟的时间段。所以我们每隔半小时限制它们上传10次。

使用会话的解决方案:

// Start sessions before you can use them
session_start();

// "Config"
$_MAX_UPLOAD_COUNT = MAX_FREEUSER_UPLOADS_SESSION; // limit to this every session timeout
$_SESSION_TIMEOUT = 60 * 30; // 30 mins

// Current upload count (default to zero)
$total_uploads_so_far = isset($_SESSION['total_uploads']) ? $_SESSION['total_uploads'] : 0;

// --- Count their uploads this session ---
// #1 Either by looping through the uploads
$uploaded_image += 1;

// #2 Or before handling the upload by counting the amount of uploaded files
$uploaded_images = count($_FILES['images']);

// They've exhausted their uploads immediately
if($uploaded_images > $_MAX_UPLOAD_COUNT) {
  // Throw an error saying you can't select more than 10 images at once
  // Don't upload any images or modify the sessions.
}

// --- Before finishing the upload check that they haven't uploaded more than 10 items ---

// Have they uploaded before, was it in the past 30 mins?
$uploaded_recently = isset($_SESSION['last_upload']) && ($_SESSION['last_upload'] + $_SESSION_TIMEOUT) > time();

if($uploaded_recently && ($total_uploads_so_far + $uploaded_images) > $_MAX_UPLOAD_COUNT) {
  // Fail to upload, throw an error etc

} else {
  // Process your upload

  // Update the info in the users session
  $_SESSION['last_upload') = time();
  $_SESSION['total_uploads') = $uploaded_images + $total_uploads_so_far;

}

使用IP /签名的解决方案:

请执行与上述相同的操作,而不是将用户$_SESSION中的上次上传时间和总上传次数存储在数据库或文本文件中。

设置为其IP地址(来自$_SERVER['REMOTE_ADDR'])或其他内容以唯一标识它们。这将允许您按用户查找(如果使用IP地址,则为伪用户)。

例如(使用文件):

$uploads = unserialize(file_get_contents('uploads.txt');
if(!$uploads) $uploads = array();

// To update the user (using variables from the above example)
$uploads[$_SERVER['REMOTE_ADDR']] = array('total_uploads' => $uploaded_images + $total_uploads_so_far, 'last_upload' => time());

file_put_contents('uploads.txt', serialize($uploads));

答案 4 :(得分:0)

处理它的最佳方式是通过IP地址跟踪上传,这仍然不是100%准确。

像这样创建数据库表uploads

--------------------------------------------------------------------------------------
|  id (unsigned int, auto increment) |  ip (varchar) |  timestamp (current timestamp)|
--------------------------------------------------------------------------------------

然后运行以下查询以查找用户在过去半小时内上传了多少

SELECT * FROM `uploads` WHERE `ip` = '{$userIP}' AND `timestamp` > DATE_SUB(NOW(), INTERVAL 30 MINUTE)

使用您选择的PHP数据库包装计数方法计算行数,如果数字小于10,则允许图像保存。

如果上传了图片,请运行

INSERT INTO `uploads` (`ip`) VALUES ('{$userIP}')

答案 5 :(得分:0)

那么为什么不通过IP或IP /用户代理保持所有上传的运行计数(仍然可欺骗但比cookie更好)

因此,在您开始上传之前,您的ajax会使用文件名列表进行预先调用。

php代码将每个文件的记录写入db。

ip / user-agent / filename或唯一fileid / last-updated

您删除上次更新的旧记录超过30分钟或2天的超时,无论是旧的。 (如果他们的上传被网络连接杀死,这可以保护他们,等等

然后计算该ip / user-agent的行数并通过ajax返回错误。这是在实际上传之前预先检查的。

在上传代码中,检查文件是否在数据库中有一行。如果它没有绕过预检(或超过你的超时)并且你没有保存文件而是发出错误。如果它有记录,则保存上载文件并删除记录。

如果实际上传图片的时间超过了您的超时时间,您必须小心选择超时,否则他们将永远无法完成上传。如果他们的上传被迅速杀死。即他们的互联网丢失然后他们将无法超过预先检查,直到超时期限到期。

正如其他地方所述,php仅在您的网络服务器上传完成后才会运行。所以充其量只是在不同的选项卡中运行多个上传等利益。您无法保护自己免受服务类型的攻击。为此,您需要一个apache速率限制扩展或使用iptable规则。

答案 6 :(得分:0)

我有类似的问题,我想限制每个用户上传的总磁盘空间。 (我在数据库中有用户的唯一ID,你可以使用IP)。这是我做的事情

  1. 当页面加载时,我创建了一个随机字符串的哈希值(盐+时间)并将其保存在会话中。
  2. 上传文件/文件时,必须通过POST发送相同的哈希值。如果不匹配,则上传将被拒绝。成功上传文件后,随机字符串将被更改。因此,可以从同一表单上传新文件而无需重新加载。
  3. 上传后,我会为每个用户(每个ip)维护一个ini文件,其中包含上传文件数量,上传文件大小的记录。如果达到限制,我会拒绝该文件。在这种情况下我更喜欢文件,因为我不需要中央数据库来分析。此外,无需索引/搜索/维护关系以订购数据库。如果你想使用数据库,我建议每个用户/ ip单独的sqlite数据库,以便更快地查询。
  4. IP更可靠,因为可以欺骗用户代理并清除cookie(因此从浏览器清除会话ID)。如果您有动态IP,甚至可以重置IP。如果您收到太多请求并且您认为自己受到了攻击,则可以暂时禁用所有请求的上传。

答案 7 :(得分:0)

我建议你按照自己的意愿行事,但是当上传完成时,只需发送回结果并验证请求上传的用户的状态,如果用户是/是,那么您可以清除会话如果不存在,如果他们更改了他们的IP地址或会话密钥,他们就不会收到结果。

您可以在db / session中保存一个简单的密钥,以将结果传递给正确的用户。

答案 8 :(得分:0)

据我了解,您希望阻止用户每个时段上传过多文件。为了做到这一点,您实际上需要在服务器端识别用户(假设可以操作任何客户端机制)而无需注册。我担心这是不可能的。

但是,你有两个不错的选择:

  1. 识别具有客户端,cookie,会话,IP,用户代理或任何其他因素和/或它们组合的用户,从而使恶意用户努力利用您的系统漏洞。它并不完美,但它会阻止大多数普通用户的尝试。

  2. 使用服务器端身份验证系统。您提到您不想放松流量,但让我们考虑一下,如果您的免费用户甚至没有花一点时间注册(只有电子邮件和密码),他们将来可能不会是您的付费客户,所以,如果你想节省交通费用,失去“leechers”流量可能是一件好事。

答案 9 :(得分:0)

是的,你可以在服务器端处理这个问题。会议是安全的。只需在php配置中禁用会话cookie。因此会话ID被添加到url而不是存储在cookie中。因此没有机会操纵某些东西。

但是:处理过多已经上传到服务器上的文件会导致不必要的流量通过互联网。所以我强烈建议您检查用户是否在客户端选择了少于10个文件。您可以使用javascript轻松完成此操作。只需处理答案,即返回FileChooser对话框(与Java对JFileChooser的处理方式相同)。在此答案中,应该是包含此表单中所有选定文件的字符串:

"pic1.png", "pic2.jpg", "pic3.jpg"

因此,您需要做的就是计算此字符串中文件名的出现次数。简单地拆分它,签名并删除“在开头和结尾(子串)并将结果放入数组。计算数组长度。如果长度大于10 =>用户选择了太多文件。

答案 10 :(得分:-1)

您可以从客户端限制文件选择

$('#fileupload').change(function(){
    var files = $(this)[0].files;
    if(files.length > 10){
       //do what you want , like alert warning
       // and disable submit button
    }else{
      //do what you want , like enable submit button
    }
});

Demo