SeniorДебаггингЧастоЕщё не отвечали
Найдите уязвимости в money-transfer сервисе на Spring
Этот сервис на Spring переводит деньги между счетами. Найдите дефекты безопасности и назовите исправление для каждого.
Ограничения:
transferчитает баланс и пишет его отдельными шагами при конкурентностиamount—BigDecimalиз запроса без ограничений- Spring Boot Actuator опубликован (см. конфиг ниже), консоль H2 включена
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account from = repo.findById(fromId); // нет проверки владения
if (from.getBalance().compareTo(amount) >= 0) { // нет проверки amount > 0
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
}
}
// application.yml: management.endpoints.web.exposure.include: "*"
Определите причины.
Гонка: чтение-проверка-запись не атомарны — фикс транзакцией с пессимистичной блокировкой. Валидация: отрицательный amount обходит проверку остатка. Авторизация: нет проверки владения fromId, а Actuator (heapdump) и H2 утекают данные. DoS: неограниченный BigDecimal — лимит. Добавить @ControllerAdvice.
- ✗Лечить гонку через synchronized вместо транзакции с блокировкой в БД
- ✗Считать Actuator и консоль H2 безопасными по умолчанию
- ✗Пропускать проверку владения счётом и знаком суммы
- →Почему пессимистичная блокировка в транзакции надёжнее synchronized для перевода?
- →Как отрицательная сумма обходит проверку остатка в этом коде?
Оглавление
Уязвимости
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account from = repo.findById(fromId);
if (from.getBalance().compareTo(amount) >= 0) {
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
}
}
- Состояние гонки (TOCTOU). Чтение баланса, проверка и запись не атомарны: два параллельных перевода оба пройдут проверку и уведут баланс в минус.
- Нарушение валидации ввода. Нет проверки
amount > 0: отрицательная сумма проходитcompareTo(...) >= 0и приsubtractпополняет отправителя. - Нарушение авторизации. Нет проверки, что
fromIdпринадлежит вызывающему. Публикация*в Actuator открываетheapdump, консоль H2 — без авторизации. - DoS. Неограниченный
BigDecimal(огромные числа) деградирует сервис. - Плохая обработка ошибок. Исключения уходят на экран со stack trace.
Исправление
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
if (amount.signum() <= 0 || amount.compareTo(MAX) > 0) throw new BadRequest();
Account from = repo.findByIdForUpdate(fromId); // пессимистичная блокировка
if (!from.ownedBy(currentUser())) throw new Forbidden();
if (from.getBalance().compareTo(amount) >= 0) { /* списание/зачисление */ }
}
✅ Транзакция с пессимистичной блокировкой убирает гонку; проверка знака и диапазона суммы; проверка владения; ограничение публикации Actuator и H2; @ControllerAdvice прячет stack trace.
Оглавление