通过PHP或Apache中断服务器端的HTTP文件上载

通过PHP或Apache中断服务器端的HTTP文件上载,php,node.js,http,mod-rewrite,file-upload,Php,Node.js,Http,Mod Rewrite,File Upload,当上传大文件(>100M)到服务器时,PHP总是首先接受来自浏览器的整个数据发布。我们无法在上传过程中插入 例如,在我的PHP代码中,在将整个数据发送到服务器之前,请检查“token”的值 <form enctype="multipart/form-data" action="upload.php?token=XXXXXX" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="3000000"

当上传大文件(>100M)到服务器时,PHP总是首先接受来自浏览器的整个数据发布。我们无法在上传过程中插入

例如,在我的PHP代码中,在将整个数据发送到服务器之前,请检查“
token
”的值

<form enctype="multipart/form-data" action="upload.php?token=XXXXXX" method="POST">
    <input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
    Send this file: <input name="userfile" type="file" />
    <input type="submit" value="Send File" />
</form>
map.php

#!/usr/bin/php
<?php
define("REAL_TARGET", "/upload/real.php\n");
define("FORBIDDEN", "/upload/forbidden.html\n");

$handle = fopen ("php://stdin","r");
while($token = trim(fgets($handle))) {
file_put_contents("/tmp/map.log", $token."\n", FILE_APPEND);
    if (check_token($token)) {
        echo REAL_TARGET;
    } else {
        echo FORBIDDEN;
    }
}

function check_token ($token) {//do your own security check
    return substr($token,0,4) === 'alix';
}
结果是。。。再次失败

所以请帮我找到解决这个问题的正确途径,或者告诉我没有办法

相关问题:


听起来您正在尝试流式传输上传,需要在处理之前进行验证: 这有用吗?


我建议您使用一些客户端插件来上传文件。你可以用

这两个插件都提供了在上传之前检查文件大小的功能

如果您想使用自己的脚本,请选中此项。这可能对你有帮助

        function readfile()
        {
            var files = document.getElementById("fileForUpload").files;
            var output = [];
            for (var i = 0, f; f = files[i]; i++) 
            {
                    if(f.size < 100000) // Check file size of file
                    {
                        // Your code for upload
                    }
                    else
                    {
                        alert('File size exceeds upload size limit');
                    }

            }
        }
函数readfile()
{
var files=document.getElementById(“fileForUpload”).files;
var输出=[];
for(var i=0,f;f=files[i];i++)
{
if(f.size<100000)//检查文件的文件大小
{
//你的上传代码
}
其他的
{
警报(“文件大小超过上载大小限制”);
}
}
}

以前的版本有些模糊。因此,我重写了代码,以显示路由处理和中间件之间的区别。每个请求都会执行中间件。它们是按照给定的顺序执行的
express.bodyParser()
是一种中间件,用于处理文件上载,如果令牌不正确,则应跳过该文件上载
mymiddleware
只是检查令牌并终止无效请求。这必须在执行
express.bodyParser()
之前完成

var express = require('express'),
app = express();

app.use(express.logger('dev'));
app.use(mymiddleware);                                 //This will work for you.
app.use(express.bodyParser());                         //You want to avoid this
app.use(express.methodOverride());
app.use(app.router);

app.use(express.static(__dirname+'/public'));
app.listen(8080, "127.0.0.1");

app.post('/upload',uploadhandler);                     //Too late. File already uploaded

function mymiddleware(req,res,next){                   //Middleware
    //console.log(req.method);
    //console.log(req.query.token);
    if (req.method === 'GET')
        next();
    else if (req.method === 'POST' && req.query.token === 'XXXXXX')
        next();
    else
        req.destroy();
}

function uploadhandler(req,res){                       //Route handler
    if (req.query.token === 'XXXXXX')
        res.end('Done');
    else
        req.destroy();
}

另一方面,
uploadhandler
无法中断上载,因为它已经被
express.bodyParser()
处理过了。它只处理POST请求。希望这有帮助。

绕过PHP的post处理的一种方法是通过PHP-CLI路由请求。创建以下CGI脚本并尝试将一个大文件上载到其中。web服务器应通过终止连接进行响应。如果是这样,那么只需要打开一个内部套接字连接并将数据发送到实际位置——当然,前提是条件满足

#!/usr/bin/php
<?php

echo "Status: 500 Internal Server Error\r\n";
echo "\r\n";
die();

?>
#/usr/bin/php

为什么不直接使用APC文件上载进度,并将进度键设置为APC文件上载的键,以便在这种情况下,提交表单并开始上载进度,但在第一次进度检查时,您将验证该键,如果该键不正确,您将中断一切:

这是一种更自然的方法。大致相同,只需将隐藏输入的密钥更改为您的令牌,并验证该密钥,并在出现错误时中断连接。也许这样更好。
使用javascript。当用户单击Submit时,通过ajax提交一个预表单,等待ajax响应,然后当它返回成功与否时,提交实际表单。你也可以使用你不想要的方法,这总比没有好

<script type="text/javascript">
function doAjaxTokenCheck() {
    //do ajax request for tokencheck.php?token=asdlkjflgkjs
    //if token is good return true
    //else return false and display error
}
</script>

<form enctype="multipart/form-data" action="upload.php?token=XXXXXX" method="POST">
    <input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
    Send this file: <input name="userfile" type="file" />
    <input type="submit" value="Send File" onclick="return doAjaxTokenCheck()"/>
</form>

函数doAjaxTokenCheck(){
//ajax是否请求tokencheck.php?token=asdlkjflgkjs
//如果令牌正确,则返回true
//否则返回false并显示错误
}
发送此文件:
首先。只需克隆存储库并运行
节点头

(剧透者,如果你正在读这篇文章,并且迫于时间的压力想做点什么,而没有心情学习(:(),最后有一个更简单的解决方案)

###总体思路

这是一个很好的问题。您需要的是非常可能,并且不需要客户端,只是在展示node.js如何运行的同时,更深入地了解HTTP协议的工作原理:)

如果我们更深入到底层,并针对这个特定的情况自己处理HTTP请求,这将变得很容易。js让您可以使用内置的

HTTP协议 首先,让我们看看HTTP请求是如何工作的

由一个标题部分组成,其格式为键:值对的通用格式,由CRLF分隔(
\r\n
)。我们知道,当到达双CRLF(即
\r\n\r\n
)时,标题部分结束

典型的HTTP GET请求可能如下所示:

RewriteEngine On
RewriteMap mymap prg:/tmp/map.php
RewriteCond %{QUERY_STRING} ^token=(.*)$ [NC]
RewriteRule ^/upload/fake.php$ ${mymap:%1} [L]
GET /resource HTTP/1.1  
Cache-Control: no-cache  
User-Agent: Mozilla/5.0 

Hello=World&stuff=other
“空行”前面的顶部是headers部分,底部是请求的主体。您的请求在正文部分看起来会有点不同,因为它使用
多部分/表单数据编码
,但标题将保持相似。让我们来探讨一下这是如何应用于我们的

nodejs中的TCP 我们可以监听TCP中的原始请求并读取我们得到的数据包,直到我们读取到我们提到的双crlf。然后,我们将检查短标题部分,我们已经有任何验证,我们需要。完成后,如果验证没有通过,我们可以结束请求(例如,通过简单地结束TCP连接),或者通过验证。这允许我们不接收或读取请求主体,而只接收更小的头

将其嵌入现有应用程序的一种简单方法是将来自该应用程序的请求代理到特定用例的实际HTTP服务器

实施细节 此解决方案是一个简单的解决方案。这只是一个建议

以下是工作流程:

  • 我们需要node.js中的
    net
    模块,该模块允许我们在node.js中创建tcp服务器

  • 使用
    net
    模块创建TCP服务器,该模块将侦听数据:
    var tcpServer=net.createServer(fun
    
    GET /resource HTTP/1.1  
    Cache-Control: no-cache  
    User-Agent: Mozilla/5.0 
    
    Hello=World&stuff=other
    
    function readHeaders(headers) {
        var parsedHeaders = {};
        var previous = "";    
        headers.forEach(function (val) {
            // check if the next line is actually continuing a header from previous line
            if (isContinuation(val)) {
                if (previous !== "") {
                    parsedHeaders[previous] += decodeURIComponent(val.trimLeft());
                    return;
                } else {
                    throw new Exception("continuation, but no previous header");
                }
            }
    
            // parse a header that looks like : "name: SP value".
            var index = val.indexOf(":");
    
            if (index === -1) {
                throw new Exception("bad header structure: ");
            }
    
            var head = val.substr(0, index).toLowerCase();
            var value = val.substr(index + 1).trimLeft();
    
            previous = head;
            if (value !== "") {
                parsedHeaders[head] = decodeURIComponent(value);
            } else {
                parsedHeaders[head] = null;
            }
        });
        return parsedHeaders;
    };
    
    function checkForCRLF(data) {
        if (!Buffer.isBuffer(data)) {
            data = new Buffer(data,"utf-8");
        }
        for (var i = 0; i < data.length - 1; i++) {
            if (data[i] === 13) { //\r
                if (data[i + 1] === 10) { //\n
                    if (i + 3 < data.length && data[i + 2] === 13 && data[i + 3] === 10) {
                        return { loc: i, after: i + 4 };
                    }
                }
            } else if (data[i] === 10) { //\n
    
                if (data[i + 1] === 10) { //\n
                    return { loc: i, after: i + 2 };
                }
            }
        }    
        return { loc: -1, after: -1337 };
    };
    
    function isContinuation(str) {
        return str.charAt(0) === " " || str.charAt(0) === "\t";
    }
    
    var net = require("net"); // To use the node net module for TCP server. Node has equivalent modules for secure communication if you'd like to use HTTPS
    
    //Create the server
    var server = net.createServer(function(socket){ // Create a TCP server
        var req = []; //buffers so far, to save the data in case the headers don't arrive in a single packet
        socket.on("data",function(data){
            req.push(data); // add the new buffer
            var check = checkForCRLF(data);
            if(check.loc !== -1){ // This means we got to the end of the headers!
                var dataUpToHeaders= req.map(function(x){
                    return x.toString();//get buffer strings
                }).join("");
                //get data up to /r/n
                dataUpToHeaders = dataUpToHeaders.substring(0,check.after);
                //split by line
                var headerList = dataUpToHeaders.trim().split("\r\n");
                headerList.shift() ;// remove the request line itself, eg GET / HTTP1.1
                console.log("Got headers!");
                //Read the headers
                var headerObject = readHeaders(headerList);
                //Get the header with your token
                console.log(headerObject["your-header-name"]);
    
                // Now perform all checks you need for it
                /*
                if(!yourHeaderValueValid){
                    socket.end();
                }else{
                             //continue reading request body, and pass control to whatever logic you want!
                }
                */
    
    
            }
        });
    }).listen(8080); // listen to port 8080 for the sake of the example
    
    server = http.createServer( function(req, res) { //create an HTTP server
        // The parameters are request/response objects
        // check if method is post, and the headers contain your value.
        // The connection was established but the body wasn't sent yet,
        // More information on how this works is in the above solution
        var specialRequest = (req.method == "POST") && req.headers["YourHeader"] === "YourTokenValue";
        if(specialRequest ){ // detect requests for special treatment
          // same as TCP direct solution add chunks
          req.on('data',function(chunkOfBody){
                  //handle a chunk of the message body
          });
        }else{
            res.end(); // abort the underlying TCP connection, since the request and response use the same TCP connection this will work
            //req.destroy() // destroy the request in a non-clean matter, probably not what you want.
        }
    }).listen(8080);
    
    var http = require('http');
     
    function handle(req, rep) {
        req.pipe(process.stdout); // pipe the request to the output stream for further handling
        req.on('end', function () {
            rep.end();
            console.log('');
        });
    }
     
    var server = new http.Server();
     
    server.on('checkContinue', function (req, rep) {
        if (!req.headers['x-foo']) {
            console.log('did not have foo');
            rep.writeHead(400);
            rep.end();
            return;
        }
     
        rep.writeContinue();
        handle(req, rep);
    });
     
    server.listen(8080);