Создание массовых заданий Шпаргалка Безопасные контроллеры

Если вы начинаете свою карьеру Java-разработчика и используете Spring MVC для создания своих REST API, вы, наверное, уже задавались вопросом, почему сама сущность не используется для получения данных в контроллере?

Чтобы ответить на этот вопрос, я должен рассказать вам странную историю, которая произошла с GitHub. В 2012 году хакеру удалось использовать недостаток в форме ввода, что позволило ему присоединиться к организации Ruby on Rails через открытый ключ ssh. После присоединения этот пользователь получил необходимое разрешение на внесение изменений в хранилища организации.

Вероятно, это произошло потому, что он смог сохранить свой ключ доступа в коллекции разрешенных ключей организации. Когда пользователь может нежелательным образом изменить значения объектов через параметры HTTP-запроса, мы называем это уязвимостью массового присвоения.

Для лучшего понимания представьте, что злоумышленник обнаруживает, что можно зарегистрировать банковский счет с разрешенным для использования значением баланса через запрос на создание счета. Такой пользователь может просто перевести эту ценность до того, как дефект будет выявлен, и нанести серьезный ущерб банку.

Примером API, позволяющего выполнить эту операцию, является:

@Entity
public class Conta{
    @Id
    @GenratedValue
    private Long id;
    private String titular;
    private BigDecimal saldo;
}


@RestController
public class CadastrarConta{
    @AutoWired
    private ContaRepository repository;

    @PostMapping("/contas")
    public ResponseEntity<?> cadastar(@RequestBody Conta conta, UriComponentsBuilder uriBuilder){
        repository.save(conta);

        URI location = uriBuilder.path("conta/credito/{id}")
                .buildAndExpand(conta.getId())
                .toUri();

        return ResponseEntity.created(location).build();
    }
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Однако разработчик уточняет, что для создания аккаунта необходимо отправить в теле запроса только имя владельца аккаунта. Как в примере ниже:

POST /contas
...
body: {
    "titular": "Jordi Henrique Marques da Silva"
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Злонамеренный пользователь мог просто ввести баланс в свой запрос и запустить свой счет с имеющимся значением в работу. Например:

POST /contas
...
body: {
    "titular": "Jordi Henrique Marques da Silva",
    "saldo":1000000.00
}
Войдите в полноэкранный режим Выход из полноэкранного режима

И это причина, по которой мы не получаем сущность в качестве шлюза в контроллере, поскольку мы уязвимы к этому недостатку безопасности.

Простым решением этой проблемы является вставка объекта для представления регистрационной информации. Этот объект является реализацией шаблона проектирования Data Transfer Object (DTO). В этот объект мы вставляем только атрибуты, связанные с регистрацией аккаунта, не позволяя пользователю получить доступ к другим полям сущности. Ниже приведен пример того, как зарегистрировать учетную запись:

public class CadastroContaDTO{
    private String titular;

    public Conta toModel(){
        return new Conta(titular);
    }
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Чтобы сделать наш контроллер безопасным, мы можем теперь заменить тип объекта счета с Account на AccountAccountDTO в методе register и адаптировать логику регистрации.

@RestController
public class CadastrarConta{
    @AutoWired
    private ContaRepository repository;

    @PostMapping("/contas")
    public ResponseEntity<?> cadastar(@RequestBody CadastroContaDTO request, UriComponentsBuilder uriBuilder){

        Conta conta = request.toModel();

        repository.save(conta);

        URI location = uriBuilder.path("conta/credito/{id}")
                .buildAndExpand(conta.getId())
                .toUri();

        return ResponseEntity.created(location).build();
    }
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Адаптированный контроллер выглядит как код, представленный выше, и помимо того, что он не уязвим для атаки массового присвоения, наличие входного DTO позволяет моделям развиваться независимо, то есть моя сущность может получить новые поля без изменения моего DTO и наоборот.

Наличие входного или выходного DTO также может привести к оптимизации трафика данных в сети, поскольку DTO содержит только необходимую информацию для удовлетворения функциональности, поля, которые ранее были сериализованы, трафикованы и не использовались, теперь больше не являются частью сообщения.

Заключение

Получение сущности в контроллере может нанести большой ущерб безопасности вашего приложения и бизнеса, но вставка DTO для получения этой информации дает несколько преимуществ, таких как повышенная безопасность от уязвимостей, развязка между API и бизнес-моделями, а также выигрыш в производительности по отношению к данным, передаваемым по сети.

Оцените статью
devanswers.ru
Добавить комментарий