
September 11, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Hashing vs encryption vs encoding trips up even experienced teams because all three transform data into something that looks unreadable. They solve different problems. Mix them up and you store passwords you can decrypt, or you encrypt API payloads you can never verify. On production custom software projects, I treat this distinction as architecture—not a trivia question. This guide maps each technique to real use cases with PHP 8.3+ and Laravel 13 examples you can ship today.
What is the difference between hashing, encryption, and encoding?
All three change how bytes look on disk or on the wire. Only two protect confidentiality. Only one is designed to be irreversible.
Hashing runs input through a one-way function. The same input yields the same digest. You cannot derive the original string from the hash. Password verification works by hashing the login attempt and comparing digests.
Encryption uses a key to scramble data. Anyone with the correct key can decrypt. Use it when the application must read the original value later—payment tokens, national ID numbers, or document contents in a client portal.
Encoding represents binary data as text. Base64 is the common example. No key is involved. Decoding is trivial. Encoding is not security. It is a format change.
| Property | Hashing | Encryption | Encoding |
|---|---|---|---|
| Reversible? | No (one-way) | Yes (with key) | Yes (no key) |
| Requires secret key? | No (uses salt + algorithm) | Yes | No |
| Primary goal | Integrity / verification | Confidentiality | Data representation |
| Typical output size | Fixed digest length | Similar to input + overhead | ~33% larger for Base64 |
| Example use | Password storage | Encrypting PAN at rest | Email attachment in JSON |
| Security if exposed alone? | Resists reversal (if strong) | Safe until key leaks | None—trivial to decode |
The Argon2id vs bcrypt password hashing article goes deeper on digest algorithms. For transport-layer secrecy, read symmetric vs asymmetric encryption. Those posts assume you already know which category you need.
When should you use hashing instead of encryption?
Hash when you never need the original value back. You only need to confirm a future input matches what was stored.
Passwords are the canonical case. A login form sends a plaintext password over TLS. Your server hashes it and compares the result to the stored digest. Storing an encrypted password means your app holds the decryption key. One database leak exposes every account.
File integrity checks use hashing too. You hash a deployment artefact and compare it to a published checksum. You are not hiding the file—you are proving it was not tampered with.
API request signing sometimes uses HMAC—a keyed hash. That is still a hash family member, not encryption. The shared secret proves authenticity without encrypting the entire payload.
Do not hash credit card numbers when you must charge them later. That needs encryption or a vault. Do not hash email addresses if you send mail to them daily—unless you accept lookup-by-hash trade-offs.
On legal-tech portals like Mijar Law Associates, client document metadata may be hashed for deduplication. The files themselves stay encrypted at rest. Different layers, different tools.
Algorithms you should and should not use for passwords
- Use: Argon2id, bcrypt, or scrypt via PHP's
password_hash() - Never use for passwords: MD5, SHA-1, plain SHA-256 without a proper KDF
- Context matters: SHA-256 is fine for checksums, wrong for password storage
The OWASP Password Storage Cheat Sheet aligns with this guidance. Treat it as the baseline audit checklist.
How do you implement encryption and encoding correctly in PHP and Laravel?
Encryption protects data you must retrieve. Laravel ships with AES-256-CBC and AES-256-GCM via the Encrypter class. Your APP_KEY drives it. Rotate keys carefully—old ciphertext becomes unreadable unless you re-encrypt.
For authenticated encryption details, see AEAD explained. GCM adds integrity alongside confidentiality. Prefer it when your Laravel version supports it for new data.
Password hashing in PHP
<?php
declare(strict_types=1);
$password = 'Correct-Horse-Battery-Staple-2026!';
$hash = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 2,
]);
if (password_verify($password, $hash)) {
echo "Login OK\n";
}
if (password_needs_rehash($hash, PASSWORD_ARGON2ID)) {
$hash = password_hash($password, PASSWORD_ARGON2ID);
}
PHP 8.3 or higher on Laravel 13 supports Argon2id natively when compiled with libsodium. Fall back to PASSWORD_BCRYPT on hosts that lack it. Check with password_get_info() after deploy—not only on your laptop.
Field-level encryption in Laravel
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Crypt;
class ClientDocument extends Model
{
protected $casts = [
'national_id' => 'encrypted',
];
public function setNotesAttribute(?string $value): void
{
$this->attributes['notes'] = $value === null
? null
: Crypt::encryptString($value);
}
public function getNotesAttribute(?string $value): ?string
{
return $value === null ? null : Crypt::decryptString($value);
}
}
The encrypted cast handles serialisation automatically. For large blobs, consider database encryption at rest and in transit plus application-level field encryption. Defence in depth beats a single layer.
Encoding with Base64
Base64 turns binary into ASCII-safe text. Email systems, JWT segments, and JSON payloads use it constantly. It is not encryption. Anyone can decode it in seconds.
<?php
$binary = random_bytes(32);
$encoded = base64_encode($binary);
$decoded = base64_decode($encoded, true);
if ($decoded === false) {
throw new RuntimeException('Invalid Base64 input');
}
Try the on-site Base64 encoder and decoder when debugging API payloads. Pair it with the password generator for test credentials—not production secrets pulled from a browser tab.
Unicode text needs UTF-8 before Base64 if you expect correct round-trips. The Nepali Unicode converter handles a different encoding problem—character sets—not cryptographic encoding. Same word, different meaning.
What are common mistakes developers make with hashing vs encryption vs encoding?
These errors appear in code review repeatedly. They are preventable once you label each field by its lifecycle requirement.
- Encrypting passwords. Reversible storage invites mass decryption after a key leak. Hash with Argon2id instead.
- Hashing data you must read later. SHA-256 of a phone number cannot be reversed for SMS delivery. Encrypt or store plaintext in a secured column.
- Calling Base64 "encryption". Compliance audits fail on this wording. Encoding is not a control.
- Using MD5 or SHA-1 for passwords. GPU cracking makes these obsolete for credentials.
- Hard-coding keys in source control.
APP_KEY, API secrets, and KMS credentials belong in environment variables or a vault. - Skipping TLS because the body is encrypted. Transport encryption and storage encryption solve different threats. Use HTTPS always.
I have seen teams on enterprise Laravel builds encrypt session IDs unnecessarily. That adds latency and key-management risk for no gain. Sessions already rely on server-side storage or signed cookies.
Another pattern: searchable encrypted fields. Standard AES makes WHERE email = ? impossible. Options include deterministic encryption with strict access controls, blind indexes (HMAC of normalised values), or dedicated search engines with tokenisation. Each trade-off deserves a design note, not an improvised helper function.
How do you choose the right approach for API tokens, payments, and legal documents?
Different assets need different controls. Map each column and cookie to a row in your data-classification spreadsheet before writing code.
API bearer tokens: Store a hash of the token, display the plaintext once at creation. Same pattern as passwords. Laravel Sanctum and Passport follow this model internally.
Payment gateway references: Encrypt if you must retry charges. Better yet, store only the gateway's tokenised reference. Never touch raw card numbers if the provider offers tokenisation.
Legal document uploads: Files at rest belong on encrypted storage—S3 SSE or disk encryption. Metadata like case numbers may stay plaintext for search. Sensitive notes get field-level encryption. On portals such as Notary Nepal, access control and audit logs matter as much as the algorithm choice.
Webhook signatures: Use HMAC-SHA256 over the raw body. Compare with hash_equals() to block timing attacks. This is hashing for authenticity, not encryption of the payload.
<?php
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signatureHeader)) {
abort(401, 'Invalid webhook signature');
}
For multi-key setups and cloud KMS, read AWS KMS envelope encryption explained and hybrid encryption explained. They extend the symmetric model for scale.
Rate limiting and abuse prevention—covered in the API rate limiting guide—pair with these crypto choices. A perfectly hashed password still fails if login endpoints allow unlimited guesses.
Operational checklist for production
- Document which tables use hashing, encryption, or encoding
- Store
APP_KEYoutside git; back it up securely - Enable TLS 1.2+ on all public endpoints via your web server
- Run
php artisan key:generateonly once per environment - Schedule password hash upgrades via
password_needs_rehash()on login - Log decryption failures; they often signal tampering or key rotation errors
Server hardening complements application crypto. See Linux system administration for PHP-FPM, permission, and firewall work that keeps keys and data paths safe on Ubuntu hosts.
The official PHP password_hash documentation lists supported algorithms per build. Verify production binaries match CI—not only your local Docker image.
Key Takeaways
- Hash passwords and API token digests; never encrypt credentials you do not need to read.
- Encrypt reversible PII and secrets at rest; protect
APP_KEYlike a root password. - Treat Base64 and UTF-8 as encoding—visible to anyone, not a security boundary.
- Use Argon2id or bcrypt via
password_hash(); retire MD5 and SHA-1 for credentials. - Compare HMAC signatures with
hash_equals()to prevent timing side channels. - Layer TLS in transit, encryption at rest, and access control—one technique never covers every threat.
People Also Ask
Is Base64 encryption?
No. Base64 is encoding. It converts binary to text without a secret key. Decoding takes one line of code. Use it for transport formats, not confidentiality. Real encryption requires a key and an algorithm like AES.
Can you decrypt a hash?
No. Hashes are one-way by design. Attackers guess inputs and hash them until a match appears—that is brute force, not decryption. Strong password hashes with salts make that impractical at scale.
Should you hash or encrypt email addresses?
Encrypt if you send email to the address and query by exact match. Hash only if you need verification without retrieval—uncommon for email. Many apps store emails in plaintext with strict access control and TLS. Choose based on your threat model and search requirements.
What is the best algorithm for password hashing in 2026?
Argon2id is the first choice on PHP 8.3+ with libsodium support. Bcrypt remains acceptable on older hosts. Both beat MD5, SHA-1, and unsalted SHA-256. Rehash on login when password_needs_rehash() returns true after you raise work factors.
Build security into your application from day one
Hashing vs encryption vs encoding is not academic. Pick wrong and you either lose data you needed or expose data you thought was protected. Label every sensitive field, apply the right transform, and test on production PHP builds before launch.
If you are planning a portal, API, or eCommerce platform that handles user credentials or confidential documents, review the API development service or browse the full project portfolio for shipped examples. Need help auditing an existing Laravel app? Contact us for a practical security review—not a generic checklist.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

