Skip to content

Python Security Essentials for Your Apps

In July 2020, Twitter faced a serious security breach where high-profile accounts were compromised, leading to unauthorized tweets soliciting Bitcoin. This incident highlighted the vulnerability of online platforms to cyberattacks and underscored the urgent need for robust security measures in today’s digital landscape.

Safeguarding applications against malicious actors is essential. Whether running a small e-commerce site or a large-scale social media platform, prioritizing security is non-negotiable. In this article, we’ll explore Python security essentials, equipping you with the tools and knowledge needed to strengthen your applications and defend against potential breaches.

In this blog post, we’ll discuss how developers can fortify their e-commerce platforms against malicious actors.

First, we’ll explore Python’s built-in security features and libraries to bolster protection against password vulnerabilities, MIME sniffing, clickjacking attacks, SQL injections, and more.

Next, we’ll introduce key third-party tools such as Cloudinary, OWASP Dependency-Check, and Bandit, which automate crucial security tasks that can be challenging to manage manually. These tools handle file security, dependency vulnerability checks, and code base vulnerability scanning, ensuring your app remains protected against potential attacks.

Finally, we’ll highlight some of those coding best practices that should be part of your routine.

Your first line of defense for security in your Python app is the built-in security features and middleware provided by your Django (or Flask) framework. This functionality goes a long way in addressing OWASP’s (Open Web Application Security Project) top ten vulnerabilities. Here are some measures easily accessible in the Django framework:

Django provides tools to ensure robust password security. This includes features such as checking that the password isn’t too similar to the user’s attributes, setting a minimum length (default is 8 characters), blocking commonly used passwords, and verifying that the password isn’t entirely numeric. Additionally, you can tailor validation rules to include, for example, a combination of characters, digits, and symbols for enhanced security.

To make sure these validations are implemented in your app, and then change the default minimum length to 12 characters:

  1. In your project’s settings.py file, make sure the built-in validators are listed under the AUTH_PASSWORD_VALIDATORS setting.
AUTH_PASSWORD_VALIDATORS = [
   {
       'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
   },
]
Code language: JavaScript (javascript)
  1. Add ‘OPTIONS’: {‘min_length’: 12}, to the ‘django.contrib.auth.password_validation.MinimumLengthValidator’ validator:
AUTH_PASSWORD_VALIDATORS = [
   {
       'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
       'OPTIONS': {'min_length': 12},
   },
   {
       'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
   },
]
Code language: JavaScript (javascript)
  1. Save the settings.py file and restart your Django development server to apply the changes.

For customizing additional validation rules, refer to the documentation.

Django’s middleware offers a suite of security measures, including:

  • SecurityMiddleware. Sets default security headers to prevent various code injection attacks. Headers like ‘X-Content-Type-Options’ prevent MIME sniffing attacks by instructing the browser not to guess file types based on content. This prevents exploitation by disguising a malicious script as an innocent image file.

  • CsrfViewMiddleware. Mitigates CSRF attacks by verifying form submissions originate from your application, thus preventing malicious requests when users are tricked into submitting them through deceptive links or emails. Include the CSRF token in your Django templates using the {% csrf_token %} template tag.

  • XFrameOptionsMiddleware. Protects against clickjacking attacks by blocking your webpage from being embedded in frames or iframes. This ensures that malicious interactive elements can’t be concealed to deceive users into performing actions that expose sensitive data or functionalities.

  • AuthenticationMiddleware. Controls access based on user authentication.

By implementing these built-in security features and middleware, you can significantly enhance the security of your Python web application against common vulnerabilities.

To integrate these middleware into your Django app:

  1. In your project’s settings.py file, make sure these features are included under the MIDDLEWARE setting.
MIDDLEWARE = [
 ...
 'django.middleware.security.SecurityMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 ...
]
Code language: JavaScript (javascript)
  1. Add the `{% csrf_token %}` to your forms in your HTML:
   {% block body %}
     <div id='backend_upload'>
       <form action="{% url "upload" %}" method="post" enctype="multipart/form-data">
         {% csrf_token %}
         {{ backend_form }}
         <input type="submit" value="Upload">
       </form>
     </div> 
   {% endblock %}
Code language: HTML, XML (xml)
  1. Refer to the appendix at the end of this blog for instructions on setting up signup, login, and logout using Django’s authentication middleware.
  1. Authorize actions exclusively for authenticated users in views.py:
# views.py

from django.shortcuts import render

def my_view(request):
    if request.user.is_authenticated:
        # User is authenticated, perform user-specific actions
        username = request.user.username
        # Other user-related logic
    else:
        # User is not authenticated, handle anonymous user case
        pass
    return render(request, 'my_template.html')
Code language: PHP (php)
  1. Authorize actions exclusively for authenticated users in templates like my_template.html: Use template variables like {{ user }}, {{ user.username }}, or {% if user.is_authenticated %} to display user-specific content or customize the user interface based on the user’s authentication status.
<!-- my_template.html -->


{% if user.is_authenticated %}
 <p>Welcome, {{ user.username }}!</p>
 <!-- Display user-specific content -->
{% else %}
 <p>Welcome, Guest!</p>
 <!-- Display content for anonymous users -->
{% endif %}
Code language: HTML, XML (xml)

An ORM serves as a protective layer for your data interactions with the database, ensuring both data integrity and security. By automating parameterized queries, which separate SQL code from user input, ORMs effectively prevent SQL injection attacks. ORMs bolster security by performing data validation, including checks on string lengths and integer ranges, thus reducing potential vulnerabilities.

If you’ve run migrations using the manage.py command (e.g., python manage.py makemigrations and python manage.py migrate), then you’re using Django ORM to manage your database schema.

Here’s some code that illustrates how the Django ORM enforces SQL security:

# upload_app/models.py
from django.db import models

class Photo(models.Model):
   image = models.ImageField(upload_to='images/') # Secure file uploads
   transformed_image = models.URLField(blank=True, null=True) # Secure URL storage

# Example of parameterized query (data insertion)
@classmethod
def create_photo(cls, image_path):
   photo = cls(image=image_path) # Creating a new Photo instance with parameterized data
   photo.save() # Saving the instance to the database
   return photo

# Example of data validation (ensuring image size doesn't exceed a certain limit)
def save(self, *args, **kwargs):
   # Check if the image file size is within a specific limit (e.g., 10MB)
   if self.image.size > 10 * 1024 * 1024: # 10MB limit raise ValueError("Image size exceeds the maximum allowed limit.")
   super().save(*args, **kwargs) # Call the parent class's save method to save the object to the database

Achieving comprehensive security may involve complex efforts that can be challenging to implement independently. This is where third-party tools come into play, offering automation for key security processes. Let’s take a look at three third-party tools that can help you keep your apps secure: Cloudinary for file security, OWASP Dependency-Check for scanning dependencies, and Bandit for identifying vulnerabilities in your Python code.

Allowing file uploads to your website can expose it to security risks, including unauthorized access and data breaches. Attackers may exploit vulnerabilities in the upload process to introduce malicious files or execute harmful scripts. Cloudinary, a comprehensive platform for image and video management, offers a range of file-handling features, including robust security measures that address these risks. Cloudinary checks your files and user uploads to stop any viruses or harmful code from reaching your web and mobile viewers, ensuring compliance with security standards by thoroughly inspecting for malware and validating files before they’re accepted.

Additionally, Cloudinary’s moderation tools can assess uploaded images to ensure appropriate content, maintaining a safe and compliant environment by detecting and filtering out potentially offensive material before it’s publicly accessible, promoting a safe and user-friendly experience for website visitors.

Explore Cloudinary’s offerings in its documentation.

Note: Moderation tools in Cloudinary are available as add-ons, with usage limits determined by your subscription plan.

To implement file security with Cloudinary:

  1. Sign up for an account and obtain your API credentials. Watch this quick video to install and configure Cloudinary in your Python app.
  2. Visit the Add-on page of the Cloudinary Console Settings, and register for the Rekognition Moderation AI and Perception Point addons.
  3. Adapt and integrate the provided code as needed to ensure robust file security.
import cloudinary.uploader
import cloudinary.utils

# Configure Cloudinary with your credentials
cloudinary.config(
    cloud_name = 'your_cloud_name',
    api_key = 'your_api_key',
    api_secret = 'your_api_secret'
)

# Upload a file securely using HTTPS, name it new_image, and automatically run it through moderation to block inappropriate and malicious files from being uploaded.

upload_result = cloudinary.uploader.upload('path/to/your/image.jpg', public_id='new_image', secure=True, moderation=’aws_rek|perception_point')

if result['moderation'][0].get('status') == 'approved'
    print('Secure upload successful:', 
       upload_result['secure_url'])
    # Display the image in an HTML template.
else:
    print('The image failed moderation'])
Code language: PHP (php)

You could be introducing risks to your project, unbeknownst to you, via your project dependencies. OWASP Dependency-Check automates the identification of vulnerabilities within these dependencies. This significantly enhances your application’s security, enabling early detection and prompt resolution of vulnerabilities during the development process.

Here’s how to install OWASP Dependency-Check and scan your project:

  1. Go to the OWASP DependencyCheck repo on GitHub.
  2. Scroll down to Releases and click the latest release to download the ZIP file.
  3. Unzip the file from your terminal by entering:unzip ~Downloads/dependency-check-<version_number>-release.zip
  4. Change to the dependency-check/bin directory by entering cd dependency-check/bin.
  5. View the files in the directory by entering ls. You should see dependency-check.bat and dependency-check.sh.
  6. Enter pwd to obtain the file path.

Here’s how to scan your project:

  1. Change directories so that you’re in the directory of the project you want to scan.
  2. In the command prompt, enter <dependency-check-path>/dependency-check.sh –scan <project_name> Note: If it’s the first time you’re running Dependency-Check, it might take a while while the list of all vulnerabilities is being downloaded.
  3. When the report is generated, view it as a web page by entering firefox dependency-check-report.html. The report provides a summary showing your project files and all the third-party vulnerabilities found along with their severity. Scroll down to drill down on all the vulnerabilities and advice on what to do next.

Dealing with every security flaw in your codebase manually can be overwhelming. Instead, Bandit offers automated vulnerability detection for identifying potential security issues in your code.

To scan your project with Bandit:

  1. Install Bandit using pip:
pip install bandit
  1. Navigate to the directory containing your Python code and simply run the following command to scan all the files in your project, including low-severity issues:
bandit -r . -ll

Go through the list of vulnerabilities found and fix them in your code.

In addition to relying on Python’s robust built-in security measures and third-party tools, make sure you adhere to security best practices within your coding routine. For example, always hash and encrypt passwords and sensitive data using libraries like fernet and cryptography. Moreover, ensure that your API keys and secrets are never exposed, utilizing secret management tools provided by the os library or employ environment variables instead. Additionally, maintain vigilance with user input validation, error handling and the use of SQL-prepared statements for secure database interactions.

Maintaining Python security in your applications demands careful attention. Protect yourself from potential tampering by leveraging built-in security features and middleware offered by Python.

Additionally, integrating essential third-party tools is crucial for addressing tasks beyond your capabilities, such as Cloudinary for ensuring file security, OWASP Dependency-Check for catching vulnerabilities in dependencies, and Bandit for finding vulnerabilities in your codebase.

And don’t forget to make security a priority in your coding routine.

Protecting your Python applications is crucial for their safety and integrity. Act now to implement these safeguards effectively.

If you found this blog post helpful and want to discuss it in more detail, head over to the Cloudinary Community forum and its associated Discord.


  1. Create a Python project containing an app with files apps.py, models.py, views.py, and forms.py, and create a virtual environment:
django-admin startproject <project_name>  
cd <project_name>  
python3 -m venv <virtual_environment_name>      
source <virtual_environment_name>/bin/activate   
pip install django
python manage.py startapp <app_name>
Code language: HTML, XML (xml)
  1. Make sure django.contrib.auth and django.contrib.contenttypes are listed under INSTALLED_APPS in settings.py. Add your app to INSTALLED_APPS, as well. Then, run manage.py migrate to create the necessary database tables.
  1. In the views.py file, create a signup view and a landing_page view, and use the @login_required decorator for all your views that require login:
# django_app/views.py

from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm

def signup(request):
   if request.method == 'POST':
       form = UserCreationForm(request.POST)
       if form.is_valid():
           form.save()
           return redirect('login')
   else:
       form = UserCreationForm()
   return render(request, 'signup.html', {'form': form})
def landing_page(request):
   if request.user.is_authenticated:
       return render(request, 'my_template.html')
   return render(request, 'landing_page.html')

@login_required
def my_app(request):
    # Action logic here
   return render(request, 'my_template.html')
Code language: PHP (php)
  1. Create a signup.html template, a registration/login.html template, a landing_page.html template, and a my_template.html template:
<!-- signup.html -->

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Sign Up</title>
</head>
<body>
   <h2>Sign Up</h2>
   <form method="post">
       {% csrf_token %}
       {{ form.as_p }}
       <button type="submit">Sign Up</button>
   </form>
</body>

<!-- registration/login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Login</title>
</head>
<body>
   <h2>Login</h2>
   <form method="post">
       {% csrf_token %}
       {{ form.as_p }}
       <button type="submit">Login</button>
   </form>
</body>
</html>

<!-- landing_page.html -->
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Welcome to Your Site</title>
</head>
<body>
   <h1>Welcome to Your Site</h1>
   <p>Sign up or log in to access the features:</p>
   <a href="{% url 'signup' %}">Sign Up</a>
   <a href="{% url 'login' %}">Log In</a>
</body>
</html>
Code language: HTML, XML (xml)
  1. Import from django.contrib.auth import views as auth_views in your url.py file and add the new paths:
# urls.py

from django.contrib import admin
from django.urls import path
from upload_app import views
from django.contrib.auth import views as auth_views

urlpatterns = [
   path('admin/', admin.site.urls),
   path('accounts/login/', auth_views.LoginView.as_view(), name='login'),
   path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
   path('', views.landing_page, name='landing_page'),
   path('signup/', views.signup, name='signup'),
   path('my_app/', views.my_app, name='my_app'),
  # Your app’s paths
]
Code language: PHP (php)
  1. To allow logging in, singing in, and logging out, add this script to the rest of the templates in your app:
{% if user.is_authenticated %}
   <p>Welcome, {{ user.username }}!</p>
   <a href="{% url 'logout' %}">Logout</a>
{% else %}
   <a href="{% url 'signup' %}">Sign up</a>
   <a href="{% url 'login' %}">Login</a>
{% endif %}
Code language: HTML, XML (xml)
  1. In settings.py, add the following code to open the respective templates whenever a user logs in and out:
LOGIN_REDIRECT_URL = 'my_app'  # Redirect to upload page after login
LOGOUT_REDIRECT_URL = 'login'   # Redirect to login page after logout
Code language: PHP (php)
Back to top