DEV Community

Cover image for Secure File Share - A safer way to share sensitive files online
Menard Maranan
Menard Maranan

Posted on

Secure File Share - A safer way to share sensitive files online

This is a submission for the The Pinata Challenge

What I Built

I've developed "Secure File Share", an open-source, self-hostable file sharing solution that addresses a common need in our digital world: the secure sharing of sensitive files without relying on third-party services.

Inspiration

Being a developer working with a remote team, it is very common for us to share sensitive company files online, which is at risk of accidentally leaking to the outside world. Files have been scattered over email attachments, chat threads, and anywhere we communicate. For now, we adopted the password-protected files sharing feature of Google Drive but we're always looking for solutions without any 3rd party involvement and where we can have full control over our files, especially if we can self-host it. Our DevOps guy is working on a solution, but if I were to build that solution, this is how I imagine it to be.

This is also heavily inspired by Onetimesecret. This is like one time secret, but for files.

How it Works

  1. Upload a file you want to share
  2. Set a passphrase and expiration, then click Create Share Link
  3. Send the share link to your intended recipient/s. It's recommended to share the passphrase separately.

The uploaded file as well as its records will all be deleted upon the specified expiration date. Upon viewing the file, the set expiration will be overwritten and will be set to 5 minutes before it's totally gone.

Key features of Secure File Transfer include:

  1. Disposable and password protected share links
  2. No sign-up required.
  3. Fully self-hostable

Demo

Quick Peek

Hero Image

Upload Page

Share Page

File View Request page

File View Page

My Code

(NOTE: You can read the README for more info)

Tech Stack

  • Backend: Express.js with TypeScript
  • Frontend: EJS templates, Bulma CSS framework, Alpine.js
  • Database: SQLite with Prisma ORM (easily replaceable if needed)
  • File Storage: Pinata cloud
  • Background Tasks: BullMQ with Redis

I tried to be as light weight as possible when choosing the techstack and to not over engineer this. That said, I just have a simple express ts backend, ejs templates with Alpine.js (for minimal frontend interactivity) and Bulma as the CSS framework. The database is just an sqlite file since I don't see a need for a full blown database in this hackathon project, but this can be easily changed since I used Prisma ORM for it. Just modify the datasource. I use BullMQ with Redis for background tasks (handling file expiration). And of course, Pinata cloud. I hosted this in fly.io as they have a generous free tier plan but this can be hosted anywhere, like in VPS (there's a docker compose you can use).

More Details

Pinata plays a pivotal role in this project as all the shared files need to be stored somewhere, and that's where Pinata fits.

Here's the myPinata.ts file where I primarily interacted with Pinata via the Files SDK

import { FileObject, PinataSDK } from "pinata";
import dotenv from "dotenv";

dotenv.config();

const pinata = new PinataSDK({
    pinataJwt: process.env.PINATA_JWT,
    pinataGateway: process.env.PINATA_GATEWAY
});

export async function uploadFile(file: FileObject) {
    try {
        const upload = await pinata.upload.file(file);
        return upload;
    } catch (error) {
        console.error(error);
    }
}

export async function getFileUrl(cid: string, expires=1800) {
    try {
        const signedUrl = await pinata.gateways.createSignedURL({
            cid,
            expires // defaults to 30 minutes (1800 seconds)
        });
        return signedUrl;
    } catch (error) {
        console.error(error);
    }
}

export async function deleteFile(fileId: string) {
    try {
        const deleted = await pinata.files.delete([fileId]);
        return deleted;
    } catch (error) {
        console.error(error);
    }
}
Enter fullscreen mode Exit fullscreen mode

I use these three functions in my endpoint handlers to handle file operations with Pinata. My background tasks also use the deleteFile function, since these background tasks are used for dispatching deletion jobs at the set expiration of the files.

Top comments (0)