Skip to content

Drupal Security Hardening — Updates, Patches and Security Best Practices

DodaTech Updated 2026-06-27 10 min read

In this tutorial, you'll learn how to harden Drupal security: keeping core and modules updated, configuring secure settings.php and .htaccess files, setting up trusted host patterns, implementing two-factor authentication, and using security modules to protect your site.

What You'll Learn

  • The Drupal Security Advisory Process and how to stay informed
  • Updating core and modules via Composer
  • Securing settings.php: credentials, hash salt, trusted host patterns, base_url
  • File permissions: directories 755, files 644, sites/default strict
  • .htaccess hardening: blocking file access, preventing PHP execution
  • services.yml security: trusted_host_patterns, CORS configuration
  • User login security: limiting login attempts
  • Password policy: strong passwords, regular changes
  • Two-factor authentication with the TFA module and Google Authenticator
  • Security modules: Security Kit, Paranoia, Username Enumeration Prevention
  • Drupal vulnerability scanner and regular security audits
  • Securing cron with a cron key
  • SSL/HTTPS configuration and mixed content prevention

Why It Matters

Drupal powers government websites, enterprise portals, and high-traffic platforms — making it a prime target for attackers. The Drupal security team releases advisories regularly, and unpatched sites are compromised within hours of a public disclosure. Security hardening is not optional. A single vulnerability in a contributed module can expose your entire database, including user credentials and sensitive content. Following security best practices protects your data, your users, and your reputation.

Real-World Use

A government agency running a Drupal site with citizen data must comply with security standards. They subscribe to Drupal security advisories, apply patches within 48 hours of release, restrict file permissions, enforce strong passwords with 2FA, and run monthly security audits. When the Drupal security team announced a critical remote code execution vulnerability (SA-CORE-2023-001), the agency patched within hours because their update process was automated and tested. Unpatched sites running the same vulnerability were compromised within 24 hours.

Learning Path

flowchart LR
  A[User Management] --> B[Security Hardening]
  B --> C[Updates and Patches]
  C --> D[File Permissions]
  D --> E[.htaccess and settings.php]
  E --> F[2FA and Password Policy]
  F --> G[Security Modules]
  G --> H[Monitoring and Audits]
  H --> I[Go-Live Checklist]

Understanding the Drupal Security Advisory Process

The Drupal security team is a volunteer group that reviews code and responds to vulnerability reports. When a security issue is confirmed, they release a Security Advisory (SA).

Security Advisory Types

  • SA-CORE: Core vulnerabilities affecting Drupal itself
  • SA-CONTRIB: Contributed module or theme vulnerabilities
  • SA-PUBLIC: Public service announcements

Each advisory includes:

  • Severity level (Critical, Highly critical, Moderate, Less critical)
  • Affected versions
  • Fixed versions
  • Description of the vulnerability
  • Mitigation steps if a patch is not yet available

Staying Informed

Subscribe to the security newsletter at Drupal.org and follow the @drupalsecurity account. Configure your site's Update Status module to email you when updates are available.

Updating Core and Modules

Keeping Drupal updated is your primary defense against known vulnerabilities.

Updating with Composer

# Check for available updates
composer outdated drupal/*

# Update Drupal core to the latest version
composer update drupal/core-recommended --with-all-dependencies

# Update a specific contributed module
composer update drupal/pathauto --with-all-dependencies

# Update all Drupal packages
composer update drupal/* --with-all-dependencies

Running Database Updates

After updating code, always run database updates:

# Run database updates via Drush
drush updatedb

# Or via the web interface
# Navigate to /update.php after core update
# Full update workflow
# 1. Put site in maintenance mode
drush state:set system.maintenance_mode 1 --input-format=integer

# 2. Update code via Composer
composer update drupal/core-recommended --with-all-dependencies

# 3. Run database updates
drush updatedb

# 4. Rebuild cache
drush cache:rebuild

# 5. Take site out of maintenance mode
drush state:set system.maintenance_mode 0 --input-format=integer

Automated Security Updates

Consider using the Project Browser module or a CI/CD pipeline to automate update checks. For critical security releases, apply patches within 48 hours.

Securing settings.php

The sites/default/settings.php file contains sensitive configuration. Here is how to secure it:

<?php
// settings.php security configuration

// Trusted host patterns — prevents HTTP Host header attacks
$settings['trusted_host_patterns'] = [
  '^www\.example\.com$',
  '^example\.com$',
  '^staging\.example\.com$',
  '^localhost$',
];

// Base URL — prevents URL generation issues
$settings['base_url'] = 'https://www.example.com';

// Hash salt — used for password hashing and token generation
// Always generate a unique, random string
$settings['hash_salt'] = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890';

// Database credentials
$databases['default']['default'] = [
  'database' => 'drupal_db',
  'username' => 'drupal_user',
  'password' => 'strong_password_here',
  'host' => 'localhost',
  'port' => '3306',
  'driver' => 'mysql',
  'prefix' => '',
  'charset' => 'utf8mb4',
];

// Private file path — outside web root if possible
$settings['file_private_path'] = '/var/www/private';

// Config sync directory — outside web root
$settings['config_sync_directory'] = '../config/sync';

File Permissions

Restrictive file permissions prevent attackers from modifying files if they gain access.

# Set directory permissions to 755 (rwx r-x r-x)
find web/ -type d -exec chmod 755 {} \;

# Set file permissions to 644 (rw- r-- r--)
find web/ -type f -exec chmod 644 {} \;

# Stricter permissions for sites/default
chmod 750 sites/default
chmod 640 sites/default/settings.php
chmod 640 sites/default/services.yml

# Private files directory
chmod 750 /var/www/private

# The files directory needs write access for uploads
chmod 775 sites/default/files

.htaccess Hardening

Drupal's .htaccess file protects the web root. Here are additional hardening rules:

# .htaccess hardening rules

# Block access to sensitive files
<FilesMatch "\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml|md)$">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
  </IfModule>
</FilesMatch>

# Block access to configuration files
<FilesMatch "(\.(yaml|yml)|settings\.php|services\.yml)$">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
  </IfModule>
</FilesMatch>

# Prevent PHP execution in files directory
<Directory "sites/default/files">
  <IfModule mod_php7.c>
    php_flag engine off
  </IfModule>
</Directory>

services.yml Security

The services.yml file configures Drupal services:

# services.yml security settings
parameters:
  # Trusted host patterns — required for production
  trusted_host_patterns:
    - '^www\.example\.com$'
    - '^example\.com$'
  
  # CORS configuration
  cors.config:
    enabled: true
    allowedHeaders:
      - 'x-csrf-token'
      - 'authorization'
      - 'content-type'
      - 'accept'
      - 'origin'
      - 'x-requested-with'
    allowedMethods:
      - 'GET'
      - 'POST'
      - 'PATCH'
      - 'DELETE'
    allowedOrigins:
      - 'https://www.example.com'
    exposedHeaders: false
    maxAge: 1000
    supportsCredentials: true
  
  # Disable file with .php extension uploads
  # Prevents attackers from uploading and executing PHP files
  file_extensions: 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'

User Login Security

Limiting Login Attempts

The core Flood control mechanism limits login attempts:

<?php
// settings.php: configure flood control
$settings['flood_limit'] = 5;       // Max failed attempts
$settings['flood_window'] = 3600;   // Within 1 hour (in seconds)

// This prevents brute force attacks on user passwords.
// After 5 failed attempts from the same IP within an hour,
// Drupal blocks further login attempts.

Password Policy

Drupal does not enforce password strength by default. Use the Password Policy module:

# Password policy configuration
password_policy:
  minimum_length: 12
  require_uppercase: true
  require_lowercase: true
  require_number: true
  require_special_character: true
  password_expiration: 90  # days
  password_history: 5      # cannot reuse last 5 passwords

Two-Factor Authentication

The TFA module adds two-factor authentication using time-based one-time passwords (TOTP):

# Install the TFA module and its dependencies
composer require drupal/tfa

# Enable the module
drush pm:enable tfa

# Configure at /admin/config/people/tfa
<?php
// Programmatically check if user has TFA enabled
$tfa_enabled = \Drupal::service('tfa.api')
  ->isPluginActiveForUser('tfa_totp', $account);

Users scan a QR code with Google Authenticator, Authy, or any TOTP app. After scanning, they enter a 6-digit code that changes every 30 seconds.

Security Modules

Security Kit (seckit)

Security Kit provides comprehensive security headers:

# Security Kit configuration
seckit:
  # Content Security Policy headers
  csp:
    default-src: 'self'
    script-src: 'self'
    style-src: 'self' 'unsafe-inline'
    img-src: 'self' data:
  # X-Frame-Options
  x_frame: SAMEORIGIN
  # X-Content-Type-Options
  x_content_type: nosniff
  # Feature Policy
  feature_policy: "geolocation 'none'; microphone 'none'; camera 'none'"

Paranoia Module

The Paranoia module locks down Drupal's configuration system, preventing administrators from making dangerous changes through the UI.

Username Enumeration Prevention

By default, Drupal reveals whether a username exists during login. This module prevents attackers from discovering valid usernames.

Regular Security Audits

Use the Security Review module to audit your site:

# Install security review module
composer require drupal/security_review
drush pm:enable security_review

# Run security checks via Drush
drush security_review:run

# View results at /admin/reports/security-review

The module checks:

  • File system permissions
  • Error reporting settings
  • Input validation
  • Database configuration
  • Trusted host patterns
  • Private file paths
  • PHP execution in files directory

Securing Cron

Cron can be triggered via HTTP, which allows anyone to trigger it if they know the URL. Secure it with a cron key:

<?php
// settings.php: set a cron key
$settings['cron_key'] = 'your-secure-random-cron-key-here';
# Trigger cron securely using the key
# Only someone with the key can trigger cron
curl -s https://www.example.com/cron/YOUR-KEY-HERE

# Or use Drush instead (recommended)
drush cron

SSL/HTTPS Configuration

Always serve Drupal over HTTPS:

<?php
// settings.php: force HTTPS
$settings['reverse_proxy'] = true;
$settings['reverse_proxy_addresses'] = [$_SERVER['REMOTE_ADDR']];

// Or redirect HTTP to HTTPS in .htaccess
// RewriteEngine On
// RewriteCond %{HTTPS} off
// RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
# .htaccess: prevent mixed content by redirecting to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

# Enable HSTS (HTTP Strict Transport Security)
# Tells browsers to always use HTTPS for this domain
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

Common Mistakes

  1. Not subscribing to security advisories: The Drupal security team announces vulnerabilities on Drupal.org. If you do not monitor these announcements, you might not know about critical patches until it is too late.

  2. Running outdated contributed modules: Even if Drupal core is updated, contributed modules can have vulnerabilities. Run composer outdated regularly and update all modules.

  3. Using default file permissions: Many hosting environments set permissive file permissions. Restrict them to 755 for directories and 644 for files, and make sure the files directory does not allow PHP execution.

  4. Not configuring trusted host patterns: Without trusted host patterns, an attacker can use HTTP Host header injection to poison cache, reset passwords, or generate malicious links.

  5. Disabling update notifications: Some administrators disable the Update Status module because they find the notifications annoying. This hides critical security update information.

Practice Questions

  1. What is the difference between SA-CORE and SA-CONTRIB advisories, and how should you respond to each?
  2. You find that Drupal is leaking valid usernames through the login form. How do you fix this?
  3. What would happen if you set the files directory to 777 permissions, and why is this dangerous?
  4. Challenge: Create a security hardening checklist for a Drupal production site. Include at least 15 items covering file permissions, configuration, modules, network security, and monitoring. For each item, write the command or configuration step needed.

FAQ

How often should I update Drupal?

Apply security updates immediately (within 48 hours for critical releases). For minor and major version updates, schedule them during maintenance windows after testing on a staging environment. Subscribe to Drupal security advisories to stay informed.

What is the most common Drupal vulnerability?

Cross-Site Scripting (XSS) is the most common vulnerability type in contributed modules. Always filter user input with XSS filters, keep modules updated, and use security modules like Security Kit to add Content Security Policy headers.

How do I secure Drupal on shared hosting?

Use strong passwords, enable HTTPS with Let's Encrypt, set correct file permissions, use a security module like Security Kit, and ensure your hosting provider isolates Drupal from other sites on the same server.

What is a trusted host pattern?

A trusted host pattern is a regex that validates the HTTP Host header. Without it, an attacker could send a request with a malicious Host header and cause Drupal to generate links pointing to an attacker-controlled domain. Always configure it in settings.php.

Do I need a security module if Drupal core is already secure?

Yes. Security modules provide defense in depth. Security Kit adds HTTP security headers (CSP, HSTS, X-Frame-Options). Paranoia restricts dangerous admin actions. These layers protect against vulnerabilities that might exist in contributed modules or custom code.

Mini Project

Goal: Harden a Drupal site for production deployment.

  1. Scan your current Drupal installation for security issues using the Security Review module
  2. Fix all issues found in the scan (file permissions, trusted hosts, error reporting, etc.)
  3. Install and configure Security Kit with CSP headers, X-Frame-Options, and HSTS
  4. Set up TFA (two-factor authentication) for all administrator accounts
  5. Configure password policy: minimum 12 characters, uppercase, lowercase, number, special character, expire every 90 days
  6. Set up a cron key and automate cron via a system cron job instead of HTTP
  7. Write a bash script that performs a daily security check: verifies file permissions, checks for Drupal security updates, and emails the report to the admin

What's Next

Now that you understand security hardening, proceed to custom module development to extend Drupal with your own functionality. Then explore Drupal API to learn about render arrays, entity queries, and the service container.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro