Implemented a complete secure backend solution to eliminate client-side API key storage vulnerabilities. All sensitive credentials are now encrypted on the backend using AES-256-GCM encryption.
server/package.json- Backend dependencies (Express, CORS, dotenv)- Note:
cryptois a built-in Node.js module and doesn't need to be installed
- Note:
server/index.js- Express server with secure API endpointsserver/crypto.js- AES-256-GCM encryption/decryption utilitiesserver/.env.example- Environment variable templateserver/.gitignore- Prevents committing .env file
src/utils/backend-api.ts- Frontend utilities for backend communication
SECURITY.md- Comprehensive security policy and implementation detailsSETUP.md- Complete setup and installation guide.env.example- Frontend environment template
-
src/pages/Settings.tsx- Added API key input handling with secure backend calls
- Integrated encryption check status display
- Added save/delete API key functionality
- Import Lock icon from lucide-react
- Integrated
backend-api.tsutilities
-
src/store/useJobStore.ts- Added security comment about not persisting sensitive data
- Configured
partializeoption to exclude API keys from localStorage - API keys now handled exclusively by backend
-
vite.config.ts- Added server port configuration for development
-
package.json- Added
"server": "cd server && npm run dev"script - Added
"server:install": "cd server && npm install"script
- Added
-
.gitignore- Added
.env,.env.local,.env.*.localto prevent committing secrets
- Added
-
README.md- Added "🔒 Security First" section at the top
- Added comprehensive "🔒 Security & Setup" section
- Updated with backend setup instructions
- Added API key management explanation
- Added production deployment security checklist
User Browser
├── Settings Page
│ └── [User enters API key]
│ └── Stored in localStorage
│ └── Accessible to JavaScript/XSS attacks
│ └── Used directly for API calls
User Browser Backend Server External APIs
├── Settings Page ├── Express Server ├── OpenAI
│ └── [User enters key] │ ├── Validates │ │
│ └── HTTPS POST │ ├── Encrypts (AES) │ │
│ └── Encrypted │ ├── Stores │ │
│ │ │ └── Uses for calls ├─→ DeepSeek
│ └────────→│ │
│ └────────────────────→└──
│ ← Receives Response ←──────────────────────┘
│ (No key exposed)
└─ Displays Result
- ✅ Algorithm: AES-256-GCM (military-grade)
- ✅ Key Derivation: SCRYPT (NIST-recommended)
- ✅ Random IV: Unique per encryption operation
- ✅ Authentication Tag: Prevents tampering
POST /api/keys/set → Store encrypted API key
GET /api/keys/check/:provider → Check if key exists (no key returned)
POST /api/keys/delete/:provider → Delete stored key
POST /api/ai/generate → Use stored key for AI generation
- ✅ Restricted to specific frontend origin
- ✅ Configurable via
FRONTEND_URLenv var - ✅ Prevents cross-site attacks
- ✅
.envfiles never committed to git - ✅
.gitignoreprevents accidental commits - ✅
.env.exampleshows what's needed
- ✅ API keys NOT stored in localStorage
- ✅ Only provider/model selection persisted
- ✅ Zustand store uses
partializeto exclude sensitive data
1. User enters API key in Settings
2. Frontend sends via HTTPS to /api/keys/set
3. Backend validates format
4. Backend encrypts with AES-256-GCM
5. Backend stores in memory/database
6. Frontend receives success message
7. Input field cleared (key never stored frontend)
1. User requests AI generation
2. Frontend sends request to /api/ai/generate
3. Backend retrieves encrypted key
4. Backend decrypts key
5. Backend calls OpenAI/DeepSeek API
6. Backend returns response
7. Frontend displays result (key never exposed)
| Vulnerability | Impact | Fix |
|---|---|---|
| Keys in localStorage | XSS attacks could steal keys | Keys now on backend only |
| Keys in state | Browser dev tools could expose keys | No sensitive data in frontend state |
| Direct API calls | Man-in-the-middle attacks | Backend makes calls with HTTPS |
| Unencrypted storage | Database breach exposes keys | AES-256-GCM encryption |
| Client-side key handling | Multiple attack vectors | Backend-only key management |
# Install dependencies
npm install
npm run server:install
# Configure
cd server && cp .env.example .env
# Edit server/.env with your API keys
cd ..
cp .env.example .env.local
# Edit .env.local with VITE_API_URL=http://localhost:3001
# Run
# Terminal 1:
npm run server
# Terminal 2:
npm run devSee SETUP.md for detailed instructions.
- Use environment variables for all secrets
- Store API keys only on backend
- Use HTTPS in production
- Rotate API keys regularly
- Monitor API usage
- Keep dependencies updated
- Commit .env files
- Use hardcoded API keys
- Log sensitive information
- Expose backend responses containing keys
- Skip HTTPS in production
- Share SESSION_SECRET
- Backend starts without errors:
npm run server - Frontend connects to backend
- Can set API key in Settings
- API key status shows as configured
- Can delete API key
- AI generation works with stored key
- Key is not visible in browser storage (F12 → Application → Storage)
- Key is not visible in network requests (F12 → Network)
- Refresh page - settings still work (server-side persistence)
- Set
NODE_ENV=production - Use HTTPS/TLS certificates
- Generate strong SESSION_SECRET
- Configure FRONTEND_URL with HTTPS
- Set up monitoring and logging
- Implement rate limiting
- Use a process manager (PM2, systemd, Docker)
- Keep dependencies updated
See SECURITY.md for full production checklist.
README.md- Main project documentation with security sectionSECURITY.md- Detailed security policy and implementationSETUP.md- Complete installation and configuration guideCHANGES.md- This file, summary of all changes
If you were using an older version without the backend:
- Stop the old application
- Install new dependencies:
npm install && npm run server:install - Configure
.envfiles (see SETUP.md) - Start backend:
npm run server - Start frontend:
npm run dev - Enter your API keys in Settings (they're stored securely now)
- Old browser storage is cleared automatically
- 📖 See SETUP.md for installation help
- 🔒 See SECURITY.md for security questions
- 📚 See README.md for feature documentation
✅ API keys now encrypted with AES-256-GCM
✅ Backend-only key storage (no browser exposure)
✅ Comprehensive security documentation
✅ Complete setup and deployment guides
✅ Production-ready security checklist
✅ Removed unnecessary crypto dependency (uses Node.js built-in module)
Your job search data is now secure! 🔐