Ali*_*lix 11 php mod-rewrite file-upload http node.js
将大文件(> 100M)上传到服务器时,PHP始终首先从浏览器接受整个数据POST.我们无法注入上传过程.
例如,在我的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>
Run Code Online (Sandbox Code Playgroud)
所以我试着这样使用mod_rewrite:
RewriteEngine On
RewriteMap mymap prg:/tmp/map.php
RewriteCond %{QUERY_STRING} ^token=(.*)$ [NC]
RewriteRule ^/upload/fake.php$ ${mymap:%1} [L]
Run Code Online (Sandbox Code Playgroud)
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';
}
Run Code Online (Sandbox Code Playgroud)
但是......它又失败了.mod_rewrite看起来在这种情况下工作太晚了.数据仍然完全转移.
然后我尝试了Node.js,像这样(代码剪辑):
var stream = new multipart.Stream(req);
stream.addListener('part', function(part) {
sys.print(req.uri.params.token+"\n");
if (req.uri.params.token != "xxxx") {//check token
res.sendHeader(200, {'Content-Type': 'text/plain'});
res.sendBody('Incorrect token!');
res.finish();
sys.puts("\n=> Block");
return false;
}
Run Code Online (Sandbox Code Playgroud)
结果是... 再次失败.
所以请帮我找到解决这个问题的正确途径,或者告诉我没办法.
相关问题:
Ben*_*aum 21
首先,您可以使用我为此创建的GitHub repo自己尝试使用此代码.只需克隆存储库并运行即可node header.
(剧透,如果你正在读这篇文章,并且在时间紧迫的情况下得到一些工作但没有心情学习(:(),最后有一个更简单的解决方案)
这是一个很好的问题.您要求的是非常可能的,并且不需要客户端,只需更深入地了解HTTP协议如何工作,同时展示node.js如何摇摆:)
如果我们深入到底层TCP协议的一个级别并为这个特定情况自己处理HTTP请求,这可以变得容易.Node.js允许您使用内置的网络模块轻松完成此操作.
首先,让我们看看HTTP请求是如何工作的.
HTTP请求包含一个标题部分,其格式为key:value对,由CRLF(\r\n)分隔.我们知道当我们达到双CRLF(即\r\n\r\n)时,标题部分结束.
典型的HTTP GET请求可能如下所示:
GET /resource HTTP/1.1
Cache-Control: no-cache
User-Agent: Mozilla/5.0
Hello=World&stuff=other
Run Code Online (Sandbox Code Playgroud)
"空行"前面的顶部是标题部分,底部是请求的主体.你的请求在正文部分看起来会有点不同,因为它是用编码的,multipart/form-data但标题将保持相似让我们探讨这是如何适用于我们的.
我们可以在TCP中监听原始请求并读取我们获得的数据包,直到我们读到我们所讨论的双crlf.然后我们将检查我们已经拥有的短标题部分,以进行我们需要的任何验证.在我们这样做之后,如果验证没有通过,我们可以结束请求(例如,通过简单地结束TCP连接),或者传递它.这允许我们不接收或读取请求主体,而只是更小的标头.
将其嵌入到现有应用程序中的一种简单方法是将请求从它代理到特定用例的实际HTTP服务器.
这个解决方案就像它的骨架一样.这只是一个建议.
这是工作流程:
我们需要netnode.js中的模块,它允许我们在node.js中创建tcp服务器
使用net将监听数据的模块创建TCP服务器: var tcpServer = net.createServer(function (socket) {....别忘了告诉它听正确的端口
socket.on("data",function(data){,这将在数据包到达时触发.socket.end()哪个将关闭连接.一种读取标题的方法:
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;
};
Run Code Online (Sandbox Code Playgroud)
一种在数据事件中获取的缓冲区中检查双CRLF的方法,如果它存在于对象中,则返回其位置:
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 };
};
Run Code Online (Sandbox Code Playgroud)
而这个小实用方法:
function isContinuation(str) {
return str.charAt(0) === " " || str.charAt(0) === "\t";
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
如果您有任何问题随时问 :)
但那有什么好玩的?如果你最初跳过这里,你将无法学习HTTP如何工作:)
Node.js有一个内置http模块.由于请求在node.js中按性质分块,特别是长请求,因此您可以实现相同的功能,而无需更深入地了解协议.
这次,让我们使用该http模块创建一个http服务器
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);
Run Code Online (Sandbox Code Playgroud)
这是基于requestnodejs http模块中的句柄在发送标头之后实际挂钩的事实(但是没有执行任何其他操作)默认情况下.(这在服务器模块中,这在解析器模块中)
用户igorw建议使用100 Continue标题更清晰的解决方案,假设您定位的浏览器支持它.100 Continue是一个状态代码,旨在完全按照您的尝试:
100(继续)状态(参见第10.1.1节)的目的是允许正在向请求主体发送请求消息的客户端确定源服务器是否愿意接受请求(基于请求头)在客户端发送请求主体之前.在某些情况下,如果服务器在不查看正文的情况下拒绝邮件,则客户端发送正文可能不合适或效率极低.
这里是 :
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);
Run Code Online (Sandbox Code Playgroud)
您可以在此处查看示例输入/输出.这将要求您使用适当的Expect:标头触发您的请求.
| 归档时间: |
|
| 查看次数: |
3276 次 |
| 最近记录: |