PHP 从 Javascript 加密流文件

2023-12-11

我正在开发一个用于大文件的文件上传器。从 HTML 脚本上传并使用 ArrayBuffer 和 Unit8Array 从 Javascript 按字节发送到 PHP。 PHP 脚本将流式传输文件并将其保存到文件夹中。

这是我的 Javascript 的样子

function upload(fileInputId, fileIndex)
    {
        var file = document.getElementById(fileInputId).files[fileIndex];
        var blob;
        var reader = new FileReader();
        reader.readAsBinaryString(file); 
        reader.onloadend  = function(evt)
        {
            xhr = new XMLHttpRequest();

            xhr.open("POST", 'upload.php?name=' + file.name, true);

            XMLHttpRequest.prototype.mySendAsBinary = function(text){
                var data = new ArrayBuffer(text.length);
                var ui8a = new Uint8Array(data, 0);
                for (var i = 0; i < text.length; i++){ 
                    ui8a[i] = (text.charCodeAt(i) & 0xff);

                }

                if(typeof window.Blob == "function")
                {
                     blob = new Blob([data]);
                }else{
                     var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
                     bb.append(data);
                     blob = bb.getBlob();
                }

                this.send(blob);
            }

            var eventSource = xhr.upload || xhr;
            eventSource.addEventListener("progress", function(e) {
                var position = e.position || e.loaded;
                var total = e.totalSize || e.total;
                var percentage = Math.round((position/total)*100);
            });

            xhr.onreadystatechange = function()
            {
                if(xhr.readyState == 4)
                {
                    if(xhr.status == 200)
                    {
                        console.log("Done");
                    }else{
                        console.log("Fail");
                    }
                }


            };

            xhr.mySendAsBinary(evt.target.result);
        };
    }

这是我的upload.php

$inputHandler = fopen('php://input', "r");
$loc = "uploads/" . $_GET["name"];
$fileHandler = fopen($loc, "w+");

while(true) {
    $buffer = fgets($inputHandler, 4096);



    if (strlen($buffer) == 0) {
        fclose($inputHandler);
        fclose($fileHandler);
        return true;
    }

    fwrite($fileHandler, $buffer);
}

我的问题是,当文件处于流模式时,如何使用 AES 或 mcrypt 加密这些上传文件?


事情是这样的。这是来自记忆且未经测试的,因为我的笔记本电脑上没有 PHPSecLib 库,而且我懒得将其全部设置......

require __DIR__ . '/vendor/autoload.php';

use phpseclib\Crypt\AES;
use phpseclib\Crypt\Random;

AESStreamEncode($input, $output, $key)
{
    $cipher = new AES(AES::MODE_CBC);
    $cipher->setKey($key);
    
    $iv = Random::string($cipher->getBlockLength() >> 3);
    $cipher->setIV($iv);
    
    $base64_iv = rtrim(base64_encode($iv), '='); //22 chars
    
    fwrite($output, $base64_iv); //store the IV this is like a salt

    while(!feof($input)) {
        $contents = fread($input, 1000000); //number of bytes to encrypt 
        $encrypted = $cipher->encrypt($contents);
        //trim the = or ==, and replace with :, write to output stream.
        fwrite($output, rtrim(base64_encode($encrypted), '=').':'); 
    }
}

AESStreamDecode($input, $output, $key)
{
    $cipher = new AES(AES::MODE_CBC);
    $cipher->setKey($key);
    
    $buffer = '';
    $iv = false;
    
    while(!feof($input)) {
        $char = fgetc($input); //get a single char
        if($char ==':'){
            if(!$iv){
                $iv = base64_decode(substr($buffer, 0, 22).'=');  //iv is the first 22 of the first chunk.
                $cipher->setIV($iv);
                $buffer = substr($buffer, 22); //remove the iv
            }
            $buffer = base64_decode($buffer.'='); //decode base64 to bin
            $decrypted = $cipher->decrypt($buffer);
            fwrite($output, $decrypted);
            
            $buffer = ''; //clear buffer.
        }else{
            $buffer .= $char;
        }
    }
}

Where $input and $output是有效的资源流句柄,例如 fromfopen etc.

 $input = fopen($filepath, 'r');
 $output = fopen($ohter_filepath, 'w');

 AESStreamEncode($input, $output, $key);

这可以让你使用类似的东西php://output如果下载解密文件则作为流。

你必须删除=因为有时会缺少或其中两个,所以我们不能依赖它们作为分隔符。我通常只是把 1 放回去,它总是能正确解码。无论如何,我认为这只是一些填充。

参考

GitHub 上的 PHPSecLib

PHPSecLib 示例

加密的文件应如下所示:

xUg8L3AatsbvsGUaHLg6uYUDIpqv0xnZsimumv7j:zBzWUn3xqBt+k1XP0KmWoU8lyfFh1ege:nJzxnYF51VeMRZEeQDRl8:

但有更长的块。 IV 就像盐一样,通常的做法是将其添加到加密字符串的前面或后面。例如

[xUg8L3AatsbvsGU]aHLg6uYUDIpqv0xnZsimumv7j:

中的部分[]是IV,(在base64_encode之后有22个字符)我数了很多次,结果总是那么长。我们只需要记录IV并设置一次即可。我想你可以为每个块做不同的 IV,但无论如何。

如果您确实使用 PHPSecLib,它也有一些不错的 sFTP 东西。只要确保获得 2.0 版本即可。基本上它有一些针对不同加密算法的后备和本机 PHP 实现。所以喜欢它会尝试open_ssl那么如果你错过了它,它将使用他们的本机实现。我将它用于 sFTP,所以我已经可以使用它了。 sFTP 需要扩展ssh2_sftp如果我记得我们设置时它仅在 Linux 上可用。

UPDATE

对于下载,您只需发出标头,然后为解码函数提供output stream,像这样的

 $input = fopen('encrypted_file.txt', 'r');
 $output = fopen('php://output', 'w');

 header('Content-Type: "text/plain"');
 header('Content-Disposition: attachment; filename="decoded.txt"');

 header('Expires: 0');
 header('Cache-Control: must-revalidate, post-check=0, pre-check=0, max-age=0');
 header("Content-Transfer-Encoding: binary");
 header('Pragma: public');

 //header('Content-Length: '.$fileSize);  //unknown

 AESStreamDecode($input, $output, $key);

这些是非常标准的标题。唯一真正的问题是,因为加密时文件大小不同,您不能简单地获取文件大小并使用它,因为它会更大一些。不传递文件大小不会阻止下载,只是不会有估计时间等。

但是因为我们在加密之前知道其大小,所以我们可以将其嵌入到文件数据本身中,如下所示:

 3555543|xUg8L3AatsbvsGUaHLg6uYUDIpqv0xnZsimumv7j:zBzWUn3xqBt+k1XP0KmWoU8lyfFh1ege:nJzxnYF51VeMRZEeQDRl8:

然后在我们下载时将其拉出,但是您必须使用单独的函数来获取它,并且不搞乱解码文件可能有点棘手。

老实说,我认为这比它的价值更麻烦。

UPDATE2

不管怎样,我对嵌入文件大小进行了这些更改,这是一个选项,但如果不小心完成,它也可能会扰乱文件的解密。 (我没有测试过这个)

AESStreamEncode($input, $output, $key, $filesize = false)
{
    $cipher = new AES(AES::MODE_CBC);
    $cipher->setKey($key);

    $iv = Random::string($cipher->getBlockLength() >> 3);
    $cipher->setIV($iv);

    $base64_iv = rtrim(base64_encode($iv), '='); //22 chars
    
    //Option1 - optional filesize
    if(false !== $filesize){
        //add filesize if given in the arguments
        fwrite($output, $filesize.'|');
    }
    
    /*
        //Option2: using fstat, remove '$filesize = false' from the arguments
        $stat = fstat($input);
        fwrite($output, $stat['size'].'|');
    */

    fwrite($output, $base64_iv); //store the IV this is like a salt

    while(!feof($input)) {
        $contents = fread($input, 1000000); //number of bytes to encrypt 
        $encrypted = $cipher->encrypt($contents);
        //trim the = or ==, and replace with :, write to output stream.
        fwrite($output, rtrim(base64_encode($encrypted), '=').':'); 
    }
}

所以现在我们应该有文件大小3045345|asdaeASE:AEREA等等,然后我们在解密的时候就可以把它拉出来了。

AESStreamDecode($input, $output, $key)
{
    $cipher = new AES(AES::MODE_CBC);
    $cipher->setKey($key);

    $buffer = '';
    $iv = false;
    $filesize = null;

    while(!feof($input)) {
        $char = fgetc($input); //get a single char
        if($char =='|'){
            /*
              get the filesize from the file,
              this is a fallback method, so it wont affect the file if
              we don't pull it out with the other function (see below)
            */
            $filesize = $buffer;
            $buffer = '';
        }elseif($char ==':'){
            if(!$iv){
                $iv = base64_decode(substr($buffer, 0, 22).'=');  //iv is the first 22 of the first chunk.
                $cipher->setIV($iv);
                $buffer = substr($buffer, 22); //remove the iv
            }
            $buffer = base64_decode($buffer.'='); //decode base64 to bin
            $decrypted = $cipher->decrypt($buffer);
            fwrite($output, $decrypted);

            $buffer = ''; //clear buffer.
        }else{
            $buffer .= $char;
        }
    }
    //when we do a download we don't want to wait for this
    return $filesize;
}

解码获取文件大小部分充当后备,或者如果您不需要它,那么您不必担心它在解码时弄乱文件。下载时我们可以使用下面的函数,这样我们就不必等待文件完全读取来获取大小(这与我们上面所做的基本相同)。

//We have to use a separate function because
//we can't wait tell reading is complete to 
//return the filesize, it defeats the purpose
AESStreamGetSize($input){
    $buffer = '';
    //PHP_INT_MAX (maximum allowed integer) is 19 chars long
    //so by putting a limit of 20 in we can short cut reading
    //if we can't find the filesize
    $limit = 20;
    $i; //simple counter.
    while(!feof($input)) {
        $char = fgetc($input); //get a single char
        if($char =='|'){
            return $buffer;
        }elseif($i >= $limit){
            break;
        }
        $buffer .= $char;
        ++$i; //increment how many chars we have read
    }
    return false;
}

然后在下载时您只需要进行一些更改即可。

$input = fopen('encrypted_file.txt', 'r');
//output streams dumps it directly to output, lets us handle larger files
$output = fopen('php://output', 'w');
//other headers go here

if(false !== ($filesize = AESStreamGetSize($input))){
    header('Content-Length: '.$fileSize);  //unknown
    //because it's a file pointer we can take advantage of that
    //and the decode function will start where the getSize left off.
    // or you could rewind it because of the fallback we have.
    AESStreamDecode($input, $output, $key);
}else{
    //if we can't find the filesize, then we can fallback to download without it
    //in this case we need to rewind the file
    rewind($input);
    AESStreamDecode($input, $output, $key);
}

如果你想缩短它,你也可以这样做,最多只有大约 19 个字符,所以这不是一个大的性能问题。

 if(false !== ($filesize = AESStreamGetSize($input))) header('Content-Length: '.$fileSize);

 rewind($input);
 AESStreamDecode($input, $output, $key);

基本上上面,我们只是做文件大小标头(或不做),然后倒回并进行下载。它会重新读取文件大小,但这非常简单。

以供参考fstat(),希望这是有道理的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PHP 从 Javascript 加密流文件 的相关文章

  • 正则表达式删除带有数字的单词

    我想删除产品名称中带有数字 参考 或小单词 2 个或更少字符 的单词 但我找不到好的正则表达式 一些例子 链式防回弹ECS 2035 应成为 链式防反弹 指南 35 厘米俄勒冈 Intenz 应该成为 俄勒冈州 Intenz 指南 Tron
  • 添加文件://. chrome 扩展程序的权限

    如何使用 Chrome 扩展启用 file 的权限 在我的manifest json中我尝试过 permissions file and permissions file 也 permissions C 这些都不起作用 permission
  • 如何使用 gatsby-image 不裁剪地显示图像?

    实例 图像可能加载缓慢 https suhadolnik photo surge sh portreti https suhadolnik photo surge sh portreti 我正在使用 GatsbyJS 制作一个摄影网站 并使
  • 从多维数组中删除多个值

    我有一个来自 WordPress 的数组get posts 功能 posts array 15 0 gt object WP Post 285 24 ID gt int 253 post author gt string 1 1 post
  • Shared Web Workers 是否会在单页重新加载、链接导航中持续存在

    共享网络工作者 http www whatwg org specs web apps current work shared workers introduction旨在允许来自同一站点 来源 的多个页面共享单个 Web Worker 但是
  • 使用 useCallback 并使用先前状态作为参数设置新对象状态

    考虑这个带有自定义表单钩子的基本表单字段组件来处理输入更改 import React useState useCallback from react const useFormInputs initialState gt const val
  • 如何通过单击按钮调用 PHP 函数

    我创建了一个名为的页面functioncalling php包含两个按钮 Submit and Insert 我想测试单击按钮时执行哪个函数 我希望输出出现在同一页面上 因此 我创建了两个函数 每个按钮一个
  • 在tinymce 4中裁剪后上传图像

    我正在开发tinymce 并且已经实现了imagetools 现在 当图像插入到文本编辑器中 然后我编辑 裁剪图像时 它将图像 src 更改为类似的内容blob www localhost asdf ghij 我想要的是裁剪后我可以将此 u
  • 帮助我将以十六进制表示的长值转换回日期/时间

    我有一个日期值 据说它是 8 个字节 一个 long 又名 int64 值 并转换为十六进制 60f347d15798c901 我如何使用 PHP 将这个和类似的值转换为时间 日期 将其转换为十进制给我 96 243 71 209 87 1
  • 如何在不同位置显示验证错误消息?

    我在用knockout js knockout validation插件 我正在添加示例小提琴 http jsfiddle net hsnCW 1 http jsfiddle net hsnCW 1 在此示例中 对数组进行了自定义验证以检查
  • 如何将base64字符串转换为文件?

    我使用 jquery 插件来裁剪图像 该插件将裁剪图像并将其作为 Base64 编码字符串提供给我 为了将其上传到 S3 我需要将其转换为文件并将该文件传递到上传函数中 我怎样才能做到这一点 我尝试了很多事情 包括使用解码字符串atob 没
  • AWS DynamoDb DocumentClient - 从项目数组创建批量写入 - node.js

    我正在尝试执行batchWrite使用 DynamoDB 的操作DocumentClient来自项目数组 JSON 这是我的代码 var items for i 0 i lt orders length i var ord orders i
  • 在 React 中存储数据

    我实际上是 React 新手 无法选择在这种情况下存储数据的最佳方式是什么 我有一个包含一些输入的表单 我需要在提交时对这些输入中的所有数据执行一些操作 所有输入都存储在一个组件中 所以 我只需要在提交时获取所有数据 现在我正在尝试选择存储
  • innerHTML 未写入 svg 组 Firefox 和 IE

    我正在做一个项目 遇到了障碍 在 Chrome 中 它按预期工作 但在 Firefox 和 IE 中则不然 下面的代码实际上只是真实项目代码的非常简化的版本 基本上我正在尝试替换 svg 的每组中的圆圈 因此 我从预编码的圆圈开始 然后删除
  • 如何处理 file_get_contents() 中的 403 错误?

    我在使用时遇到 403 错误file get contents 我想这样处理这个错误 if required function file get contents url detects there is a 403 error do so
  • 为什么需要原型对象(在函数中)?

    我阅读了大量有关原型的材料并了解继承的一般情况 然而 这是困扰我的一件事 我无法弄清楚 On dmitrysoshnikov com http dmitrysoshnikov com ecmascript javascript the co
  • PHP 指针与引用

    在 PHP 中 使用指针有什么区别 例如 function foo var var 3 a 0 foo a 以及参考 function foo var var 3 a 0 foo a 它们都修改了原始变量的值 但是它们内部的表示方式不同吗
  • iframe 内 Web 元素的 QuerySelector

    编辑 新标题 我正在寻找的是 iframe 内元素的 document querySelector 我已经用谷歌搜索了很多答案 最后我被难住了 我正在尝试在 iframe 内查询 我正在构建要在 Selenium 中使用的字符串选择器 通常
  • 致命错误:找不到类“App\PDO”

    当我尝试使用命名空间时出现此错误 I have namespace App 顶部 班级看起来像 class database function construct try this gt db new PDO lt here the err
  • PHP - 将文件系统路径转换为 ​​URL

    我经常发现项目中的文件需要从文件系统和用户浏览器访问 一个例子是上传照片 我需要访问文件系统上的文件 以便可以使用 GD 来更改图像或移动它们 但我的用户还需要能够从类似以下的 URL 访问文件example com uploads myp

随机推荐

  • 在上传之前调整从图库或相机拍摄的图像的大小

    我的网站中有一个表单 允许用户上传照片 我的 Android 应用程序使用 WebView 来允许用户访问该网站 单击上传按钮后 应用程序允许用户在图库中现有的图像之间进行选择 或者拍摄新照片并上传该图像 我为此使用的代码是 showAtt
  • Cosmos DB - 图形 API - 导出 graphSON 并尝试使用迁移工具导入它

    我在新图中插入了一些顶点和边 我已将生成的 grapSON 保存在 json 文件中 然后我尝试使用迁移工具将该 json 文件导入到新图表中 它只起到了部分作用 我有一个带有顶点的新图 但它们没有这样的属性或边 id c39f435b 3
  • 如何停止由 Win32::Daemon 启动的 Win32 服务?

    我可以使用以下脚本在 Windows 10 Strawberry Perl 版本 5 30 1 上成功启动 Win32 服务 package Win32 XYZService use feature qw say use strict us
  • 用 execv 调用 'ls'

    我是系统调用和 C 编程新手 正在完成我的大学作业 我想调用 ls 命令并让它打印目录 我所拥有的 我添加了注释 以便您可以看到我通过每个变量看到的内容 int execute command cmd char full path 50 f
  • 在 C# 中是否有一种简单的方法可以通过扩展名来确定文件是什么?

    在 C 中是否有一种简单的方法可以通过扩展名来确定文件是什么 例如 如果我传递文件扩展名 txt 那么它将返回 文本文档 或者如果我传递 pdf 它将返回 Adobe Acrobat Reader 我看到 Windows 资源管理器中内置了
  • SQL 语句与 CASE 连接

    我在 MS ACCESS 中执行 SQL CASE IF ELSE 时遇到以下 3 个表的问题 但我不知道如何开始 表 A 注册 Name Desc Amount Year NameA JAN NOV 100 00 2015 NameA B
  • ng-单击复选框不更新表单的 $pristine 属性

    AngularJS 第一次更新隐藏文本时 表单的 pristine 属性不会更新 我有一个 AngularJS 表单 我想知道表单的任何字段是否已更新 当复选框更新时 相应的 pristine属性未更新 所以我添加了一个隐藏的文本框 它绑定
  • 将模型导入到 Three.js - 性能

    对于从要在 Three js JavaScript 应用程序中使用的文件加载 3D 模型的性能 尤其是在 Blender 中生成模型的情况 是否有一种好的 推荐的方法 我目前有以下工作流程 在 Blender 中创建模型 使用 Three
  • 如何使用C在Linux中获取已安装驱动器的卷名?

    我目前正在编写程序 该程序必须显示有关已安装闪存驱动器的信息 我想显示完整空间 可用空间 文件系统类型和卷名称 但问题是 我找不到任何可以获取卷名称 卷标签 的 API 有没有任何api可以做到这一点 附注我正在通过的完整空间 可用空间和文
  • Redis PubSub 订阅机制是如何工作的?

    我想创建一个发布 订阅基础设施 其中每个订阅者都将收听多个 例如 100k 频道 我认为使用 Redis PubSub 来实现此目的 但我不确定订阅数千个频道是否是最佳实践 为了回答这个问题 我想知道 Redis 中的订阅机制如何在后台工作
  • docker-compose - 重启策略 - 不保留图像中的更改

    让我们考虑以下示例 version 3 services some service build restart unless stopped This docker compose工作正常 但是在重新启动期间 它会保留先前运行 重新启动之前
  • 如何从排序列表中选择小于给定整数的元素?

    我有一系列素数 例如0 到 1000 之间的整数 primes 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 1
  • 具有 URL 值的 HTML 标记属性的完整列表?

    除了以下属性之外 是否还有以 URL 作为值的 HTML 标记属性 href标签上的属性 a area src标签上的属性 img a
  • 将字符串转换为日期时间 vb.net

    我需要将字符串转换为日期格式 要求是如果选择当前月份 则日期应为 getdate 如果选择任何其他月份 则应选择该月的第一个月 输入的数据是 2010 年 1 月 2010 年 2 月 等 但它应该作为 01 01 10 或 02 01 1
  • JQuery - on()-方法/动态处理程序

    我有一份等候名单和一份参与者名单 管理员可以通过单击等待列表中用户名旁边的 div 将用户添加到参与者列表中 单击 div 将某人添加到参与者列表后 将调用 ajax 请求 gt 该请求会更新数据库中用户的状态 并且 如果 ajax 请求成
  • WebPack TypeError:无法读取未定义的属性“请求”

    我继承了一个现有的 Angular2 项目 当我跑步时NPM start我收到一个很长的错误 开头是 Html Webpack 插件 类型错误 无法读取未定义的属性 请求 完整的错误输出 http textuploader com d5n2
  • Android CoreLocation 标题

    我目前正在研究一种算法 需要准确估计移动设备的航向 对于iOS中的开发 我不必估计用户标题 因为框架已经提供了以下值trueHeading通过 CoreLocation 框架 所以我不必实现我自己的融合算法 这的美丽trueHeading是
  • Android 中的 Websocket 和 cookie

    我正在开发一个 Android 应用程序 我需要一个 Websockets 框架 该框架允许我在 Websocket 的第一个连接中发送 cookie 而不是在每条消息中 我试过了Autobahn and Java WebSocket但他们
  • facebook graph api 图片

    如何使用 graph api 检索朋友的图片 我已经设法使用这个来获取我朋友的个人资料图片 https graph facebook com user id 但是 我想获取我朋友发布的照片 我能够得到这个数据 link http www f
  • PHP 从 Javascript 加密流文件

    我正在开发一个用于大文件的文件上传器 从 HTML 脚本上传并使用 ArrayBuffer 和 Unit8Array 从 Javascript 按字节发送到 PHP PHP 脚本将流式传输文件并将其保存到文件夹中 这是我的 Javascri