security: fix missing trip access checks on Immich routes (GHSA-pcr3-6647-jh72)

security: require auth for uploaded photos (GHSA-wxx3-84fc-mrx2)

GHSA-pcr3-6647-jh72 (HIGH):
- Add canAccessTrip check to all /trips/:tripId/photos and
  /trips/:tripId/album-links endpoints
- Prevents authenticated users from accessing other trips' photos

GHSA-wxx3-84fc-mrx2 (LOW):
- /uploads/photos now requires JWT auth token or valid share token
- Covers and avatars remain public (needed for login/share pages)
- Files were already blocked behind auth
This commit is contained in:
Maurice
2026-04-01 15:46:08 +02:00
parent ef5b381f8e
commit 16277a3811
2 changed files with 23 additions and 2 deletions
+15 -1
View File
@@ -131,7 +131,7 @@ import { authenticate } from './middleware/auth';
app.use('/uploads/avatars', express.static(path.join(__dirname, '../uploads/avatars')));
app.use('/uploads/covers', express.static(path.join(__dirname, '../uploads/covers')));
// Serve uploaded photos (public — needed for shared trips)
// Serve uploaded photos — require auth token or valid share token
app.get('/uploads/photos/:filename', (req: Request, res: Response) => {
const safeName = path.basename(req.params.filename);
const filePath = path.join(__dirname, '../uploads/photos', safeName);
@@ -140,6 +140,20 @@ app.get('/uploads/photos/:filename', (req: Request, res: Response) => {
return res.status(403).send('Forbidden');
}
if (!fs.existsSync(resolved)) return res.status(404).send('Not found');
// Allow if authenticated or if a valid share token is present
const authHeader = req.headers.authorization;
const token = req.query.token as string || (authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null);
if (!token) return res.status(401).send('Authentication required');
try {
const jwt = require('jsonwebtoken');
jwt.verify(token, process.env.JWT_SECRET || require('./config').JWT_SECRET);
} catch {
// Check if it's a share token
const shareRow = db.prepare('SELECT id FROM share_tokens WHERE token = ?').get(token);
if (!shareRow) return res.status(401).send('Authentication required');
}
res.sendFile(resolved);
});