Самая массовая категория. Основные паттерны:
Эндпоинт /api/documents/:id проверял аутентификацию, но не проверял, принадлежит ли документ текущему пользователю:
// УЯЗВИМЫЙ КОД
app.get('/api/documents/:id', auth, async (req, res) => {
const doc = await Document.findByPk(req.params.id);
if (!doc) return res.status(404).json({ error: 'Not found' });
res.json(doc); // Любой авторизованный юзер видит любой документ
});
// ИСПРАВЛЕННЫЙ КОД
app.get('/api/documents/:id', auth, async (req, res) => {
const doc = await Document.findOne({
where: {
id: req.params.id,
userId: req.user.id, // Проверка владельца
},
});
if (!doc) return res.status(404).json({ error: 'Not found' });
res.json(doc);
});
Мы нашли 7 эндпоинтов с аналогичной проблемой: документы, выписки, шаблоны переводов, настройки уведомлений. Для системного исправления внедрили middleware авторизации на уровне ресурса:
// middleware/ownership.js
function ownershipCheck(model, ownerField = 'userId') {
return async (req, res, next) => {
const resource = await model.findByPk(req.params.id);
if (!resource) {
return res.status(404).json({ error: 'Not found' });
}
if (resource[ownerField] !== req.user.id) {
// Логируем попытку доступа к чужому ресурсу
logger.warn('IDOR attempt', {
userId: req.user.id,
resourceId: req.params.id,
model: model.name,
ip: req.ip,
});
return res.status(404).json({ error: 'Not found' });
}
req.resource = resource;
next();
};
}
// Использование
app.get('/api/documents/:id', auth, ownershipCheck(Document), (req, res) => {
res.json(req.resource);
});
Оставить комментарий