This guide provides step-by-step instructions for implementing critical security fixes to the Yiku Thumbnail Service.
- Node.js service running
- Access to server.js file
- Environment variables configured
Add this to your .env file:
API_KEY=your-super-secure-api-key-hereAdd this code at the top of server.js after the imports:
// Authentication middleware
const authenticateApiKey = (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({
success: false,
error: 'API key required'
});
}
if (apiKey !== process.env.API_KEY) {
return res.status(401).json({
success: false,
error: 'Invalid API key'
});
}
next();
};Add authenticateApiKey to all your endpoints:
// Before (example)
app.post('/generate', async (req, res) => {
// After (example)
app.post('/generate', authenticateApiKey, async (req, res) => {Apply to these endpoints:
/generate/update-video-thumbnail/cleanup/cleanup-wrong-bucket/cleanup-wrong-structure
Don't apply to these endpoints (keep public):
/health/status/test-supabase/debug-env/metadata/:filename
Change this line in server.js:
// Before
app.listen(PORT, '0.0.0.0', async () => {
// After
app.listen(PORT, '127.0.0.1', async () => {This restricts access to localhost only.
npm install express-rate-limitAdd this code after your imports in server.js:
const rateLimit = require('express-rate-limit');
// Rate limiting middleware
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: {
success: false,
error: 'Too many requests, please try again later.'
},
standardHeaders: true,
legacyHeaders: false,
});
// Apply rate limiting to all routes
app.use(limiter);npm install express-validatorReplace your current /generate endpoint with this:
const { body, validationResult } = require('express-validator');
app.post('/generate', authenticateApiKey, [
body('video_url')
.isURL()
.matches(/^gs:\/\/.*\.mp4$/)
.withMessage('Video URL must be a valid Google Cloud Storage MP4 URL'),
body('video_id')
.isString()
.isLength({ min: 1, max: 100 })
.withMessage('Video ID must be a string between 1 and 100 characters')
], async (req, res) => {
// Check for validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
error: 'Validation failed',
details: errors.array()
});
}
// Your existing generate logic here
try {
const { video_url, video_id } = req.body;
// ... rest of your existing code
} catch (error) {
// ... error handling
}
});npm install corsAdd this after your imports in server.js:
const cors = require('cors');
// CORS configuration
app.use(cors({
origin: [
'http://localhost:3000',
'http://127.0.0.1:3000',
'https://yourdomain.com' // Replace with your actual domain
],
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'x-api-key'],
credentials: false
}));# This should fail (no API key)
curl -X POST http://localhost:3000/generate \
-H "Content-Type: application/json" \
-d '{"video_url": "gs://test/video.mp4", "video_id": "test"}'
# This should fail (wrong API key)
curl -X POST http://localhost:3000/generate \
-H "Content-Type: application/json" \
-H "x-api-key: wrong-key" \
-d '{"video_url": "gs://test/video.mp4", "video_id": "test"}'
# This should work (correct API key)
curl -X POST http://localhost:3000/generate \
-H "Content-Type: application/json" \
-H "x-api-key: your-super-secure-api-key-here" \
-d '{"video_url": "gs://test/video.mp4", "video_id": "test"}'# Test rate limiting (run this multiple times quickly)
for i in {1..150}; do
curl -X GET http://localhost:3000/health
echo "Request $i"
done# This should fail (invalid URL)
curl -X POST http://localhost:3000/generate \
-H "Content-Type: application/json" \
-H "x-api-key: your-super-secure-api-key-here" \
-d '{"video_url": "invalid-url", "video_id": "test"}'
# This should fail (missing video_id)
curl -X POST http://localhost:3000/generate \
-H "Content-Type: application/json" \
-H "x-api-key: your-super-secure-api-key-here" \
-d '{"video_url": "gs://test/video.mp4"}'Update your .env file to include:
# Add this line to your existing .env file
API_KEY=your-super-secure-api-key-hereAfter making these changes:
# Stop the current service
pkill -f "node server.js"
# Start the service again
cd /path/to/yiku-thumbnail
node server.js- API key authentication works on protected endpoints
- Public endpoints (health, status) remain accessible
- Rate limiting prevents abuse
- Input validation blocks invalid requests
- Service only accessible from localhost
- All endpoints return proper error messages
After implementing these immediate fixes:
- Test thoroughly in your development environment
- Update your N8N workflow to include the
x-api-keyheader - Deploy to production
- Monitor logs for any issues
- Consider implementing the medium and low priority security measures
- Never commit your
.envfile to version control - Use a strong, unique API key (32+ characters, mix of letters, numbers, symbols)
- Rotate your API key periodically
- Monitor your logs for suspicious activity
- Keep your dependencies updated regularly