Comment implémenter l'authentification dans un système client-serveur Python

PythonPythonBeginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Implementing secure authentication is a crucial aspect of building robust Python client-server systems. This tutorial will guide you through the process of implementing authentication in your Python applications, starting with basic username and password authentication and moving to token-based methods. By the end of this lab, you will understand how to secure your Python applications with proper authentication mechanisms.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ErrorandExceptionHandlingGroup(["Error and Exception Handling"]) python(("Python")) -.-> python/NetworkingGroup(["Networking"]) python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/DataStructuresGroup(["Data Structures"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python/BasicConceptsGroup -.-> python/type_conversion("Type Conversion") python/ControlFlowGroup -.-> python/conditional_statements("Conditional Statements") python/DataStructuresGroup -.-> python/dictionaries("Dictionaries") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ModulesandPackagesGroup -.-> python/using_packages("Using Packages") python/ErrorandExceptionHandlingGroup -.-> python/catching_exceptions("Catching Exceptions") python/NetworkingGroup -.-> python/http_requests("HTTP Requests") subgraph Lab Skills python/type_conversion -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} python/conditional_statements -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} python/dictionaries -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} python/function_definition -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} python/using_packages -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} python/catching_exceptions -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} python/http_requests -.-> lab-398021{{"Comment implémenter l'authentification dans un système client-serveur Python"}} end

Understanding Authentication Basics and Setting Up Your Environment

Authentication is the process of verifying the identity of a user or system before granting access to protected resources. In client-server systems, proper authentication ensures that only authorized users can access sensitive data or perform certain operations.

Setting Up Your Environment

Let's start by setting up our working environment. First, create a new directory for our project:

mkdir -p ~/project/auth_demo
cd ~/project/auth_demo

Next, we need to install the necessary Python packages that we'll use throughout this lab:

pip install flask flask-login requests

You should see output similar to this:

Collecting flask
  Downloading Flask-2.2.3-py3-none-any.whl (101 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.8/101.8 KB 6.2 MB/s eta 0:00:00
Collecting flask-login
  Downloading Flask_Login-0.6.2-py3-none-any.whl (17 kB)
Collecting requests
  Downloading requests-2.29.0-py3-none-any.whl (62 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.5/62.5 KB 5.5 MB/s eta 0:00:00
...
Successfully installed flask-2.2.3 flask-login-0.6.2 requests-2.29.0 ...

Authentication Concepts

Before diving into implementation, let's understand the basic authentication concepts:

  1. Username and Password Authentication: The most common form where users provide credentials to verify their identity.

  2. Token-based Authentication: After successful login, the server generates a token for the client to use in subsequent requests, avoiding the need to send credentials each time.

  3. Session-based Authentication: The server stores session information after a successful login and provides a session ID to the client.

Let's visualize a basic authentication flow:

Client                                Server
  |                                     |
  |--- Login Request (credentials) ---->|
  |                                     |--- Verify Credentials
  |                                     |
  |<---- Authentication Response -------|
  |                                     |
  |--- Subsequent Requests (with auth) >|
  |                                     |--- Verify Auth
  |                                     |
  |<---- Protected Resource Response ---|

Now that we understand the basics and have set up our environment, let's create a simple file structure for our project:

touch ~/project/auth_demo/server.py
touch ~/project/auth_demo/client.py

In the next step, we'll implement a basic username and password authentication system using Flask on the server side and a Python client to authenticate with it.

Implementing Basic Username and Password Authentication

In this step, we'll implement a basic authentication system that uses usernames and passwords. We'll create:

  1. A Flask server that handles authentication
  2. A Python client that authenticates with the server

Creating the Server

Let's implement our Flask server with basic authentication capabilities. Open the server.py file in the VSCode editor:

code ~/project/auth_demo/server.py

Now add the following code to implement a simple authentication server:

from flask import Flask, request, jsonify, session
import os

app = Flask(__name__)
## Generate a random secret key for session management
app.secret_key = os.urandom(24)

## Simple in-memory user database
users = {
    "alice": "password123",
    "bob": "qwerty456"
}

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    ## Extract username and password from the request
    username = data.get('username')
    password = data.get('password')

    ## Check if the user exists and the password is correct
    if username in users and users[username] == password:
        session['logged_in'] = True
        session['username'] = username
        return jsonify({"status": "success", "message": f"Welcome {username}!"})
    else:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected():
    ## Check if the user is logged in
    if session.get('logged_in'):
        return jsonify({
            "status": "success",
            "message": f"You are viewing protected content, {session.get('username')}!"
        })
    else:
        return jsonify({"status": "error", "message": "Authentication required"}), 401

@app.route('/logout', methods=['POST'])
def logout():
    ## Remove session data
    session.pop('logged_in', None)
    session.pop('username', None)
    return jsonify({"status": "success", "message": "Logged out successfully"})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

This server:

  • Uses a simple in-memory dictionary to store usernames and passwords
  • Provides endpoints for login, accessing protected content, and logout
  • Uses Flask's session management for maintaining authentication state

Creating the Client

Now, let's create a client that can authenticate with our server. Open the client.py file:

code ~/project/auth_demo/client.py

Add the following code:

import requests
import json

## Server URL
BASE_URL = "http://localhost:5000"

def login(username, password):
    """Authenticate with the server using username and password"""
    response = requests.post(
        f"{BASE_URL}/login",
        json={"username": username, "password": password}
    )

    print(f"Login response: {response.status_code}")
    print(response.json())

    ## Return the session cookies if login was successful
    return response.cookies if response.status_code == 200 else None

def access_protected(cookies=None):
    """Access a protected resource"""
    response = requests.get(f"{BASE_URL}/protected", cookies=cookies)

    print(f"Protected resource response: {response.status_code}")
    print(response.json())

    return response

def logout(cookies=None):
    """Logout from the server"""
    response = requests.post(f"{BASE_URL}/logout", cookies=cookies)

    print(f"Logout response: {response.status_code}")
    print(response.json())

    return response

if __name__ == "__main__":
    ## Test with valid credentials
    print("=== Authenticating with valid credentials ===")
    cookies = login("alice", "password123")

    if cookies:
        print("\n=== Accessing protected resource ===")
        access_protected(cookies)

        print("\n=== Logging out ===")
        logout(cookies)

    ## Test with invalid credentials
    print("\n=== Authenticating with invalid credentials ===")
    login("alice", "wrongpassword")

    ## Try to access protected resource without authentication
    print("\n=== Accessing protected resource without authentication ===")
    access_protected()

This client:

  • Provides functions to login, access a protected resource, and logout
  • Handles cookies to maintain the session between requests
  • Tests both valid and invalid credentials

Running the Authentication System

To see our authentication system in action, we'll need to run both the server and client. First, let's start the server:

python ~/project/auth_demo/server.py

You should see output similar to this:

 * Serving Flask app 'server'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000

Now, open a new terminal tab by clicking the "+" button next to the terminal, and run the client:

cd ~/project/auth_demo
python client.py

You should see output demonstrating:

  1. Successful login with valid credentials
  2. Access to protected resources with valid authentication
  3. Successful logout
  4. Failed login attempt with incorrect credentials
  5. Failed attempt to access protected resources without authentication

This output confirms that our basic username and password authentication system is working correctly.

In the next step, we'll enhance this system by implementing token-based authentication for improved security.

Implementing Token-Based Authentication

In the previous step, we created a basic authentication system using sessions. While this works for simple applications, token-based authentication offers several advantages, particularly for APIs and distributed systems.

In token-based authentication:

  • The server generates a token after successful authentication
  • The client stores and sends this token with subsequent requests
  • The server validates the token instead of checking credentials each time

Let's upgrade our system to use JSON Web Tokens (JWT), a popular standard for token-based authentication.

Installing Required Packages

First, we need to install the PyJWT package:

pip install pyjwt

You should see output confirming the installation:

Collecting pyjwt
  Downloading PyJWT-2.6.0-py3-none-any.whl (20 kB)
Installing collected packages: pyjwt
Successfully installed pyjwt-2.6.0

Updating the Server for Token-Based Authentication

Let's modify our server to use JWT tokens instead of sessions. Update the server.py file:

code ~/project/auth_demo/server.py

Replace the existing code with:

from flask import Flask, request, jsonify
import jwt
import datetime
import os

app = Flask(__name__)
## Secret key for signing JWT tokens
SECRET_KEY = os.urandom(24)

## Simple in-memory user database
users = {
    "alice": "password123",
    "bob": "qwerty456"
}

def generate_token(username):
    """Generate a JWT token for the authenticated user"""
    ## Token expires after 30 minutes
    expiration = datetime.datetime.utcnow() + datetime.timedelta(minutes=30)

    payload = {
        'username': username,
        'exp': expiration
    }

    ## Create the JWT token
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token

def verify_token(token):
    """Verify the JWT token"""
    try:
        ## Decode and verify the token
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload
    except jwt.ExpiredSignatureError:
        ## Token has expired
        return None
    except jwt.InvalidTokenError:
        ## Invalid token
        return None

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    ## Extract username and password from the request
    username = data.get('username')
    password = data.get('password')

    ## Check if the user exists and the password is correct
    if username in users and users[username] == password:
        ## Generate a JWT token
        token = generate_token(username)

        return jsonify({
            "status": "success",
            "message": f"Welcome {username}!",
            "token": token
        })
    else:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected():
    ## Get the token from the Authorization header
    auth_header = request.headers.get('Authorization')

    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({"status": "error", "message": "Missing or invalid token"}), 401

    ## Extract the token
    token = auth_header.split(' ')[1]

    ## Verify the token
    payload = verify_token(token)

    if payload:
        return jsonify({
            "status": "success",
            "message": f"You are viewing protected content, {payload['username']}!"
        })
    else:
        return jsonify({"status": "error", "message": "Invalid or expired token"}), 401

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

This updated server:

  • Generates JWT tokens upon successful authentication
  • Validates tokens for protected resources
  • Has endpoints for login and accessing protected content

Updating the Client for Token-Based Authentication

Now, let's update our client to use the JWT tokens. Update the client.py file:

code ~/project/auth_demo/client.py

Replace the existing code with:

import requests
import json

## Server URL
BASE_URL = "http://localhost:5000"

def login(username, password):
    """Authenticate with the server using username and password"""
    response = requests.post(
        f"{BASE_URL}/login",
        json={"username": username, "password": password}
    )

    print(f"Login response: {response.status_code}")
    data = response.json()
    print(data)

    ## Return the token if login was successful
    return data.get('token') if response.status_code == 200 else None

def access_protected(token=None):
    """Access a protected resource using JWT token"""
    headers = {}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    response = requests.get(f"{BASE_URL}/protected", headers=headers)

    print(f"Protected resource response: {response.status_code}")
    print(response.json())

    return response

if __name__ == "__main__":
    ## Test with valid credentials
    print("=== Authenticating with valid credentials ===")
    token = login("alice", "password123")

    if token:
        print("\n=== Accessing protected resource with valid token ===")
        access_protected(token)

    ## Test with invalid credentials
    print("\n=== Authenticating with invalid credentials ===")
    login("alice", "wrongpassword")

    ## Try to access protected resource without token
    print("\n=== Accessing protected resource without token ===")
    access_protected()

    ## Try to access protected resource with invalid token
    print("\n=== Accessing protected resource with invalid token ===")
    access_protected("invalid.token.value")

This updated client:

  • Retrieves and stores the JWT token after successful authentication
  • Sends the token in the Authorization header for accessing protected resources
  • Tests various scenarios including valid and invalid authentication attempts

Running the Token-Based Authentication System

Now let's run our updated token-based authentication system. First, stop any running server processes with Ctrl+C, then start the updated server:

python ~/project/auth_demo/server.py

Open a new terminal tab or use the existing second tab to run the client:

cd ~/project/auth_demo
python client.py

You should see output demonstrating:

  1. Successful login with valid credentials, receiving a JWT token
  2. Access to protected resources with a valid token
  3. Failed login attempt with incorrect credentials
  4. Failed attempt to access protected resources without a token
  5. Failed attempt to access protected resources with an invalid token

This output confirms that our token-based authentication system is working correctly.

Advantages of Token-Based Authentication

Token-based authentication offers several advantages over session-based authentication:

  1. Stateless: The server doesn't need to store session information.
  2. Scalability: Works well in distributed environments with multiple servers.
  3. Mobile-friendly: Suitable for mobile applications where cookies might not work well.
  4. Cross-domain: Can be used across different domains easily.
  5. Security: Tokens can be configured to expire, reducing the risk of session hijacking.

In real-world applications, you might want to further enhance your authentication system with features like token refreshing, role-based access control, and secure token storage.

Enhancing Security with Password Hashing

In our previous implementations, we stored passwords in plain text, which is a significant security risk. In this step, we'll enhance our authentication system by implementing password hashing using the bcrypt algorithm.

Why Hash Passwords?

Storing passwords in plain text poses several security risks:

  • If your database is compromised, attackers can immediately use the passwords
  • Users often reuse passwords across multiple sites, so a breach can affect other services
  • It violates security best practices and may breach data protection regulations

Hashing passwords solves these problems by:

  • Converting passwords into fixed-length strings that can't be reversed
  • Adding a "salt" to prevent attackers from using precomputed lookup tables (rainbow tables)
  • Ensuring that even if two users have the same password, their hashes will be different

Installing Required Packages

First, we need to install the bcrypt package:

pip install bcrypt

You should see output confirming the installation:

Collecting bcrypt
  Downloading bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl (593 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 593.8/593.8 KB 10.5 MB/s eta 0:00:00
Installing collected packages: bcrypt
Successfully installed bcrypt-4.0.1

Creating a User Registration System

Let's enhance our server to include user registration with password hashing. Update the server.py file:

code ~/project/auth_demo/server.py

Replace the existing code with:

from flask import Flask, request, jsonify
import jwt
import datetime
import os
import bcrypt

app = Flask(__name__)
## Secret key for signing JWT tokens
SECRET_KEY = os.urandom(24)

## Store users as a dictionary with username as key and a dict of hashed password and roles as value
users = {}

def hash_password(password):
    """Hash a password using bcrypt"""
    ## Convert password to bytes if it's a string
    if isinstance(password, str):
        password = password.encode('utf-8')

    ## Generate a salt and hash the password
    salt = bcrypt.gensalt()
    hashed = bcrypt.hashpw(password, salt)

    return hashed

def check_password(password, hashed):
    """Verify a password against its hash"""
    ## Convert password to bytes if it's a string
    if isinstance(password, str):
        password = password.encode('utf-8')

    ## Check if the password matches the hash
    return bcrypt.checkpw(password, hashed)

def generate_token(username, role):
    """Generate a JWT token for the authenticated user"""
    ## Token expires after 30 minutes
    expiration = datetime.datetime.utcnow() + datetime.timedelta(minutes=30)

    payload = {
        'username': username,
        'role': role,
        'exp': expiration
    }

    ## Create the JWT token
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token

def verify_token(token):
    """Verify the JWT token"""
    try:
        ## Decode and verify the token
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload
    except jwt.ExpiredSignatureError:
        ## Token has expired
        return None
    except jwt.InvalidTokenError:
        ## Invalid token
        return None

@app.route('/register', methods=['POST'])
def register():
    data = request.get_json()

    ## Extract registration data
    username = data.get('username')
    password = data.get('password')

    ## Basic validation
    if not username or not password:
        return jsonify({"status": "error", "message": "Username and password are required"}), 400

    ## Check if the username already exists
    if username in users:
        return jsonify({"status": "error", "message": "Username already exists"}), 409

    ## Hash the password and store the user
    hashed_password = hash_password(password)
    users[username] = {
        'password': hashed_password,
        'role': 'user'  ## Default role
    }

    return jsonify({
        "status": "success",
        "message": f"User {username} registered successfully"
    })

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    ## Extract username and password from the request
    username = data.get('username')
    password = data.get('password')

    ## Check if the user exists
    if username not in users:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

    ## Check if the password is correct
    if check_password(password, users[username]['password']):
        ## Generate a JWT token
        token = generate_token(username, users[username]['role'])

        return jsonify({
            "status": "success",
            "message": f"Welcome {username}!",
            "token": token
        })
    else:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected():
    ## Get the token from the Authorization header
    auth_header = request.headers.get('Authorization')

    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({"status": "error", "message": "Missing or invalid token"}), 401

    ## Extract the token
    token = auth_header.split(' ')[1]

    ## Verify the token
    payload = verify_token(token)

    if payload:
        return jsonify({
            "status": "success",
            "message": f"You are viewing protected content, {payload['username']}!",
            "role": payload['role']
        })
    else:
        return jsonify({"status": "error", "message": "Invalid or expired token"}), 401

@app.route('/admin', methods=['GET'])
def admin():
    ## Get the token from the Authorization header
    auth_header = request.headers.get('Authorization')

    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({"status": "error", "message": "Missing or invalid token"}), 401

    ## Extract the token
    token = auth_header.split(' ')[1]

    ## Verify the token
    payload = verify_token(token)

    if not payload:
        return jsonify({"status": "error", "message": "Invalid or expired token"}), 401

    ## Check if user has admin role
    if payload['role'] != 'admin':
        return jsonify({"status": "error", "message": "Admin access required"}), 403

    return jsonify({
        "status": "success",
        "message": f"Welcome to the admin panel, {payload['username']}!"
    })

## Add an admin user for testing
admin_password = hash_password("admin123")
users["admin"] = {
    'password': admin_password,
    'role': 'admin'
}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

This enhanced server:

  • Uses bcrypt for secure password hashing
  • Includes a user registration endpoint
  • Adds role-based access control (admin vs regular users)
  • Pre-creates an admin user for testing

Updating the Client

Now let's update our client to test these new features. Update the client.py file:

code ~/project/auth_demo/client.py

Replace the existing code with:

import requests
import json

## Server URL
BASE_URL = "http://localhost:5000"

def register(username, password):
    """Register a new user"""
    response = requests.post(
        f"{BASE_URL}/register",
        json={"username": username, "password": password}
    )

    print(f"Registration response: {response.status_code}")
    print(response.json())

    return response.status_code == 200

def login(username, password):
    """Authenticate with the server using username and password"""
    response = requests.post(
        f"{BASE_URL}/login",
        json={"username": username, "password": password}
    )

    print(f"Login response: {response.status_code}")
    data = response.json()
    print(data)

    ## Return the token if login was successful
    return data.get('token') if response.status_code == 200 else None

def access_protected(token=None):
    """Access a protected resource using JWT token"""
    headers = {}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    response = requests.get(f"{BASE_URL}/protected", headers=headers)

    print(f"Protected resource response: {response.status_code}")
    print(response.json())

    return response

def access_admin(token=None):
    """Access the admin panel using JWT token"""
    headers = {}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    response = requests.get(f"{BASE_URL}/admin", headers=headers)

    print(f"Admin panel response: {response.status_code}")
    print(response.json())

    return response

if __name__ == "__main__":
    ## Test user registration
    print("=== Registering a new user ===")
    register("testuser", "testpass123")

    ## Try to register with the same username (should fail)
    print("\n=== Registering with existing username ===")
    register("testuser", "differentpass")

    ## Test regular user login and access
    print("\n=== Regular user login ===")
    user_token = login("testuser", "testpass123")

    if user_token:
        print("\n=== Regular user accessing protected resource ===")
        access_protected(user_token)

        print("\n=== Regular user trying to access admin panel ===")
        access_admin(user_token)

    ## Test admin login and access
    print("\n=== Admin login ===")
    admin_token = login("admin", "admin123")

    if admin_token:
        print("\n=== Admin accessing protected resource ===")
        access_protected(admin_token)

        print("\n=== Admin accessing admin panel ===")
        access_admin(admin_token)

This updated client:

  • Tests user registration functionality
  • Demonstrates role-based access control
  • Tests both regular user and admin authentication

Running the Enhanced Authentication System

Now let's run our enhanced authentication system. First, stop any running server processes with Ctrl+C, then start the updated server:

python ~/project/auth_demo/server.py

Open a new terminal tab or use the existing second tab to run the client:

cd ~/project/auth_demo
python client.py

You should see output demonstrating:

  1. Successful user registration
  2. Failed registration with an existing username
  3. Regular user login and access to protected resources
  4. Failed attempt by a regular user to access the admin panel
  5. Admin login and access to both protected resources and the admin panel

This output confirms that our enhanced authentication system with password hashing and role-based access control is working correctly.

Security Advantages

Our enhanced system now provides:

  1. Secure Password Storage: Passwords are hashed using bcrypt, a slow hashing function designed to resist brute-force attacks.
  2. Role-Based Access Control: Different users can have different access levels based on their roles.
  3. Token-Based Authentication: Continues to provide the benefits of JWT tokens we implemented earlier.
  4. User Registration: Allows for dynamic user creation with secure password storage.

These enhancements make our authentication system much more robust and suitable for real-world applications.

Summary

In this lab, you've learned how to implement authentication in a Python client-server system, from basic concepts to advanced techniques. You've successfully:

  1. Understood the fundamental concepts of authentication in client-server systems
  2. Implemented basic username and password authentication using Flask sessions
  3. Enhanced security with token-based authentication using JWT
  4. Added secure password hashing with bcrypt
  5. Implemented role-based access control for different user types

These skills provide a solid foundation for building secure Python applications. Remember that authentication is just one aspect of application security - in production systems, you should also consider:

  • Using HTTPS for all communications
  • Implementing rate limiting to prevent brute-force attacks
  • Adding logging and monitoring for suspicious activities
  • Keeping all dependencies updated to address security vulnerabilities

By applying these principles, you can build Python applications that effectively protect user data and maintain secure operations.