Laravel Writable Config Files: How to Set Read-Only Permissions on Configuration

Config files writable by the web server can be modified by an attacker who gains limited access. Set them to read-only to prevent configuration tampering.

Medium severity Infrastructure Security Updated 2026-05-01 Markdown

Configuration files in config/ define your application's behavior: database connections, mail credentials, cache drivers, file storage backends, queue connections, and security settings. If these files are writable by the web server process, an attacker who gains limited code execution — through a file upload vulnerability, a deserialization attack, or a compromised Composer package — can modify them to redirect connections to attacker-controlled infrastructure.

The defense-in-depth principle is at work here. No single security control is perfect. A web application might have file upload validation, but a zero-day in an image processing library could bypass it. If config files are writable, a successful exploit of any vulnerability immediately escalates to configuration takeover. If config files are read-only (owned by the deploy user, not the web server user), the attacker's access is significantly limited even after initial compromise. Restrictive file permissions minimize the blast radius of other vulnerabilities.

This vulnerability class is particularly relevant in shared hosting environments and VPS setups where deployment is done manually. When a developer runs composer install or php artisan commands as the web server user, the created files inherit that user's ownership, making them writable by the web server. Proper deployment practice runs deployment commands as a dedicated deploy user with the web server in the same group — achieving the necessary read access for the web server without granting write access.

The Problem

If your config/ directory or individual configuration files are writable by the web server user (www-data, nginx, etc.), an attacker who gains limited code execution — through a file upload vulnerability, deserialization attack, or compromised dependency — can modify your application configuration. They could change the database connection to their own server, redirect mail to their address, disable security features, or add their own service credentials.

How to Fix

  1. 1

    Set config files to read-only

    Make config files readable but not writable by the web server:

    chmod 644 config/*.php
    chmod 755 config/
    # Verify
    ls -la config/
    # Files should show -rw-r--r-- (644)
    # Directory should show drwxr-xr-x (755)

    The web server needs to read config files but never write them.

  2. 2

    Set correct ownership

    Config files should be owned by your deploy user, not the web server:

    chown -R deploy:www-data config/
    # deploy user can read/write (for deployments)
    # www-data group can only read (for serving)

    This way, even if the web server process is compromised, it cannot modify configuration.

  3. 3

    Use config:cache in production

    Cached configuration is loaded from a single compiled file, reducing filesystem access:

    php artisan config:cache

    This creates bootstrap/cache/config.php. In production, Laravel reads this cached file instead of scanning the config/ directory. This also provides a small performance benefit.

    Run php artisan config:cache as part of every deployment.

How to Verify

Check file permissions:

find config/ -type f -perm -o+w

This should return no results. Also verify the web server user cannot write:

sudo -u www-data touch config/test.php 2>&1
# Should output: Permission denied

Run php artisan stackshield:scan --check=SS057 to verify.

Prevention

Set permissions in your deployment script. Use config:cache to minimize filesystem access. Never run artisan commands as the web server user in production. Include permission checks in your deployment pipeline.

Frequently Asked Questions

What about .env — should it be read-only too?

Yes. The .env file should be 600 (owner read/write only) or 640 (owner read/write, group read). The web server needs to read it (or it is read once during config:cache), but it should never be writable by the web server process.

Does config:cache make config files completely unnecessary?

No. The config files are still needed for: (1) generating the cache (config:cache reads them), (2) local development (where you typically do not cache config), and (3) re-caching after .env changes. Keep them in your deployment but with read-only permissions.

What if my deployment process needs to modify config files?

Config files should not be modified during deployment. If you are templating config values directly into PHP files during deployment, this is an anti-pattern. Use .env variables and config() references instead. Configuration should come from environment variables read at runtime, not from deployment-time file modification that requires the files to be writable.

Is it safe to run php artisan config:cache as the www-data user?

No. Artisan commands that write to the filesystem (config:cache, route:cache, view:cache) should be run as the deploy user, not as the web server user. When run as www-data, the cached files are owned by www-data, which means the web server can later overwrite them — creating the exact writable-config vulnerability this guide addresses. Run all deployment commands as your deploy user.

Which config files are most dangerous if writable by the web server?

The highest-risk config files are: config/database.php (allows redirecting all queries to an attacker's database), config/mail.php (allows capturing all outbound email), config/filesystems.php (allows redirecting file storage to attacker-controlled location), and config/logging.php (allows hiding attacker activity by redirecting logs). Any config file that defines an external connection endpoint is high-value for post-compromise escalation.

Related Security Terms

Free security check

Is your Laravel app exposed right now?

34% of Laravel apps we scan have at least one critical issue. Most teams don't find out until something breaks. Our free scan checks your live application in under 60 seconds.

18% have debug mode on
72% missing security headers
12% have exposed .env
Scan My App Free No signup required. Results in 60 seconds.