Criando Controllers seguros do Mass Assignment Cheat Sheet

9607 단어 javawebdevspring
Se você esta iniciando sua carreira como Desenvolvedor(a) Java 및 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 formalário de entrada, que permitiu ele se associar a organização do Ruby on Rails através de uma chave ssh publica . 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 paraâmetros de uma requisição HTTP, chamamos de Vulnerabilidade de Atribuição de Massa .

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 예시 API que permita esta operação é:

@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. 예를 들어 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. 예를 들면:

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 Representative as informações de cadastro. Este objeto é uma implementação do padrão de projeto Data Transfer Object (DTO) . Neste objeto inserimos apenas os atributos referente ao cadastro da Conta, não permitindo que o usuário acesse os demais campos da entidade. 예를 들면 다음과 같은 지적 사항이 있습니다.

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 logica de cadastro.

@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는 그 반대도 마찬가지입니다.

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 faazem mais parte parte mensagem.

결론



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.

좋은 웹페이지 즐겨찾기