Criando Controllers seguros do Mass Assignment Cheat Sheet
Tags: webdev, java, spring
Published: 2022-07-25 17:30:00

Se você esta iniciando sua carreira como Desenvolvedor(a) Java e utiliza Spring MVC para construir suas APIs REST já deve ter se  perguntado o por quê não é utilizado a própria entidade para receber dados no **_Controller_**?

Para responder essa pergunta preciso te contar uma história bizarra que aconteceu com o GitHub.  Em 2012 um hacker conseguiu explorar uma falha em um formulário de entrada, que permitiu ele se associar a organização do [Ruby on Rails](https://github.com/rails) através de uma [chave ssh publica](https://docs.github.com/pt/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent). Após se associar este usuário possuía permissão necessária para fazer alterações nos repositórios da organização.

Isto aconteceu porquê provavelmente ele conseguiu persistir a sua chave de acesso na coleção de chaves permitidas da organização. Quando um usuário consegue alterar valores de objetos de maneira indesejada através de parâmetros de uma requisição HTTP, chamamos de [Vulnerabilidade de Atribuição de Massa](https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html).

Para entender melhor imagine que um usuário mal-intencionado descobre que é possível cadastrar uma conta bancaria com um valor de saldo aprovado para uso através da requisição de criação de conta. Este usuário poderia simplesmente transferir este valor antes que a falha fosse identificada e causar sérios prejuízos ao banco.

Um exemplo de API que permita esta operação é:

```java
@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();
    }
}
```

Por mais que o desenvolvedor especifique que para criar uma conta é necessário enviar apenas o nome do titular no corpo da requisição. Como no exemplo abaixo:

```
POST /contas
...
body: {
    "titular": "Jordi Henrique Marques da Silva"
}
```

O usuário mal-intencionado poderia simplesmente informar o saldo na sua requisição e iniciar sua conta com valor disponível em operação. Por exemplo:

```
POST /contas
...
body: {
    "titular": "Jordi Henrique Marques da Silva",
    "saldo":1000000.00
}
```

E este é o motivo do porquê não recebemos a entidade como porta de entrada de dados no **_Controller_**, pois ficamos vulneráveis a esta falha de segurança.

Uma solução simples para este problema é inserir um objeto para representar as informações de cadastro. Este objeto é uma implementação do padrão de projeto [Data Transfer Object (DTO)](https://www.infoq.com/br/articles/dto-hipster-ou-depreciado/). Neste objeto inserimos apenas os atributos referente ao cadastro da Conta, não permitindo que o usuário acesse os demais campos da entidade. Abaixo o exemplo para cadastro de conta:

```java
public class CadastroContaDTO{
    private String titular;

    public Conta toModel(){
        return new Conta(titular);
    }
}
```

Para deixar o nosso **_Controller_** seguro agora basta substituir o tipo do objeto conta de _Conta_ para _CadastroContaDTO_ no método cadastrar e adaptar a lógica de cadastro.

```Java
@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();
    }
}
```
O **_Controller_** adaptado fica igual ao código apresentado acima, e além de não ficar vulnerável ao ataque de atribuição de massa, ter um _DTO_ de entrada permite que os modelos evoluam de maneira independente, ou seja, minha entidade pode ganhar novos campos, sem que meu _DTO_ seja alterado e vice-versa.

Ter um _DTO_ de entrada ou saída pode trazer consigo também uma otimização de dados trafegados na rede, dado que o _DTO_ contém apenas as informações necessárias para satisfazer uma funcionalidade, campos que antes eram serializados, trafegados e não utilizados, agora não fazem mais parte da mensagem. 


## Conclusão

Receber uma entidade no **_Controller_** pode trazer grandes danos a segurança da sua aplicação e seu negócio, porém, ao inserir um  _DTO_ para receber estas informações, proporciona diversas vantagens como maior segurança contra vulnerabilidades, desacoplamento entre os  modelos da API e negócio, e ganhos em desempenho em relação aos dados trafegados na rede.