- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Hashing vs Encryption vs Encoding: The One Question That Tells Them Apart
About Post
"Don't worry, the passwords are encrypted. We base64 them."
That sentence contains two mistakes, and I've heard versions of it more than once. Hashing, encryption and encoding all turn readable data into something that looks like gibberish, so they're easy to mix up. But they answer completely different questions, and using the wrong one is how passwords, tokens and personal data end up exposed.
Let's untangle them for good, with one question that sorts out almost every case.
The one question: who should be able to get the original back?
- Anyone → that's encoding. The goal is format, not secrecy.
- Only someone with the key → that's encryption. The goal is confidentiality.
- Nobody, ever → that's hashing. The goal is verification without storing the original.
Keep that in your head and the rest of this article is detail.
Encoding: changing the format, not hiding anything
Encoding turns data into a different representation so it can travel safely through a system that expects a certain format. Base64 turns binary into plain text characters, so an image can live inside JSON or an email. URL encoding turns a space into %20. UTF-8 turns characters into bytes.
$encoded = base64_encode('secret123'); // "c2VjcmV0MTIz"
$original = base64_decode($encoded); // "secret123"
There's no key. Anyone can decode it, instantly. That's not a weakness, it's the design.
The classic confusion: a JWT is encoded, not encrypted. Paste one into any JWT decoder and you'll read the payload in plain text. The signature proves it hasn't been changed, but it doesn't hide anything. Never put secrets or sensitive personal data in a JWT payload.
Encryption: secret, but reversible
Encryption scrambles data with a key so that only someone with the right key can turn it back. Use it when you need the original later: an API key for a third-party service, a stored document, data travelling over HTTPS.
The workhorse is AES, a symmetric cipher, meaning the same key encrypts and decrypts. Asymmetric encryption (RSA and elliptic-curve schemes) uses a public key to encrypt and a private key to decrypt, which is what makes things like the TLS handshake possible.
In Laravel, you rarely touch the algorithms directly:
use Illuminate\Support\Facades\Crypt;
$token = Crypt::encryptString($gatewayApiKey); // safe to store
$apiKey = Crypt::decryptString($token); // needs APP_KEY
// Or per attribute on a model:
protected function casts(): array
{
return ['gateway_api_key' => 'encrypted'];
}
Laravel uses AES-256 with a message authentication code, so tampered data fails to decrypt instead of quietly producing garbage. The security of all of it rests on one thing: APP_KEY. Keep it out of Git, back it up, and remember that if you lose it, every encrypted value is lost with it.
Hashing: one way, on purpose
A hash function turns any input into a fixed-length fingerprint. The same input always gives the same output, a tiny change gives a completely different output, and there's no way to compute the input from the output.
That makes hashing perfect for two jobs:
- Checking integrity. Did this file download correctly? Has this webhook body been changed? SHA-256 is the usual choice, and for webhooks you use an HMAC, a hash mixed with a shared secret, so only the real sender can produce a valid signature.
- Storing passwords. You never need a user's password back. You only need to check that what they typed matches. So you store a hash and compare hashes.
Why passwords need a special kind of hash
Here's the gotcha that bites even experienced developers: SHA-256 is the wrong tool for passwords, and MD5 is worse.
General-purpose hashes are designed to be fast. That's great for checking files and terrible for passwords, because if your database leaks, an attacker can try enormous numbers of guesses per second against fast hashes.
Password hashing algorithms are deliberately slow and salted:
- bcrypt: the long-standing default, with a tunable cost factor.
- Argon2id: a more modern choice that is also memory-hard, which makes attacks with specialised hardware more expensive.
- A random salt for every password, so two users with the same password get different hashes and precomputed tables are useless.
The good news: you don't implement any of this yourself.
// Plain PHP
$hash = password_hash($password, PASSWORD_DEFAULT); // bcrypt today
$ok = password_verify($input, $hash);
// Laravel
$hash = Hash::make($password);
$ok = Hash::check($input, $hash);
if (Hash::needsRehash($hash)) { /* re-hash after a successful login */ }
The salt and the algorithm settings are stored inside the hash string itself, so there's no separate column to manage. One detail worth knowing: bcrypt only uses the first 72 bytes of the input, so very long passphrases are silently truncated. Argon2id doesn't have that limit.
The cheat sheet
| Encoding | Encryption | Hashing | |
|---|---|---|---|
| Purpose | Format and transport | Confidentiality | Verification and integrity |
| Reversible? | Yes, by anyone | Yes, with the key | No |
| Needs a key? | No | Yes | No (HMAC uses a secret) |
| Examples | Base64, URL encoding, UTF-8 | AES, RSA, TLS | SHA-256, bcrypt, Argon2id |
| Use it for | Binary in JSON, URLs, emails | API keys, sensitive stored data | Passwords, checksums, signatures |
Common confusions, corrected
- ❌ "Base64 encrypted." ✅ Base64 is encoding. It hides nothing.
- ❌ "We encrypt passwords." ✅ Passwords should be hashed with bcrypt or Argon2id, so even you can't read them.
- ❌ "Let me decrypt the hash." ✅ Hashes can't be decrypted. "Cracking" a hash means guessing inputs until one matches, which is exactly what slow hashes are built to resist.
- ❌ "SHA-256 is secure, so it's fine for passwords." ✅ Secure for integrity, too fast for passwords.
- ❌ "The JWT is safe to put anything in." ✅ It's signed, not encrypted. Anyone can read it.
If you remember one thing: ask who should be able to get the original back. Anyone: encode. Only the key holder: encrypt. Nobody: hash, and for passwords, use a slow password hash through your framework.
Which of these confusions have you run into most in code reviews? I'd love to hear the most creative "encryption" you've ever found in a codebase.

Be first to comment it...