Tuesday, October 7, 2025

🧠 How to Upgrade Your Spring Boot Login for Full OWASP Protection (XSS, CSRF, HttpOnly JWT)

 Modern web apps often use localStorage for JWTs — but that’s risky.

localStorage is accessible to JavaScript, so an XSS attack can easily steal your token.
The proper way: use HttpOnly cookies + CSRF tokens.



Here’s how to transform your existing /login endpoint securely without breaking Kafka, Redis caching, or rate limiting.


🪜 Step-by-Step Migration Plan

Step 1: Switch from LocalStorage to HttpOnly Secure Cookie

  • Instead of returning the JWT in the response body, set it as an HttpOnly, Secure, SameSite=Strict (or Lax) cookie.

  • These cookies can’t be accessed by JavaScript — protecting against XSS.

  • No change is needed in your Kafka or Redis logic — they’ll continue working because you’re just changing how the token is delivered, not the backend authentication logic.

💡 Kafka login notifications and Redis login limiters will remain unaffected, since they trigger before token issuance.


 


Step 2: Introduce a CSRF Token

  • When a user logs in, generate a CSRF token (a random UUID).

  • Store this token:

    • Option 1: in Redis (recommended if you already use Redis)

    • Option 2: in an SQL table (csrf_tokens)

  • Send this token as a non-HttpOnly cookie or via a header (so frontend can read it).

  • Frontend includes this token in every state-changing request header:

X-CSRF-TOKEN: <token>

🛡️ The backend will reject any POST/PUT/DELETE without a valid CSRF token that matches the user’s session or Redis entry.


Step 3: Secure Cookie Configuration

Update your application.properties:

server.servlet.session.cookie.http-only=true

server.servlet.session.cookie.secure=true

server.servlet.session.cookie.same-site=Strict

If your frontend and backend are on different domains:

server.servlet.session.cookie.domain=.yourdomain.com

Step 4: CORS Configuration (Critical)

When using cookies for auth:

  • You must enable credentials and disable wildcards (*).

Step 5: Frontend Adjustments

  • Remove localStorage usage for JWTs.

  • ✅ Use fetch or axios

  • Store only the CSRF token in memory or sessionStorage.

  • For POST/PUT/DELETE requests

  • Handle 403 (CSRF error) responses gracefully — show a message like “Session expired, please refresh or re-login.”

Step 6: Optional — Add Session Mapping (for Admin Panels or Token Revocation)

If you want to track or revoke tokens:

  • Add a session_id column in DB or Redis mapping:

session_id -> jwt_id
  • On logout or admin disable, revoke by session ID.


Step 7: Test OWASP Protections

Verify:

  • No JWT in localStorage or sessionStorage

  • ✅ Cookies have HttpOnly, Secure, and SameSite flags

  • ✅ CSRF token mismatch returns 403

  • ✅ XSS payloads can’t read cookies

  • ✅ Rate limiter still blocks excessive login attempts

  • ✅ Kafka still receives login notifications


⚙️ Additional Considerations

🗄️ Database Changes

  • Optional: Add csrf_tokens table or store in Redis (csrf:{sessionId} → token).

🔧 Config Updates

  • Add cookie + CORS settings to application.properties.

  • Ensure backend sends cookies via ResponseCookie.from() in Spring.

💻 Frontend

  • Remove token storage logic.

  • Add withCredentials: true in requests.

  • Always attach X-CSRF-TOKEN header on write requests.


✅ Summary

Protection Mechanism Mitigates
HttpOnly cookie     JWT in secure cookie         XSS
CSRF token     Separate token validation         CSRF
Input sanitization (already using Jsoup)     Clean username/password         Injection
Rate limiting (already in place)     IP-based limiter         Brute force
Kafka login events     Audit trail         Security monitoring

Monday, October 6, 2025

PenTesting:⚠️ The Hidden Danger of Storing Roles in Local Storage in React

 When building a React app, it’s common to store simple user data — like names or themes — in localStorage. It’s convenient, persistent, and easy to use:

localStorage.setItem("role", "ADMIN");

But here’s the problem: anything stored in localStorage is 100% visible and editable by anyone who opens your website.



🧠 What Is localStorage?

localStorage is a small key-value database built into every browser. It’s meant for client-side storage, not secure data.

You can see and change it anytime using the browser’s DevTools → Application → Local Storage tab.


🔓 The Common Mistake

Many developers (especially beginners) use localStorage to store login-related info, such as:

localStorage.setItem("role", "ADMIN");

Then they rely on it in React to control access:

onst role = localStorage.getItem("role");

if (role === "ADMIN") {

  // show admin dashboard

}

At first glance, this looks fine. But anyone can open the browser console and simply run:

localStorage.setItem("role", "ADMIN");

location.reload();

What if the hacker does not the items in the localstorage?  The answer is there is a way to view all items in the localstorage. Just type:

console.table(Object.entries(localStorage));


 

 

Boom 💥 — the app now thinks they’re an admin.
All admin menus, buttons, or “restricted” pages might appear instantly.


🧱 But Wait — Does That Mean They’re Actually Admin?

It depends on your backend.

  • ❌ If your backend does not verify roles (e.g., it trusts the frontend), your data is wide open.

  • ✅ If your backend does verify permissions using tokens or sessions, the fake “admin” role on the frontend only affects the UI — the hacker still can’t perform real admin actions.


🛡️ How to Do It Safely

  1. Never trust localStorage for authorization.
    It’s fine for cosmetic data (like theme = "dark"), but not for anything related to user identity or privileges.

  2. Use JWTs or sessions for secure authentication.
    Store a signed token (like a JWT) that your backend verifies with every request.
    The token itself should contain the user’s role — and the backend should confirm it before granting access.

  3. Let the backend decide what data to send.
    For example, instead of giving all users /admin/data, make the server check the token and respond with “403 Forbidden” if the user isn’t authorized.

  4. Hide UI elements based on real backend responses.
    Don’t just rely on what’s in localStorage — render admin features only after verifying the user’s role from the backend.


🕵️‍♂️ The Bottom Line

If your app’s security depends on a value stored in localStorage, then your app is not secure.

localStorage should be treated like a sticky note on a public computer — convenient, but visible (and editable) to anyone.


✅ TL;DR

WhatSafe?Notes
Theme preferenceCosmetic only
Username⚠️Not sensitive but editable
Access token (JWT)⚠️OK if short-lived and validated by backend
User role / admin flagNever store as plain text
Passwords / secrets🚫Never, ever store here

Saturday, October 4, 2025

Implementing Real-Time Admin Notifications with Kafka in Spring Boot Microservices

 

Introduction

In modern microservices architectures, real-time event-driven communication is essential for building responsive applications. This post walks through implementing Kafka-based admin notifications across two independent Spring Boot microservices: a Todo App (handling user management) and a Blog service (handling content and comments).

Architecture Overview

Our setup consists of:

  • Todo App Microservice (Port: 8081) - Manages users and authentication
  • Blog Microservice (Port: 8083) - Manages blog posts and comments
  • Kafka - Message broker for inter-service communication
  • WebSocket - Real-time push notifications to frontend
  • React Frontend - Displays toast and bell notifications

Both services run independently but communicate through Kafka topics to deliver real-time notifications to administrators.

Use Cases

  1. Todo App: Admin receives notification when any user logs in
  2. Blog Service: Admin receives notification when any user posts a comment (since comments require moderation)

Implementation Steps

Step 1: Create BlogProducer.java

The producer is responsible for publishing events to Kafka when significant actions occur in your application.

Step 2: Create BlogConsumer.java

The consumer listens to Kafka messages and forwards them to WebSocket clients in real-time.

Step 3: Create WebSocketConfig.java

Configure WebSocket endpoints to enable real-time communication with the frontend.

Step 4: Update CommentController/Service

Integrate the producer into your existing comment creation logic.

Step 5: Update Frontend - AdminNotifications.js

Connect to the WebSocket endpoint and display notifications.

Step 6: Required Dependencies

Add  dependencies to your pom.xml.

Add to configure application.properties.

The Complete Flow

  1. User posts a comment on a blog post
  2. CommentController saves the comment and calls BlogProducer
  3. BlogProducer publishes message to Kafka topic blog-notifications
  4. BlogConsumer (listening to Kafka) receives the message
  5. BlogConsumer forwards message to WebSocket endpoint /topic/admin-notifications
  6. Frontend (connected via WebSocket) receives the notification
  7. Toast notification appears on screen
  8. Bell icon updates with unread count

Testing Your Implementation

  1. Start Kafka and Zookeeper
  2. Start both microservices (Todo App and Blog)
  3. Login as an admin user
  4. Open browser console to verify WebSocket connection
  5. Post a comment (either as admin or regular user)
  6. Verify toast notification appears
  7. Verify bell icon shows unread count

Benefits of This Architecture

  • Decoupled Services: Todo and Blog services remain independent
  • Scalability: Kafka handles high message throughput
  • Real-time Updates: WebSocket provides instant notifications
  • Event-Driven: Easy to add more notification types
  • Fault Tolerance: Kafka persists messages if consumers are temporarily down

Extending the System

You can easily extend this pattern for additional notifications:

  • User registration events
  • Post publication notifications
  • Comment approval/rejection alerts
  • System-wide announcements

Simply create new Kafka topics and add corresponding producers/consumers for each event type.

Conclusion

By combining Kafka for inter-service communication and WebSocket for real-time frontend updates, we've built a robust notification system across independent microservices. This architecture scales well and provides the foundation for building more complex event-driven features in your application.

🧠 How to Upgrade Your Spring Boot Login for Full OWASP Protection (XSS, CSRF, HttpOnly JWT)

 Modern web apps often use localStorage for JWTs — but that’s risky. localStorage is accessible to JavaScript , so an XSS attack can easi...