Skip to content

PHP Tutorial PHP Security

Avoiding Sensitive Data Exposure

Lesson 301 of 369 3 min read By Deepak Paswan Updated Oct 5, 2026

Many breaches are not clever attacks. A password sits in a file that anyone can download, or a backup is left on the server, or an error page prints the database name. This lesson is about not leaving the door open.

Secrets: where they live

Database passwords, API keys and mail credentials must not be:

  • Written in code that is committed to Git.
  • Stored in a file inside the public web folder.
  • Printed in error messages or logs.
  • Sent to the browser (in JavaScript, HTML comments or JSON).

They belong in environment variables or a config file outside the web root. See Environment Variables.

index.php
PHP
<?php
putenv("API_KEY=sk_live_example");

$key = getenv("API_KEY");

echo $key !== false ? "The key is read from the environment" : "missing";
Output
OUTPUT
The key is read from the environment

Files that must never be public

FileRisk
.env, config.php copies (config.php.bak, config.old)Passwords and keys
.git/ folderThe whole source code history, with old secrets
Database dumps (backup.sql, site.zip)All your data
composer.json, composer.lock, vendor/Reveal exact library versions to attackers
phpinfo() pages and test scriptsShow your configuration
Log filesOften contain personal data and tokens
Editor and OS files (.DS_Store, .idea/)Reveal the folder structure

Keep application code outside the public folder. Only public/index.php and static assets belong in it.

On Apache, you can deny dotfiles:

code
APACHE
<FilesMatch "^\.">
    Require all denied
</FilesMatch>
Options -Indexes

Options -Indexes turns off directory listings.

Search for leaks

From your project folder, check what Git tracks:

terminal
BASH
git ls-files | grep -i -E "\.env|\.sql|\.bak|backup|id_rsa"

If a secret was ever committed, changing the file is not enough. The old version stays in Git history. Treat that secret as compromised: change it (rotate it) at the source.

Do not show errors

Turn off display_errors on live sites, and show a friendly page. See Production Error Handling.

Return only what is needed

An API or a page should include only the fields that the viewer needs.

index.php
PHP
<?php
$user = ["id" => 7, "name" => "Ana", "email" => "ana@example.com", "password_hash" => '$2y$10$abc', "role" => "admin"];

$public = array_intersect_key($user, array_flip(["id", "name"]));

echo json_encode($public);
Output
OUTPUT
{"id":7,"name":"Ana"}

Select only needed columns in SQL (not SELECT *), and build the output from an allow-list of fields. Then a new sensitive column never leaks by accident.

Encrypt sensitive data you must store

For things like API tokens or personal data that you must read back, use authenticated encryption (sodium_crypto_secretbox, or a library). Keep the key outside the database.

index.php
PHP
<?php
$key = sodium_crypto_secretbox_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

$cipher = sodium_crypto_secretbox("secret token", $nonce, $key);

echo sodium_crypto_secretbox_open($cipher, $nonce, $key);
Output
OUTPUT
secret token

This needs the sodium extension, which ships with PHP but must be enabled (extension=sodium). Passwords are not encrypted. They are hashed. Never store card numbers yourself. Use a payment provider that handles them.

Collect less

Data you never collect cannot leak. Ask: do we need the date of birth? The full address? Delete data when you no longer need it, and follow the privacy rules that apply to your visitors.

Backups

Keep backups, store them off the web server, encrypt them, and test restoring them. A backup in /public_html/backup.zip is a gift to attackers.

HTTPS

Without HTTPS, every login and form can be read by anyone on the network. Use it everywhere.

dangerous
Rotate when in doubt
If a password, key or token may have been seen by someone who should not have it (a leaked repository, a former employee), replace it. It is cheap compared to a breach.