Preface
Recently, I have encountered a problem that I think is a very common problem of Web developers. In a Laravel
project, users can upload files of different sizes and of course those files should be encrypted to ensure user privacy.
Laravel
has provided Encryption for this matter. However, it is primarily designed to encode common values such as numbers or letters. Encrypting a small file (for example, an image) can use the encrypt
helper, but in the process the content of the file is already in memory. If your working file is a large file, then obviously it will become a pretty serious problem.
I searched for a library or some solution to solve this problem and had the answer on stackoverflow and using php to implement the solution described by stackoverflow
.
I decided to create a package designed for Laravel
projects in order to provide a simple and easier solution for encryption/decryption
with a short and easy to understand syntax.
I call it the FileVault
package and you can see it on Github . If you want to skip this article, then you can go straight to Repo on Github
and get started with it. The package has very specific documentation for you to use it.
Tutorial.
In this tutorial, I will show you all the steps you need to convert a large file in a new laravel project.
First, obviously, you need to create a new project and we’ll call it security-app
with the following command:
1 2 | laravel new security-app |
At the time of this writing, we use Laravel v6.5.2
everyone.
Because I have used the Laravel installer
, I already have an applicaiton with built-in keys created and added in the .env
file. If you use a different way to install, don’t forget to create an app key for the project. Use the command:
1 2 | php artisan key:generate |
Because I used Laravel Valet , I was automatically created domain security-app.test
to work. If you do not use it or use a different development environment, then you need to add local domain to test.
ok, next we install the laravel/ui
package:
1 2 | composer require laravel/ui — dev |
And I will use bootstrap
and auth
too:
1 2 | php artisan ui bootstrap --auth |
And finally compile everything just setup:
1 2 | npm install && npm run dev |
And of course we can not forget the config database in the .env
file and run the command:
1 2 | php artisan migrate |
ok, now we can create users and log in to access the user dashboard.
Note: The purpose of this article is just a demo, so I will create a simple form for uploading the file, but in your app, you should care to use a more sophisticated upload function. With files that are too large, you might think about how to split the file and upload it to the server.
A very good package (in my opinion) that you can use for this problem is pion / laravel-chunk-upload .
Laravel
auth has Laravel
us /home
routes. It is defined in the HomeController
and uses the home.blade.php
view file.
Our job now is to edit the file view home.blade.php
and add a form to upload the image:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <form action="{{ route('uploadFile') }}" method="post" enctype="multipart/form-data" class="my-4"> @csrf <div class="form-group"> <div class="custom-file"> <input type="file" class="custom-file-input" id="userFile" name="userFile"> <label class="custom-file-label" for="userFile">Choose a file</label> </div> </div> <button type="submit" class="btn btn-primary">Upload</button> @if (session()->has('message')) <div class="alert alert-success mt-3"> {{ session('message') }} </div> @endif </form> |
Now create a route:
1 2 | Route::post(‘/home’, ‘ <a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="5a1235373f1935342e283536363f281a292e35283f">[email protected]</a> ’)->name(‘uploadFile’); |
And create a new method inside HomeController
. This method will store the uploaded file in the subdirectories identified through the user id of that user. In this case, it will be storage/app/files/{user-id}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * Store a user uploaded file * * @param IlluminateHttpRequest $request * @return IlluminateHttpResponse */ public function store(Request $request) { if ($request->hasFile('userFile') && $request->file('userFile')->isValid()) { Storage::putFile('files/' . auth()->user()->id, $request->file('userFile')); } return redirect()->route('home')->with('message', 'Upload complete'); } |
This is where we need to encrypt the file that the user has uploaded. Now let’s get the package file-vault
:
1 2 | composer require soarecostin/file-vault |
This package will allow access to the FileVault facade
which will define methods for encrypting and decoding files, as well as methods for setting information such as different keys to encrypt different files or just to where will save the file.
We will use the FileVault::encrypt($file)
to encrypt user uploaded files. This function will delete the original file (the file the user uploads to the server) and replace it with the file with the same name but the extension is .enc
.
If you want to customize the file name, you just need to pass one more input parameter to describe the name that the file will be received into encrypt
. If you want to keep the original file, just use the encryptCopy
.
Currently, the method store
will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * Store a user uploaded file * * @param IlluminateHttpRequest $request * @return IlluminateHttpResponse */ public function store(Request $request) { if ($request->hasFile('userFile') && $request->file('userFile')->isValid()) { $filename = Storage::putFile('files/' . auth()->user()->id, $request->file('userFile')); // Check to see if we have a valid file uploaded if ($filename) { FileVault::encrypt($filename); } } return redirect()->route('home')->with('message', 'Upload complete'); } |
Next, we need to find a way to decode and download the uploaded files to the server.
The first need to initialize the downloadFile
route and the downloadFile
method inside the HomeController
.
1 2 | Route::get(‘/files/{filename}’, ‘ <a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="165e797b735579786264797a7a736456727961787a797772507f7a73">[email protected]</a> ’)->name(‘downloadFile’); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * Download a file * * @param string $filename * @return IlluminateHttpResponse */ public function downloadFile($filename) { // Basic validation to check if the file exists and is in the user directory if (!Storage::has('files/' . auth()->user()->id . '/' . $filename)) { abort(404); } return response()->streamDownload(function () use ($filename) { FileVault::streamDecrypt('files/' . auth()->user()->id . '/' . $filename); }, Str::replaceLast('.enc', '', $filename)); } |
The downloadFile
method uses Laravel native streamDownload . In the callback, we call the streamDecrypt
method of the FileVault facade
provided, which will decrypt the file and return each part to the streamDownload
method, allowing the user to download the decoded file.
ok, so now we can encrypt in a very simple and convenient way. We have just finished creating the form for the user uipload, encrypting the file and decrypting it when the user requests to re-download the file that each account itself has.
Of course in production projects we need more security components. And FileVault
is designed to help you with that.
If you like this article and want to learn more about how to upload and encrypt files using Amazon S3, you can visit another article I wrote: How to encrypt and upload large files to Amazon S3 in Laravel.
Source
https://medium.com/better-programming/how-to-encrypt-large-files-in-laravel-293460836ded