Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Encrypted Cloud

1. Why Encrypted Cloud

Personal cloud storage applications, such as Google Drive, Microsoft OneDrive, Dropbox, etc., allow you to store all kinds of files online and make them available anywhere via a web-based interface. With hundreds of millions of users, these cloud storage platforms are frequent targets of cyberattacks. These cyberattack attempts target not only the server, but the users too, via techniques such as phishing. An example is 2012 Dropbox data breach, which exposed over 68 million accounts. When these passwords are exposed, users’ files can be accessed by malicious entities.

The best way to protect the files stored online in a cloud environment is encryption. Encrypted Cloud is a solution that aims at encrypting a user’s cloud-based file data.

But is a regular encryption good enough? If you store your password at the server where the encrypted data is also stored, then it’s still a security risk as malicious actors who get hold of the file also get passwords, thus defeating the whole purpose of encryption. That’s why the closest to perfect security for users’ files is end-to-end encryption in which file passwords are never transferred to the server. Plus, of course, selecting a password that is practically uncrackable.

Encrypted Cloud allows a user to encrypt their important files locally within the browser using a strong password and then upload to a cloud-based storage server.

2. Program Features

The application allows you to register yourself with your name, username, and a strong password. The guidelines for strong password are given in the “Help” section. After registration, you will be asked to log in and then you can upload your important files from the “Upload” section. Unlike normal cloud stores, Encrypted Cloud requires individual passwords for each file uploaded. A file cannot be uploaded without a password. Also, it is recommended to keep individual passwords for each file, despite it being as inconvenient as it is.

The uploaded files are visible from the “Dashboard”, from where a user can download, change the file passwords, or delete files. Users can also change their user passwords as well.

3. Application Architecture

In a nutshell, the application architecture is as given below.

Run app.py (auto-creates the database and initializes the application).

User registers -> Server handles registration, hashes user password and updates database -> User logs in -> User can see their files and upload new files ->User tries to upload a file ->The file is locally encrypted via functions within “script.js” -> Encrypted blob is generated and sent to the server along with encryption parameters -> Server stores the file within a user folder and adds file metadata and encryption parameters to the database’s “files” table.

Client-side data Server-side data Notes
Username Username
User password Hashed version of the password It’s impossible to derive the user password with it.
User files Encrypted user files
File passwords -- Never transmitted
Encryption parameters Encryption parameters Useless for attackers without the password

3.1. Tech Stack

3.1.1. Server Side

  • Flask for the primary application, along with various Python modules such as Os, Sqlite3, Shutil, Werkzeug Security, Werkzeug Utils, Re, Functools
  • Jinja for templating
  • HTML for various web pages
  • Bootstrap for CSS styles
  • SQLite3 for the database

3.1.2. Client Side

  • JavaScript to handle various events and provide functionalities
  • Web Crypto API to provide encryption and decryption functionality

3.1.3. Application Files

Files of the application and their purpose.

  • App.py: The main Flask file that initiates the application
  • Helper.py: The Python file that defines several helper functions for “app.py”
  • Schema.sql: The SQL schema of the application is defined in this file.
  • Userdata.db: The primary database of the application with two tables, “users” and “files”
  • Requirements.txt: Mentions the application’s dependencies, such as Flask, Jinja, Werkzeug, etc
  • Templates:
    • Register.html: Allows users to register for the application
    • Login.html: Allows users to log into their accounts
    • Index.html: Shows the dashboard with user files to logged-in users
    • Upload.html: Shows users a form to upload a file
    • Help.html: Static help file for users
    • Layout.html: The base layout that dynamically extends other templates
  • Static:
    • Script.js: The JavaScript file that allows users to encrypt and decrypt files locally

4. Program Design

4.1. Encryption Parameters

Encrypted Cloud uses the industry standard AES-256 encryption algorithm, and uses automatically generated encryption parameters such as IV and salt. AES-256 is a symmetric (which means it uses a single password) block cipher, widely considered to be one of the most trusted encryption standards today. This is why it’s the preferred encryption standard used by governments, banks, military, etc.

The application also makes use of a key derivation algorithm that uses 200,000 iterations, making it hard for attackers to try and decrypt files offline via brute force.

In order to derive keys, encrypt, and decrypt files, the program uses JavaScript’s Web Crypto API, which provides necessary encryption algorithms and functions via the browser itself.

4.2. Server Side

Encrypted Cloud is a Flask application. It starts from “app.py” file at the base directory of the application. When opening for the first time, the app auto-creates the database by using the “schema.sql” file which is also kept at the base directory. There is a separate “helper.py” file that includes additional helper functions that the app requires.

The database is SQLite3-based and is stored in “Userdata/userdata.db” file. It comes with two tables: “Users” and “Files”. The “Users” table holds the following information about each user:

  • Id: This is the primary key and integer-based unique ID for each user.
  • Name: This holds the name (title-cased) for each user.
  • Username: This holds the user’s unique username. The uniqueness is checked at the database level.
  • Passhash: The hashed user password for checking when a user tries to log in. Hashing is done at the server side using Werkzeug Security, and no plaintext password is ever saved by the server.

The “Files” table contains the following columns.

  • User_id: This column is the foreign key that references the file-owning user’s id.
  • File_id: This contains a unique, autoincrementing id for each file.
  • File_name: This contains the name of the file as saved on the server side. File names are secured before storage to protect against file-name-based attacks, such as path traversal.
  • Original_name: This stores the original file name as uploaded by the user. This is preserved so that when the files are displayed to the user, this is shown even though the file name on the disk may be secured.

And the following encryption/decryption parameters.

  • IV: Initialization vector created as part of the encryption process (more on this below).
  • Salt: Created as part of the encryption process (more on this below).
  • Iterations: Set at the client side (more on this below).

4.2.1. Templates

As part of the program, we have several HTML templates and a base template (“layout.html”) that shows the menu as well as programmatically displays “login” or “register” or “change password” and “Help” options. “Login” and “Register” options are not displayed to a logged-in user via Jinja template logic on the “layout.html” page. “Dashboard” and “Upload” routes are inaccessible without logging in, as implemented by the “needs_login()” decorator within “helper.py”.

When a user registers for the first time, “register.html” is shown where they can enter their username and password. The data is checked automatically and error messages, if any, are displayed to the user. The user needs to input a strong password during registration. The password is checked by the “strong_password()” function. After registration, the user’s details are saved to the database.

After logging in, the session details such as “session[“user_id”]” is automatically set. Here, the user is presented with the dashboard, where their uploaded files appear. Users can navigate to the “Upload” section, where they can select a file or drag and drop it. Drag and drop handler automatically changes the default behavior with “preventDefault()” and causes style change dynamically. Once a file is dropped (drop event is triggered), the file’s name is fetched and displayed inside the area.

Now, the user needs to enter a strong password and confirm it in order to initiate encryption and upload. This file password is used only during encryption and not passed to the server at any point.

Once the file is encrypted and uploaded successfully, the user is redirected back to the dashboard where they can view the file along with 3 options: “Change File Password”, “Download & Decrypt”, and “Delete”. Each option is intercepted by JavaScript on the client side. User needs to input the password before the file password can be changed or the file can be downloaded. For deletion, the user needs to confirm.

4.2.2. Database Commits

At the server side, the “open_db()” function helps open the database every time data needs to be written to it. It uses Flask’s global variable “g” to store database information throughout each request. In order to commit successful database writes or rollback unsuccessful ones, the application uses Flask’s “teardown_appcontext()” function which automatically calls “close_db()” function that either commits or rolls back database changes.

4.2.3. File Storage

When users upload files, they are automatically stored in a folder named the user’s ID under the path specified by the “FILE_FOLDER” global variable. So, a normal user folder in the current implementation may look like “/files/3/file.txt”. Files and database entries of each user are checked automatically and reconciled via the “update_files()” function. This function auto-removes database entries that do not correspond to file entries and vice versa. Because a database entry without the corresponding file and a file without the corresponding database entry are useless. Currently the functionality to create subfolders is not available.

4.3. Client Side

The process of file encryption happens at the client side, on a user’s web browser. This functionality is implemented via JavaScript through an array of functions within the “script.js” file.

After a user logs in and goes to the “Upload” page, they are presented with the option to upload a file. Only one file can be uploaded at a time, so if a user selects a file as well as drags and drops a second file, the selected file is chosen for encryption.

The file password must conform to the guidelines mentioned in the “Help” section. This is checked at client side by the “strongPassword()” function.

Now, we’ll look at each of the main functions and its parameters.

4.3.1. encryptFile()

This is the main function that helps with encrypting users’ files. It uses the Web Crypto API of JavaScript to generate the encryption key and encrypt files. As part of the encryption process, it generates the following parameters.

  • Salt: Salt is a unique randomly generated byte array using “crypto.getRandomValues();” function. This function returns cryptographically secure random values. The purpose of salt is to convert a user password to a unique encryption key. This way, even if multiple users have the same password, their encryption keys would turn out different. After attaching the salt, within the “encryptFile()” function, we generate an intermediary “passKey” that forms the input to the final encryption key, “encKey”. Note that each of those keys have their usages also defined. For instance, “passKey” can only be used to derive “encKey” and “encKey” can only be used for encryption.
  • IV: This stands for initialization vector. This is also a randomly generated byte array that is attached to the final step of encrypting the file. The purpose of IV is to ensure that the same files once encrypted return markedly different encrypted data.

Together with salt and IV, it’s ensured that two copies of the same file with the same password would give two different encrypted files. This way, an attacker wouldn’t be able to sufficiently guess the configuration of a password.

  • Iterations: This is another key concept of cryptography. Iterations is a variable that goes into the key derivation algorithm, PBKDF2, which stands for “password-based key derivation function 2”. The purpose of iterations is to make the derivation of a key as hard as possible for an attacker. When we add a high number like 200,000 in this case, the key derivation has to iterate that many times for each key. This makes it computationally infeasible for attackers who need to derive millions of keys per second in order to successfully brute-force. On the other hand, it’s not too slow for a normal user. Newer key derivation options such as Argon2 are available which also make it space-consuming for attackers, thereby thwarting modern GPU-based attack techniques.

IV, salt, and iterations are key encryption/decryption metadata that is needed for encrypting and then decrypting the files, in addition to the password itself. But these variables need not necessarily be made private, as knowing these will not help an attacker speed up their process in any way. Hence, these parameters are transferred to the server and stored within the database.

The “encryptFile()” function takes a file and its corresponding password as its inputs. It autogenerates IV and salt, and iterations are set inside it. It calls the “crypto.subtle.encrypt()” method to encrypt the file using these parameters and returns the encrypted blob and its parameters in a dictionary.

4.3.2. decryptFile()

This function simply reverses the encryption process and provides the decrypted data to the user. It takes variables such as the encrypted blob, salt, IV, and iterations as its input and simply returns the decrypted blob. It derives the decryption key and uses the “crypto.subtle.decrypt()” method for decrypting the file with the key.

4.3.3. encryptAndUpload()

This facilitator function achieves two key functionalities. It helps with the upload of a file to the server after encrypting it via “encryptFile()”. Secondly, it allows the reupload of a file after changing the file password, via the “reupload” action. For the reupload route, the input for the function comes from the “updatePassReupload()” function.

4.3.4. updatePassReupload()

This function when called, shows a Bootstrap modal (a popup box) on the “Dashboard” page, from where it fetches a new file password for the selected file. Then calls “encryptAndUpload()” to upload the file encrypted with the new password.

4.3.5. getData()

The function downloads the data from the server, when a “fileID” is passed into it. The file ID of each file is available as part of a hidden input variable on the dashboard. The function takes this variable and fetches the file via the “/download” route at the server. It returns a dictionary with all the required details such as the encrypted file itself, IV, salt, iterations, file name, and file ID. These details are fetched as response headers from the server.

4.3.6. strongPassword()

This function checks the validity of each file password given by the user. It replicates the same checks for user passwords at the server side.

4.3.7. bytesToB64 and b64ToBytes

These are twin functions that allow converting byte data to Base64 and vice versa. IV and salt are generated as bytes; they cannot be transferred to the server without such encoding. On the other side, encoded data from the server is converted back to bytes by the second function at client side in order to be useful for decryption.

4.3.8. downloadFile()

This function cleverly triggers downloading of a file at the user end by automatically creating an anchor element on the page and programmatically clicking it with the file data. It is triggered when the user clicks the download file option on the Dashboard after providing the correct password. Wrong passwords would trigger error messages. The click handler gets the file ID from the page, calls “getData()” to fetch the file and decryption metadata from the server, calls “decryptFile()” to decrypt the file, and finally calls the “downloadFile()” function to trigger the download.

5. Screenshots

And finally, here are some screenshots from the application.

Main page

User registration

Login page

Upload page

Changing file password

User password change

About

The final project for CS50X, an encrypted cloud storage project

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages