Is It Safe to Send Passwords in Login Requests? HTTP vs HTTPS Security
"Is it safe to add password in LoginRequest? Can someone reach the user's request?" Great security question! The answer depends entirely on whether you're using HTTP or HTTPS. With HTTP, anyone on the network can see your password in plain text. With HTTPS, it's encrypted end-to-end. Let's understand the difference and how to secure your login.
The Login Requestโ
@PostMapping("/auth/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
// request.getPassword() contains plain text password
User user = userRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new RuntimeException("Invalid credentials"));
if (passwordEncoder.matches(request.getPassword(), user.getPassword())) {
return ResponseEntity.ok(new LoginResponse(user.getId(), ...));
}
throw new RuntimeException("Invalid credentials");
}
Request body (JSON):
{
"email": "testuser@example.com",
"password": "testpass123"
}
The question: Can someone intercept this password during transmission?
The Risk: HTTP vs HTTPSโ
โ HTTP (UNSAFE)โ
Client Network Server
โ โ โ
โ {"password": "..."} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (PLAIN TEXT!) โ
What happens:
- Client sends JSON with password
- Password travels through network unencrypted
- Anyone on the network can see it
Who can intercept:
- โ ๏ธ Your ISP (Internet Service Provider)
- โ ๏ธ WiFi network owner (coffee shop, airport)
- โ ๏ธ Anyone on the same WiFi network
- โ ๏ธ Man-in-the-middle attackers
- โ ๏ธ Network administrators
- โ ๏ธ Hackers using packet sniffers
โ HTTPS (SAFE)โ
Client Network Server
โ โ โ
โ {"password": "..."} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (ENCRYPTED!) โ
โ [garbled encrypted data] โ
What happens:
- Client sends JSON with password
- Password is encrypted before leaving your device
- Travels through network as encrypted gibberish
- Only the server can decrypt it
Who can see:
- โ Only your client and the server
- โ Network observers see encrypted data (useless without decryption key)
HTTP: How the Attack Worksโ
The Scenarioโ
You're at a coffee shop using their WiFi:
Your Laptop โ Coffee Shop WiFi โ Internet โ Server
โ
Hacker
(using Wireshark)
What the Hacker Sees (HTTP)โ
Network packet captured:
POST /api/auth/login HTTP/1.1
Host: yourapp.com
Content-Type: application/json
Content-Length: 67
{
"email": "testuser@example.com",
"password": "testpass123"
}
Hacker now has:
- โ
Your email:
testuser@example.com - โ
Your password:
testpass123 - โ
The API endpoint:
/api/auth/login
Game over! ๐จ
What the Hacker Sees (HTTPS)โ
Network packet captured:
POST /api/auth/login HTTP/1.1
Host: yourapp.com
Content-Type: application/json
Content-Length: 67
๏ฟฝ๏ฟฝ๏ฟฝXs๏ฟฝ9k๏ฟฝ2๏ฟฝM๏ฟฝ๏ฟฝ๏ฟฝvT๏ฟฝ8o๏ฟฝ๏ฟฝP๏ฟฝ๏ฟฝ๏ฟฝ1๏ฟฝG๏ฟฝ๏ฟฝk
ลซ๏ฟฝX๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝาซ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝG๏ฟฝ๏ฟฝฯ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝฯญr๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ
Hacker sees:
- โ Encrypted gibberish
- โ Cannot decrypt without private key
- โ Useless data
You're safe! โ
HTTPS Encryption Explainedโ
How HTTPS Worksโ
1. Client โ Server: "Hello, I want HTTPS"
2. Server โ Client: "Here's my SSL certificate and public key"
3. Client: Verifies certificate is legitimate
4. Client: Generates session key, encrypts it with server's public key
5. Client โ Server: Sends encrypted session key
6. Server: Decrypts session key with private key
7. Both use session key for symmetric encryption
8. All data encrypted/decrypted using session key
Result: End-to-end encryption of all data including passwords.
Visual Flowโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Client (Your Browser/Postman) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ LoginRequest โ โ
โ โ { "email": "user@example.com", "password": "pass123" } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
[TLS/SSL Encryption Layer]
โ
Encrypted: ๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT๏ฟฝ8o๏ฟฝ๏ฟฝP๏ฟฝ๏ฟฝ๏ฟฝ1๏ฟฝG
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Network (WiFi, Internet, ISP) โ
โ โ ๏ธ Anyone can see traffic here โ
โ BUT: sees only encrypted gibberish โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
[TLS/SSL Decryption Layer]
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Server (Spring Boot) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ LoginRequest (decrypted) โ โ
โ โ { "email": "user@example.com", "password": "pass123" } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Critical Misunderstanding: "Wait - We're Hashing the Password!"โ
The Confusionโ
You might think:
"But we're using
passwordEncoder.encode(request.getPassword())to hash the password, so it's secure, right?"
Wrong! Let's understand exactly when hashing happens:
The Complete Registration/Login Flowโ
Registration:
@PostMapping("/auth/register")
public ResponseEntity<UserResponse> register(@RequestBody RegisterRequest request) {
User user = new User();
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword())); // โ Hashing happens HERE
userRepository.save(user);
return ResponseEntity.ok(new UserResponse(...));
}
Step-by-step breakdown:
1. Client (Postman) sends HTTP POST:
POST http://localhost:8082/api/auth/register
Body: {"email": "test@example.com", "password": "testpass123"}
โ
โ ๏ธ PASSWORD IN PLAIN TEXT IN TRANSIT!
2. Request travels through network:
Network sees: {"email": "test@example.com", "password": "testpass123"}
โ
โ ANYONE CAN INTERCEPT THIS!
3. Server receives request:
request.getPassword() = "testpass123" (still plain text)
โ
4. Server hashes password:
passwordEncoder.encode("testpass123")
โ "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
โ
5. Server stores hash in database:
Database: password = "$2a$10$N9qo8..."
โ
SAFE IN DATABASE
6. Server sends response:
{"id": 4, "username": "testuser", "email": "testuser@example.com", ...}
โ
โ
NO PASSWORD IN RESPONSE (safe)
The Problem: Hashing Happens on the SERVER, Not in Transitโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TRANSMISSION (Network) โ
โ โ
โ Client โโโโโโโ Network โโโโโโโ Server โ
โ (PLAIN TEXT!) โ
โ โ
โ โ Hashing doesn't protect this part! โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ STORAGE (Database) โ
โ โ
โ Server โ Database โ
โ (HASHED: $2a$10$...) โ
โ โ
โ โ
Hashing protects this part! โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
What Each Security Measure Protectsโ
| Security Measure | What It Protects | What It DOESN'T Protect |
|---|---|---|
| Password Hashing | Database storage | Network transmission |
| HTTPS Encryption | Network transmission | Database storage |
| Both Together | Complete security | โ Everything protected |
Visual Comparisonโ
HTTP + Hashing (INCOMPLETE SECURITY):
Client Network Server Database
โ โ โ โ
โ {"password": "testpass123"} โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ PLAIN TEXT โ โ
โ โ โ โ
โ โ โ โ
โ [Hacker intercepts] โ โ
โ โ Sees: "testpass123" โ โ
โ โ โ โ
โ โ passwordEncoder.encode(...) โ
โ โ โ โ
โ โ โ "$2a$10$N9qo..." โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โ
HASHED โ
Result: Hacker intercepts plain password before it reaches server! ๐จ
HTTPS + Hashing (COMPLETE SECURITY):
Client Network Server Database
โ โ โ โ
โ {"password": "testpass123"} โ โ
โโโโโโโโโโโโโโโโโโโโโโ [TLS Encryption] โ โ
โ โ โ โ
โ Encrypted: ๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT๏ฟฝ8o๏ฟฝ๏ฟฝP๏ฟฝ๏ฟฝ๏ฟฝ1๏ฟฝG โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ
ENCRYPTED โ โ
โ โ โ โ
โ โ โ โ
โ [Hacker intercepts] โ โ
โ โ Sees: "๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT๏ฟฝ8o๏ฟฝ๏ฟฝ" โ โ
โ โ Cannot decrypt! โ โ
โ โ โ โ
โ โ [TLS Decryption] โ โ
โ โ "testpass123" โ โ
โ โ โ โ
โ โ passwordEncoder.encode(...) โ
โ โ โ โ
โ โ โ "$2a$10$N9qo..." โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โ
HASHED โ
Result: Hacker sees only encrypted gibberish! โ
The Response is Safe (No Password)โ
{
"id": 4,
"username": "testuser",
"email": "testuser@example.com",
"role": "USER",
"message": "Login successful"
}
This response is safe because:
- โ No password included
- โ Only public data (username, email, role)
- โ Even over HTTP, this response doesn't expose credentials
But the REQUEST was the problem!
Summary Tableโ
| Scenario | Request Security | Database Security | Overall |
|---|---|---|---|
| HTTP + No Hashing | โ Plain text in transit | โ Plain text in DB | โโ Completely insecure |
| HTTP + Hashing | โ Plain text in transit | โ Hashed in DB | โ Still vulnerable |
| HTTPS + No Hashing | โ Encrypted in transit | โ Plain text in DB | โ Still vulnerable |
| HTTPS + Hashing | โ Encrypted in transit | โ Hashed in DB | โ โ Completely secure |
You MUST use BOTH HTTPS and password hashing for complete security!
What Actually Happens When You Enable HTTPS?โ
The "Magic" Explainedโ
When you configure HTTPS in Spring Boot, several components work together to encrypt your traffic automatically. Let's demystify the magic:
HTTPS = HTTP + TLS/SSLโ
HTTP (plain communication)
+
TLS/SSL (encryption protocol)
=
HTTPS (secure communication)
The Components Involvedโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your Spring Boot Application โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ @RestController โ โ
โ โ @PostMapping("/auth/login") โ โ
โ โ public ResponseEntity<LoginResponse> login(...) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Spring Boot (port 8443) โ โ
โ โ - Reads application.properties โ โ
โ โ - Configures SSL/TLS โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Embedded Tomcat Web Server โ โ
โ โ - Listens on HTTPS port โ โ
โ โ - Handles SSL/TLS handshake โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ TLS/SSL Protocol Layer โ โ
โ โ - Encrypts outgoing data โ โ
โ โ - Decrypts incoming data โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ SSL Certificate + Private Key (keystore.p12) โ โ
โ โ - Public key: shared with clients โ โ
โ โ - Private key: kept secret โ โ
โ โ - Certificate: proof of identity โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
Network
The TLS Handshake (The "Magic" โจ)โ
When a client connects to https://localhost:8443, this happens automatically in ~100ms:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 1: Client Hello โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Client โ Server: โ
โ "Hello! I want to establish a secure connection" โ
โ "I support these encryption algorithms: [AES-256, ...]" โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 2: Server Hello + Certificate โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Server โ Client: โ
โ "Hello! Here's my SSL certificate" โ
โ "Here's my public key: [public key data]" โ
โ "Let's use AES-256 encryption" โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 3: Client Verifies Certificate โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Client checks: โ
โ โ Is certificate valid? โ
โ โ Is it issued by trusted authority? โ
โ โ Does domain match? โ
โ โ Is it expired? โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 4: Client Generates Session Key โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Client: โ
โ "I'll generate a random secret key" โ
โ session_key = random_bytes(256) โ
โ "Now I'll encrypt it with server's public key" โ
โ encrypted_key = encrypt(session_key, server_public_key) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 5: Client Sends Encrypted Session Key โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Client โ Server: โ
โ "Here's the session key (encrypted): [encrypted_key]" โ
โ โ
โ โ ๏ธ Even if intercepted, only server can decrypt it! โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 6: Server Decrypts Session Key โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Server: โ
โ "I'll decrypt the session key with my private key" โ
โ session_key = decrypt(encrypted_key, server_private_key) โ
โ "Great! Now we both have the same secret key" โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 7: Secure Communication Established โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Both Client and Server: โ
โ "All future data will be encrypted with session_key" โ
โ โ
โ Client: encrypt(data, session_key) โ Server โ
โ Server: decrypt(data, session_key) โ process โ
โ Server: encrypt(response, session_key) โ Client โ
โ Client: decrypt(response, session_key) โ display โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Result: All communication is now encrypted! ๐
What Your Code Sees vs What TLS Seesโ
Your Java Code (unchanged):
@PostMapping("/auth/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
// You receive plain text here
String email = request.getEmail(); // "test@example.com"
String password = request.getPassword(); // "testpass123"
// Your code works exactly the same!
User user = userRepository.findByEmail(email).orElseThrow(...);
if (passwordEncoder.matches(password, user.getPassword())) {
return ResponseEntity.ok(new LoginResponse(...));
}
throw new RuntimeException("Invalid credentials");
}
What TLS Does (automatically, behind the scenes):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. Incoming Request (from network) โ
โ Encrypted: ๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT๏ฟฝ8o๏ฟฝ๏ฟฝP๏ฟฝ๏ฟฝ๏ฟฝ1๏ฟฝG๏ฟฝ๏ฟฝ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 2. TLS Layer Decrypts โ
โ Decrypted: {"email": "test@...", "password": "pass123"} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 3. Your Code Receives Plain Text โ
โ request.getPassword() = "testpass123" โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 4. Your Code Processes and Returns Response โ
โ return ResponseEntity.ok(new LoginResponse(...)); โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 5. TLS Layer Encrypts Response โ
โ Encrypted: ๏ฟฝ๏ฟฝX๏ฟฝ๏ฟฝ3f๏ฟฝ๏ฟฝY๏ฟฝ๏ฟฝ๏ฟฝ2๏ฟฝP๏ฟฝ๏ฟฝ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 6. Outgoing Response (to network) โ
โ Encrypted: ๏ฟฝ๏ฟฝX๏ฟฝ๏ฟฝ3f๏ฟฝ๏ฟฝY๏ฟฝ๏ฟฝ๏ฟฝ2๏ฟฝP๏ฟฝ๏ฟฝ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
You write zero encryption code! It's all handled by Tomcat + Java's security libraries + operating system.
The Certificate and Keysโ
What's in keystore.p12:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SSL Certificate (keystore.p12) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ 1. Public Key โ
โ - Shared with clients during TLS handshake โ
โ - Used by clients to encrypt session key โ
โ - Safe to share publicly โ
โ โ
โ 2. Private Key โ
โ - Kept secret by server โ
โ - Used to decrypt session key from client โ
โ - NEVER shared with anyone โ
โ โ
โ 3. Certificate Metadata โ
โ - Domain: localhost โ
โ - Valid from: 2025-01-01 โ
โ - Valid until: 2026-01-01 โ
โ - Issued by: Self-signed (for development) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration Triggers the Magicโ
In application.properties:
server.ssl.enabled=true # โ Activates HTTPS
server.ssl.key-store=classpath:keystore.p12 # โ Certificate location
server.ssl.key-store-password=changeit # โ Keystore password
server.ssl.key-store-type=PKCS12 # โ Certificate format
What happens when Spring Boot starts:
1. Spring Boot reads application.properties
โ
2. Sees server.ssl.enabled=true
โ
3. Tells Tomcat: "Enable HTTPS on port 8443"
โ
4. Tomcat loads keystore.p12
โ
5. Tomcat extracts public key + private key
โ
6. Tomcat configures TLS/SSL protocol
โ
7. Tomcat starts listening on HTTPS port
โ
8. Application ready: https://localhost:8443 โ
Why It's "Magic"โ
You don't write any of this code:
// โ You DON'T write this (TLS does it automatically)
String encryptedPassword = TLS.encrypt(request.getPassword());
String decryptedPassword = TLS.decrypt(encryptedPassword);
Instead, you write this:
// โ
You write this (exactly the same as HTTP)
@PostMapping("/auth/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
String password = request.getPassword(); // Already decrypted by TLS!
// ... your business logic
}
The magic:
- โ Encryption/decryption handled by TLS layer
- โ Your code stays simple and unchanged
- โ No performance impact on your business logic
- โ Industry-standard security automatically applied
Performance Impactโ
TLS handshake: ~100ms (one-time per connection) Encryption/decryption: ~1-5ms per request (negligible)
Total overhead: Minimal, worth it for security!
Configuring HTTPS in Spring Bootโ
Step 1: Generate SSL Certificateโ
For development (self-signed certificate):
keytool -genkeypair \
-alias tomcat \
-keyalg RSA \
-keysize 2048 \
-keystore keystore.p12 \
-validity 365 \
-storetype PKCS12 \
-storepass changeit
What this creates:
- File:
keystore.p12(SSL certificate) - Type: PKCS12 (standard format)
- Valid for: 365 days
- Password:
changeit
Move it to your project:
mv keystore.p12 src/main/resources/
Step 2: Configure application.propertiesโ
# Server Configuration
server.port=8443
# SSL/TLS Configuration
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=changeit
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat
# Force HTTPS
server.ssl.protocol=TLS
server.ssl.enabled-protocols=TLSv1.2,TLSv1.3
Step 3: Access via HTTPSโ
Before:
http://localhost:8082/api/auth/login โ Insecure
After:
https://localhost:8443/api/auth/login โ
Secure
Security Best Practicesโ
1. โ Always Use HTTPS in Productionโ
# Production
server.ssl.enabled=true
Never deploy to production without HTTPS!
Get free SSL certificates:
- Let's Encrypt - Free automated certificates
- Certbot - Automatic renewal
- Cloud providers (AWS, Azure, GCP) - Managed certificates
2. โ Hash Passwords (You're Already Doing This!)โ
@Service
public class AuthService {
@Autowired
private PasswordEncoder passwordEncoder;
public void register(RegisterRequest request) {
User user = new User();
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword())); // โ
Hashed!
userRepository.save(user);
}
}
Password flow:
Client sends: "testpass123"
โ (HTTPS encryption during transit)
Server receives: "testpass123"
โ (BCrypt hashing)
Database stores: "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
Even if database is compromised:
- โ Attacker gets hash:
$2a$10$N9qo8... - โ Cannot reverse hash to get
testpass123 - โ Password remains secure
3. โ Never Log Passwordsโ
// โ NEVER DO THIS
@PostMapping("/auth/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
log.info("Login attempt: email={}, password={}",
request.getEmail(),
request.getPassword()); // โ DANGEROUS!
// ...
}
// โ
CORRECT
@PostMapping("/auth/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
log.info("Login attempt: email={}", request.getEmail()); // โ
Safe
// Password never logged
}
4. โ Use Secure Headersโ
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers()
.httpStrictTransportSecurity() // Force HTTPS
.maxAgeInSeconds(31536000) // 1 year
.includeSubDomains(true)
.and()
.contentSecurityPolicy("default-src 'self'");
return http.build();
}
}
HSTS Header:
Strict-Transport-Security: max-age=31536000; includeSubDomains
What it does:
- Forces browser to ALWAYS use HTTPS
- Even if user types
http://, browser converts tohttps:// - Prevents SSL stripping attacks
5. โ Use JWT Tokens After Loginโ
Don't send password on every request!
// โ WRONG: Send password on every request
@GetMapping("/api/profile")
public UserResponse getProfile(@RequestBody LoginRequest request) {
// Validates password every time
}
// โ
CORRECT: Send JWT token after login
@GetMapping("/api/profile")
public UserResponse getProfile(@RequestHeader("Authorization") String token) {
// Validates token (no password needed)
}
Login flow:
1. Client sends: { "email": "...", "password": "..." }
2. Server validates credentials
3. Server generates JWT token
4. Server returns: { "token": "eyJhbGciOiJIUzI1..." }
5. Client stores token
6. Future requests: Authorization: Bearer eyJhbGciOiJIUzI1...
7. No password sent after initial login
When to Use Whatโ
Development (Local Testing)โ
# HTTP is acceptable for local development
server.port=8082
server.ssl.enabled=false
Acceptable because:
- Traffic never leaves your machine
- No network to intercept
- Faster development (no certificate setup)
Staging/Productionโ
# HTTPS is MANDATORY
server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=${SSL_PASSWORD} # From environment variable
Required because:
- Traffic goes through public internet
- Real user credentials at risk
- Compliance requirements (GDPR, PCI-DSS)
Testing HTTPS Locallyโ
Using Postmanโ
Self-signed certificate warning:
Settings โ General โ SSL certificate verification โ OFF
Make request:
POST https://localhost:8443/api/auth/login
Headers:
Content-Type: application/json
Body:
{
"email": "testuser@example.com",
"password": "testpass123"
}
Using cURLโ
# Accept self-signed certificate
curl -k -X POST https://localhost:8443/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "testuser@example.com",
"password": "testpass123"
}'
Using Browserโ
https://localhost:8443/api/auth/login
Browser warning:
โ ๏ธ Your connection is not private
This site uses a self-signed certificate
[Advanced] โ [Proceed to localhost (unsafe)]
Accept for development only!
The Complete Security Stackโ
Layer 1: HTTPS (Transport Security)โ
Encrypts data in transit
Prevents network interception
Layer 2: Password Hashing (Storage Security)โ
passwordEncoder.encode(request.getPassword())
// Plain: "testpass123"
// Hashed: "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
Layer 3: JWT Tokens (Session Security)โ
// After login, use tokens instead of passwords
String token = jwtService.generateToken(user);
// Future requests use token, not password
Layer 4: Spring Security (Authorization)โ
http
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated();
Real-World Attack Scenariosโ
Scenario 1: Coffee Shop WiFi (HTTP)โ
Setup:
- You're at Starbucks
- Connected to public WiFi
- Your app uses HTTP
Attack:
Your Laptop
โ (sends password in plain text)
WiFi Router
โ (hacker captures packet)
Hacker's Wireshark
โ Sees: "testpass123"
Result: Account compromised! ๐จ
Scenario 2: Coffee Shop WiFi (HTTPS)โ
Setup:
- You're at Starbucks
- Connected to public WiFi
- Your app uses HTTPS
Attack:
Your Laptop
โ (sends encrypted password)
WiFi Router
โ (hacker captures packet)
Hacker's Wireshark
โ Sees: "๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT๏ฟฝ8o๏ฟฝ๏ฟฝP"
Result: Password safe! โ
Scenario 3: Man-in-the-Middle Attackโ
HTTP (vulnerable):
Client โ Attacker โ Server
(intercepts and reads everything)
HTTPS (protected):
Client โ Attacker โ Server
(sees encrypted data only)
(cannot decrypt without private key)
Common Misconceptionsโ
โ "Password hashing is enough"โ
Wrong!
Hashing protects stored passwords, not transmitted passwords.
HTTP + Hashing:
Client: "password123" (plain text in transit) โ Network sees: "password123"
Server: hashes to "$2a$10$..." โ Database stores hash
Attacker intercepts: "password123" โ Can login!
HTTPS + Hashing:
HTTPS + Hashing:
Client: "password123" โ Encrypted to "๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT" โ Network sees: "๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT"
Server: decrypts, hashes to "$2a$10$..." โ Database stores hash
Attacker intercepts: "๏ฟฝ๏ฟฝk๏ฟฝ๏ฟฝvT" โ Cannot decrypt or use!
โ "Only big companies need HTTPS"โ
Wrong!
ALL websites handling passwords need HTTPS:
- Personal blogs with login
- Small e-commerce sites
- Internal corporate apps
- Student projects (if deployed publicly)
โ "HTTPS is expensive"โ
Wrong!
Free options:
- Let's Encrypt (free certificates)
- Cloudflare (free SSL)
- Cloud providers (often included)
Summaryโ
The Questionโ
"Is it safe to send passwords in LoginRequest? Can someone intercept it?"
The Answerโ
It depends on HTTP vs HTTPS:
| Protocol | Safety | Can Be Intercepted? |
|---|---|---|
| HTTP | โ Unsafe | โ Yes (plain text) |
| HTTPS | โ Safe | โ No (encrypted) |
Security Layersโ
Layer 1: HTTPS โ Protects transmission (network security)
Layer 2: Password Hash โ Protects storage (database security)
Layer 3: JWT Tokens โ Protects sessions (authentication security)
Layer 4: Spring Security โ Protects endpoints (authorization security)
Quick Referenceโ
Development (localhost):
server.ssl.enabled=false # HTTP is acceptable
Production:
server.ssl.enabled=true # HTTPS is MANDATORY
server.ssl.key-store=classpath:keystore.p12
Best Practices:
- โ Always use HTTPS in production
- โ Hash passwords with BCrypt/Argon2
- โ Never log passwords
- โ Use secure headers (HSTS)
- โ Use JWT tokens after login (don't send password repeatedly)
Bottom line: HTTPS encrypts passwords during transmission. Password hashing protects stored passwords. You need BOTH for complete security. Never deploy to production without HTTPS! ๐
