- Security
- A
Built-in security mechanisms of Python frameworks
When conducting software development process audits, we often hear that functionality is implemented in the framework, and this may raise questions from security personnel.
Introduction
When auditing software development processes, we often hear that functionality is implemented in a framework, and this can raise questions from information security specialists.
Python, being one of the popular programming languages, offers many frameworks, each of which must be secure and have built-in security mechanisms or the ability to embed these mechanisms. In this article, we will try to understand what capabilities the frameworks actually provide, examine the security mechanisms, and ways to configure them using the example of common frameworks: Django, FastAPI, and Flask.
All these frameworks provide developers with powerful tools for creating secure applications, but their approaches and implementations differ.
1. Django Security
Django is a powerful and reliable web framework in Python that includes a wide range of security mechanisms that contribute to the creation of secure web applications. One of the key features of Django compared to other frameworks is its built-in security mechanisms, which provide a range of tools to protect against the most common attacks, such as CSRF, XSS, SQL injection, and others.
Built-in Security Mechanisms
CSRF (Cross-Site Request Forgery)
Django prevents CSRF attacks by using tokens that are checked with each data modification request. The CSRF token ensures that each request to the server comes from a trusted user and a trusted source. Django automatically adds the CSRF token to each form and checks this token when receiving a POST request.
XSS (Cross-Site Scripting)
Django protects against XSS by automatically escaping all variables output in templates. This prevents malicious scripts that could be inserted through input forms from being executed in the user's browser.
SQL Injection
Django uses ORM to generate all SQL queries to the database. This isolates developers from directly writing SQL code and protects against SQL injection, as all queries are built using parameterization and checked before execution.
Password Handling and Storage
By default, Django uses hashing and salting for passwords, making it extremely difficult to recover the original password from the hash. It is recommended to use Django's built-in classes, such as User and make_password, for working with passwords.
Authentication and Authorization
Django offers a powerful authentication and authorization system that makes it easy to manage users, groups, access rights, and fine-tune access to resources. Django provides built-in tools for user authentication, such as the User class and Authentication Middleware.
Clickjacking
Protection against clickjacking in Django is implemented through the X-Frame-Options mechanism, which allows sites to specify the circumstances under which content can be loaded in a frame. This prevents attacks where an attacker could mislead a user into clicking on a hidden frame.
2. Configuring Django Security Settings
Security settings in Django are configured through the settings.py file, which is the central place for application configuration. In this file, you can configure various parameters that will affect the security of your application.
Main security parameters
SECRET_KEY
Default value: Not set by default, must be unique.
Purpose: Used for cryptographic signing, important for the security of sessions and data associated with cookies.
Note: Never place SECRET_KEY in publicly accessible sources.
DEBUG
Default value: True
Purpose: When enabled, it displays detailed error information, which can be dangerous on a production server.
Note: Always set DEBUG = False on production servers.
ALLOWED_HOSTS
Default value: []
Purpose: Defines a list of strings representing the host/domain names that this site can serve.
Note: Configure this value according to the domains on which your application should run.
SECURE_BROWSER_XSS_FILTER
Default value: False
Purpose: Enables the XSS filter in the user's browser.
Note: It is recommended to set True to protect against XSS attacks.
X_FRAME_OPTIONS
Default value: DENY
Purpose: Controls the loading of the site inside the ,
Note: DENY blocks all attempts to load pages into frames, which helps prevent clickjacking.
SECURE_SSL_REDIRECT
Default value: False
Purpose: Redirects all HTTP requests to HTTPS.
Note: Enable in production if your site operates entirely over HTTPS.
CSRF_COOKIE_SECURE
Default value: False
Purpose: Indicates that the CSRF cookie should only be transmitted over HTTPS.
Note: Enable if your site uses HTTPS.
SESSION_COOKIE_SECURE
Default value: False
Purpose: Indicates that session cookies should only be transmitted over HTTPS.
Note: Similar to CSRF_COOKIE_SECURE, enable for HTTPS sites.
Example of a configured configuration file
# settings.py
SECRET_KEY = 'your_secret_key_here'
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.ru', 'www.yourdomain.ru']
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
SECURE_SSL_REDIRECT = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
# Additional security settings
SECURE_HSTS_SECONDS = 31536000 # Enable HSTS with a duration of one year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
3. FastAPI Security
FastAPI is a modern and high-performance framework for building APIs. It does not have a built-in security system in the traditional sense. Instead, it provides the fastapi.security module, which includes a number of classes for authentication and working with API keys.
Built-in security mechanisms
Authentication and Authorization
FastAPI supports several authentication methods, including:
OAuth2: Using OAuth2PasswordBearer, the framework can manage access tokens, which is often used in modern APIs.
API Keys: Mechanisms such as APIKeyHeader, APIKeyQuery, and APIKeyCookie allow the use of API keys for simple request authentication.
Password Handling and Storage
FastAPI offers integration with libraries such as passlib and bcrypt for secure password hashing and verification. These libraries help developers easily implement reliable password hashing and subsequent verification. FastAPI itself does not have built-in tools for working with passwords but easily integrates with external libraries that provide such capabilities.
Protection against CSRF and XSS
CSRF: Since FastAPI is typically used to create APIs with authentication tokens, CSRF is not as significant a threat as in traditional web forms. However, for scenarios where cookies are used, it is recommended to use third-party solutions to add CSRF tokens.
XSS: FastAPI does not automatically handle XSS, as this relates to content generated on the client side. Developers should ensure that all user data displayed in web applications is properly sanitized and escaped.
Protection against SQL Injection
Using ORM, such as SQLAlchemy, provides protection against SQL injections. FastAPI easily integrates with such ORM, ensuring that all database queries are strictly parameterized — this prevents the injection of malicious code.
Clickjacking
To protect against clickjacking, developers can use HTTP headers such as X-Frame-Options to control the loading of pages in frames. FastAPI makes it easy to add headers through API responses or using Starlette middleware.
4. Configuring FastAPI Security Settings
FastAPI itself does not have a specialized security configuration file. Instead, security settings and configurations are defined directly in the application code.
Main Security Mechanisms
OAuth2 Authentication:
Purpose: The OAuth2PasswordBearer class is used to create a token retrieval mechanism.
Note: Ensures API security by verifying access tokens before processing user requests.
Example:
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
API Keys:
Purpose: Using APIKeyHeader, APIKeyQuery, and APIKeyCookie classes to handle API keys.
Note: Verifying API keys to access certain routes or API functions.
Example:
from fastapi import FastAPI, Security
from fastapi.security.api_key import APIKeyHeader, APIKey
app = FastAPI()
api_key_header = APIKeyHeader(name="X-API-Key")
async def get_api_key(api_key_header: str = Security(api_key_header)):
if api_key_header == "secret-key":
return "Authorized"
else:
return "Unauthorized"
@app.get("/secure-data")
async def secure_data(authorization: str = Security(get_api_key)):
return {"status": authorization}
HTTP Basic Auth:
Purpose: Using the HTTPBasic class for basic authentication.
Note: Simple user authentication using a username and password.
Example:
import secrets
from fastapi import FastAPI, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.exceptions import HTTPException
app = FastAPI()
security = HTTPBasic()
def check_credentials(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = secrets.compare_digest(credentials.username, "user")
correct_password = secrets.compare_digest(credentials.password, "password")
if not (correct_username and correct_password):
raise HTTPException(status_code=401, detail="Incorrect email or password")
return credentials
@app.get("/secure-area")
def secure_area(credentials: HTTPBasicCredentials = Depends(check_credentials)):
return {"message": "Secure area accessed", "user": credentials.username}
Example of a configured configuration file
import secrets
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, APIKeyHeader, HTTPBasic, HTTPBasicCredentials
from jose import jwt # Added for handling JWT tokens
app = FastAPI()
# Security settings
security = HTTPBasic()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
api_key_header = APIKeyHeader(name="X-API-Key")
# Authentication check functions
def check_credentials(credentials: HTTPBasicCredentials = Depends(security)):
correct_username = secrets.compare_digest(credentials.username, "user")
correct_password = secrets.compare_digest(credentials.password, "password")
if not (correct_username and correct_password):
raise HTTPException(status_code=401, detail="Incorrect email or password")
return credentials
def get_api_key(api_key: str = Depends(api_key_header)):
if api_key == "correct_key":
return api_key
else:
raise HTTPException(status_code=401, detail="Invalid API key")
# Routes
@app.post("/token")
async def login():
# Assume the function returns a JWT token after verifying credentials (omitted for simplicity)
return {"access_token": "your_token"}
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
# Return user data using the token
return {"token": token}
@app.get("/secure-area")
def secure_area(credentials: HTTPBasicCredentials = Depends(check_credentials)):
# Access to the secure area using HTTP Basic Auth
return {"message": "Secure area accessed", "user": credentials.username}
@app.get("/secure-data")
async def secure_data(api_key: str = Depends(get_api_key)):
# Access to secure data using API key
return {"message": "Access granted", "api_key": api_key}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
5. Flask Security
Flask is a lightweight web framework for Python that is widely used for building web applications and APIs. Due to its flexibility and minimalist structure, Flask provides developers with a foundation for implementing many security features, but many security aspects require manual configuration or integration with external libraries.
Built-in Security Mechanisms
Protection against CSRF (Cross-Site Request Forgery)
Flask-WTF, an extension for Flask, provides protection against CSRF. This extension automatically adds CSRF tokens to forms and validates these tokens upon receiving requests, helping to prevent unauthorized actions on behalf of authenticated users.
Protection against XSS (Cross-Site Scripting)
To combat XSS, Flask and its Jinja2 template engine automatically escape all variable outputs in templates unless otherwise specified. This prevents the execution of unwanted scripts that could be inserted into page content through user input.
Protection against SQL Injection
Using ORM, such as SQLAlchemy, prevents SQL injections as database queries are formed without directly passing parameters from the user, eliminating the possibility of executing malicious code through input data.
Authentication and Authorization
Flask-Login is a tool for managing user sessions that simplifies the authentication process. It helps developers control access to certain parts of the application by requiring users to log in.
Protection against Clickjacking
To protect against clickjacking, the X-Frame-Options header can be used. Flask-Talisman, another extension, allows easy management of security headers, including CSP (Content Security Policy), and helps combat XSS and other attack vectors.
6. Configuring Flask Security Settings
In Flask, security parameters are usually set in the configuration file config.py. Flask does not have a built-in default configuration file but offers a flexible configuration system.
Main Security Parameters
Below are the key security parameters that can be configured in Flask:
SECRET_KEY
Default value: Not set by default, must be unique.
Purpose: Used for signing session cookies and other secure operations.
Note: The key must be protected and not disclosed.
SESSION_COOKIE_SECURE
Default value: False
Purpose: Instructs the browser to use cookies only over HTTPS.
Note: Enable only if you are using HTTPS.
SESSION_COOKIE_HTTPONLY
Default value: True
Purpose: Prevents JavaScript access to cookies.
Note: Helps mitigate XSS attacks.
PERMANENT_SESSION_LIFETIME
Default value: 31 days
Purpose: Sets the lifetime of a permanent session.
Note: The value is set as a timedelta object.
REMEMBER_COOKIE_DURATION
Default value: 365 days
Purpose: Sets how long the cookie will remember the login information.
Note: When using Flask-Login.
Example of a configured configuration file
from datetime import timedelta
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key_here'
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=14)
@app.route('/')
def index():
return "Welcome to the Flask app!"
if __name__ == "__main__":
app.run(ssl_context='adhoc')
Conclusion
Python frameworks provide extensive capabilities for ensuring application security. Whether you use Django, FastAPI, or Flask, each of these frameworks has a set of tools that help protect against the most common threats in built-in or integrable ways. However, it is important to understand that no tool or framework can provide complete security on its own. Applying best practices and using proven libraries play a key role in enhancing the security of your applications. In summary, application security depends not only on the chosen framework but also on your attention to security details at every stage of development.
Author: Artem Porfiryev, Senior Application Security Engineer at UCSB
Write comment