Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Building a Secure Flask Website with Role-Based Access Control and Encryption

Learn how to enhance a Flask website with role-based access control and encryption for data confidentiality, using Python, SQLite3, and cryptography libraries.

Flask security role-based access control encryption Flask data confidentiality Flask RBAC tutorial secure web app Python Flask cryptography SQLite encryption COP4521 assignment hardened Flask website Flask login system Flask user roles Python web security encrypt database fields Flask authentication Flask authorization

Introduction to Enhanced Flask Security

In today's web development landscape, security is paramount. With data breaches making headlines—from gaming platforms to AI apps—developers must harden their applications. This tutorial guides you through enhancing a basic Flask website with role-based access control (RBAC) and encryption for data confidentiality, similar to what's required in COP4521 Assignment 6. By the end, you'll understand how to protect user data and restrict access based on roles.

Setting Up Your Flask Project

Start with the Flask website from a previous assignment. Ensure you have Python, Flask, SQLite3, and a cryptography library like cryptography installed. Create a new directory for your hardened site and copy your existing files.

pip install flask cryptography

Initialize your database with a user table that includes a SecurityLevel column (1, 2, or 3). For example:

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT UNIQUE NOT NULL,
    password TEXT NOT NULL,
    name TEXT,
    phnum TEXT,
    security_level INTEGER NOT NULL
);

Implementing Role-Based Access Control

RBAC ensures users only see pages they're authorized to access. First, create a login page at /login with username and password fields. When the user submits, validate credentials against the database. If successful, store the user's role in the session.

from flask import session, redirect, request, render_template

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = validate_user(username, password)
        if user:
            session['user_id'] = user['id']
            session['security_level'] = user['security_level']
            return redirect('/home')
        else:
            return render_template('login.html', error='Invalid username and/or password!')
    return render_template('login.html')

Now, protect routes based on SecurityLevel. Create a decorator or check in each route:

def required_security_level(level):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if 'user_id' not in session:
                return redirect('/login')
            if session.get('security_level', 0) < level:
                return 'Page not found', 404
            return f(*args, **kwargs)
        return decorated_function
    return decorator

Then apply to routes. For example, only SecurityLevel 3 users can access the 'Add User' page:

@app.route('/add_user')
@required_security_level(3)
def add_user():
    # ...

Encrypting Sensitive Fields

To achieve data confidentiality, encrypt fields like name, phnum, and password before storing in SQLite. Use a symmetric encryption algorithm like AES via the cryptography library. Generate a key and store it securely (e.g., in environment variables).

from cryptography.fernet import Fernet

key = Fernet.generate_key()
cipher = Fernet(key)

# Encrypt before insert
encrypted_name = cipher.encrypt(name.encode())
encrypted_phnum = cipher.encrypt(phnum.encode())
encrypted_password = cipher.encrypt(password.encode())

When reading data, decrypt:

decrypted_name = cipher.decrypt(encrypted_name).decode()

Modify your login logic to encrypt the input password before comparing with the stored encrypted password. Similarly, encrypt the username if it's stored encrypted (though the assignment only requires encrypting specific fields).

Building the Home Page with Dynamic Links

Based on the user's SecurityLevel, display different links. In your template:

<h2>Welcome, {{ session['username'] }}</h2>
<ul>
    <li><a href='/show_results'>Show my Contest Entry Results</a></li>
    {% if session['security_level'] >= 2 %}
        <li><a href='/list_users'>List Baking Contest Users</a></li>
    {% endif %}
    {% if session['security_level'] == 3 %}
        <li><a href='/add_user'>Add new Baking Contest User</a></li>
        <li><a href='/entries_results'>Baking Contest Entry Results</a></li>
    {% endif %}
    <li><a href='/logout'>Logout</a></li>
</ul>

Adding a Logout Link

Implement logout by clearing the session:

@app.route('/logout')
def logout():
    session.clear()
    return redirect('/login')

Handling Unauthorized Access

If a user tries to access a page without proper permissions, redirect to login or show 'Page not found'. Use the decorator or check in each route.

Input Validation for Contest Entry

When adding a contest entry, validate inputs: name not empty, votes as non-negative integers. Use Flask's request.form and return errors if invalid.

def validate_entry(name, excellent, ok, bad):
    errors = []
    if not name or name.strip() == '':
        errors.append('Name of Baking Item is required.')
    if not excellent.isdigit() or int(excellent) < 0:
        errors.append('Excellent votes must be a non-negative integer.')
    # similar for ok and bad
    return errors

Testing with Different User Roles

Create test users with SecurityLevel 1, 2, and 3. For example:

  • User1 (level 1): can only see own results and add entries.
  • User2 (level 2): can also list users.
  • Admin (level 3): has full access.

Log in as each and verify the home page links match the requirements.

Encryption Considerations

Encrypting fields like name and phone number protects data at rest. However, note that encrypting the password means you cannot use SQL's SELECT with plain text. You must encrypt the input before querying. Also, the key must be kept secret. In production, use environment variables or a key management system.

Conclusion

By following this tutorial, you've enhanced your Flask website with RBAC and encryption, aligning with modern security practices. These skills are crucial for developing secure web applications, whether for a baking contest or a fintech app. Remember to test thoroughly and keep your encryption keys safe.