Olá, boas-vindas ao curso de Desenvolvimento Seguro. Sou o Geovane Fedrecheski e serei seu instrutor.
Audiodescrição: Geovane é um homem de pele clara, barba e cabelos curtos também claros. Usa óculos de armação redonda na cor preta e camiseta cinza-claro. Ao fundo, uma parede lisa com iluminação azul.
Este curso é para você, pessoa desenvolvedora de software que deseja aprimorar a segurança de suas aplicações.
Para aprendermos tudo isso, usaremos a VollMed, uma aplicação que realiza o gerenciamento de uma clínica na qual é possível cadastrar pacientes e realizar o login.
Nesse curso, focaremos no back-end, ou seja, trabalharemos com uma API Rest melhorando os logs e erros.
Para realizar esse curso, é muito importante que você saiba sobre linha de comando e tenha conhecimento básico em NodeJS, HTTP e APIs.
Além dos vídeos, você também terá atividades, acesso ao Fórum para tirar dúvidas e ao Discord para interagir com a comunidade.
Te esperamos no próximo vídeo!
Vamos começar a praticar. Para isso, abrimos o projeto no GitHub.
Usaremos a API ValMed, um sistema de gerenciamento de clínicas que possui funcionalidades como cadastro de pacientes e agendamento de exames.
Trabalharemos principalmente com o back-end. Se você já concluiu o curso anterior já deve estar familiarizado.
Com o código já baixado e a pasta aberta no terminal, vamos executá-lo passando o comando docker-compose up
, seguido de "Enter".
docker-compose up
Enquanto carrega, analisaremos o Trello, outra ferramenta que utilizaremos nesse curso para organizar o trabalho e tarefas.
Durante o curso, teremos demandas de segurança para serem aplicadas na API. Então, no Trello, temos uma coluna destinada às demandas, outra referente as tarefas a fazer, tarefas em andamento e finalizadas.
Nesse vídeo, desenvolveremos a tarefa "Informações sensíveis em mensagens de erro", que sugere que as mensagens de erro da API da VollMed estão revelando informações sensíveis sobre o sistema.
Antes de fazermos isso, verificaremos que o servidor já inicializou. Para isso, voltamos no terminal. Se a mensagem for "App Data Source inicializado", significa que tudo correu bem.
Faremos um teste utilizando a ferramenta Insomnia, afinal, por meio dela podemos fazer requisições. No menu lateral esquerdo, encontramos requisições pré-configuradas, a opção selecionada é o POST
do cadastro do paciente.
Você encontra instruções sobre a configuração do Insomnia no Preparando Ambiente.
No centro da tela podemos visualizar o código com os dados do paciente. Para testarmos, clicamos no botão "Send", localizado no topo superior direito da tela.
Feito isso, aparece a mensagem "202 Accepted" e abaixo o resultado, isso significa que deu certo. Porém, relembrando, segundo as especificações do Trello, se inserirmos o campo de imagem, haverá um erro e mensagens sensíveis serão exibidas.
Para corrigir, precisamos inserir o campo "imagem": "uma_bela_imagem.png"
em um objeto paciente. Então, copiamos esse código, voltamos no Insomnia e colamos na linha 17. Depois, clicamos em "Send".
//Trecho omitido
"senha": "abCD12!@",
"telefone": "11988887777",
"possuiPlanoSaude": true,
"planosSaude": [3,2,5),
"imagem": "uma_bela_imagem.png",
"historico": ["bronquiete, leve", "sinusite, moderado"]
}
Repare que ao fazer isso, aparece a mensagem "409 Conflict", pois já tinha um paciente com esse CPF. Então, no navegador, acessamos o Gerador de CPF, copiamos e colamos no Insomnia. Feito isso, novamente clicamos em "Send".
Agora tivemos um erro "502 Bad Gateway". A ferramenta indica que as informações, como id, CPF e e-mail, fazem parte do banco de dados da VollMed, ou seja, são dados sensíveis que não tornamos público. Isso porque pessoas podem utilizar esses dados contra o próprio sistema. Então, a mensagem de erro faz todo sentido.
Analisando o restante do retorno, também notamos códigos de erro relacionados ao driver do banco de dados. A partir disso, a pessoa atacante pode descobrir a versão do banco de dados e encontrar uma falha de segurança. Sendo assim, nunca é apropriado deixar visível para o usuário final informações internas do sistema.
Para resolvermos isso, copiamos a mensagem de erro "Paciente não foi criado". Depois, abrimos o VS Code, na barra de menu lateral, clicamos no ícone identificado por uma lupa, colamos e apertamos "Enter".
Feito isso, esse trecho de código é encontrado no arquivo pacienteController.ts
na linha 112. Analisando o código, descobrimos que essa linha é chamada na função criarPaciente
. Isso significa que faz sentido, pois a rota que usamos no Insomnia era para criar paciente.
No início dessa função, logo que iniciada, há um try
. Dessa forma, toda exceção estará sob gerenciamento, caso ocorra uma. Mais abaixo, notamos que dentro do try
há um cath
que exibe o erro, caso tenha um.
É nesse trecho que a mensagem "Paciente não foi criado" foi definida. Portanto, o erro é passado na sua totalidade, é completamente retornado para o front-end, é isso que queremos evitar.
Sendo assim, na linha 113, apagamos o trecho error
. Antes de 'Paciente não foi criado'
escrevemos message
seguido de dois pontos.
//Trecho omitido
res.status(202).json(pacienteSemDadosSensiveis)
} catch (error) {
if (error.name === 'ValidationError') {
res.status(400).json({ message: error.message })
} else {
res.status(502).json({ message: 'Paciente não foi criado' })
console.log(error)
}
}
}
Assim, deixamos o código mais sucinto e removemos as informações sensíveis. Ao salvar nosso código já é recompilado. Para conferirmos se deu certo, abrimos o terminal. Se aparecer uma mensagem informando que foi inicializado e não foi encontrado nenhum erro é que deu certo.
Voltamos no Insomnia, usaremos a mesma requisição, então clicamos em "Send". Feito isso, aparece a mensagem de erro "502 Bad Gateway", que é o que esperávamos, porém, agora o erro está mais resumido, sem informações sensíveis ou detalhes do sistema.
Voltamos no Trello para conferir se fizemos todas as correções necessárias. Ao abrir o cartão, logo abaixo, encontramos um checklist da OWASP
, uma fundação que define diretrizes para criar códigos seguros.
Nesse caso, temos as seguintes diretrizes referentes aos erros:
- Não expor informações sensíveis nas repostas de erros, inclusive detalhes de sistema, identificadores de sessão ou informação da conta do usuário;
- Usar mecanismos de tratamento de erros que não mostrem informações de depuração (debug) ou informações da pilha de exceção;
- Usar mensagens de erro genéricas.
Como realizamos todas essas correções, marcamos como completas. Feito isso, movemos esse card para a coluna "Feito", pois concluímos a primeira tarefa para tornar a aplicação da VollMed mais segura.
No próximo vídeo, aprenderemos como unificar o tratamento de erro.
Até lá!
No vídeo anterior, no arquivo pacienteController.ts
, fizemos uma modificação na função de criarPaciente()
, removendo o trecho referente ao envio de dados sensíveis.
Se analisarmos como estamos tratando os erros nesse código, notamos algumas diferenças. Por exemplo, na linha 59, quando validamos o CPF, lançamos uma exceção. Isso faz sentido, pois estamos dentro de um try, então, quando ocorre um erro, é lançado uma exceção.
Porém, em alguns outros casos, temos caminhos que são erros, como na linha 66, em if (existePacienteComCPF)
, porém, não lançamos uma exceção, apenas enviamos a resposta da solicitação HTTP.
Isso torna o código inconsistente. Inclusive, no Trello, encontramos um card chamado "Unificar tratamento de erros". Ao abri-lo, descobrimos que a tarefa é usar a classe AppError
, pois todos os erros passam por ela. Portanto, se precisarmos fazer uma modificação, o código estará mais homogêneo e será mais fácil de mudar.
Voltamos ao VS Code para fazermos a aplicação.Na linha 67, ao invés de enviarmos uma resposta com um status que vamos escrever manualmente, inserimos uma nova linha abaixo de if (existentePAcienteComCPF)
e escrevemos throw new AppError()
. Dentro dos parênteses, entre aspas simples, passamos Já existe um paciente com este CPF
, seguido de Status.CONFLICT
, que tem o código 409.
Feito isso, apagamos o trecho de código anterior. Ficando dessa forma:
//Trecho omitido
const existePacienteComCPF = await AppDataSource.getRepository(Paciente).findOne({
where: { cpf }
})
if (existePacienteComCPF != null) {
throw new AppError('Já existe um paciente com esse CPF!', Status.CONFLICT)
}
Lembrando que a classe AppError
já trata a questão do statusCode
. Então, podemos passar o status para a classe AppError
, que atua como ErrorHandler.ts
a fim de unificar o tratamento de erros. Isso significa que não precisamos enviar manualmente a resposta do HTTP, porque o tratamento de erros cuidará disso.
Em seguida, continuamos analisando o código e buscando tratamentos de erro que não estejam seguindo a diretriz. Repare que na linha 108, no momento em que é feito o catch
do try
, temos dois casos.
O primeiro é o teste de validação, então, se vem um erro de validação, será enviado a resposta. Porém, nesse caso não faz sentido, pois já tratamos os erros de validação. Então apagamos da linha 109, if (error.name === 'ValidationError')
até a linha 111. Deixando somente o trecho de código para quando o paciente não for criado. Dessa forma:
//Trecho omitido
res.status(202).json(pacienteSemDadosSensiveis)
} catch (error) {
res.status(502).json({ 'PAciente não foi criado' })
console.log(error)
}
}
Porém, como estamos enviando a resposta manualmente, faremos o mesmo. Então, abaixo de catch(error)
escrevemos throw new AppError()
passando, dentro de chaves e aspas duplas, a mensagem Paciente não foi criado, Status BAD_GATEWAY
e removemos a linha anterior.
Em seguida, movemos a linha de código console.log(error) 1
para baixo de catch(error)
Dessa forma:
//Trecho omitido
res.status(202).json (pacienteSemDados Sensiveis)
} catch (error)
console.log(error) 1
throw new AppError('Paciente não foi criado', Status.
BAD_GATEWAY)
}
Como retiramos a parte de validação do final do catch, não precisamos colocar o try no início do código. Então, na linha 38 o apagamos. Depois, o adicionamos na linha 90, antes de começarmos a criar o endereço.
//Trecho omitido
try {
if (endereco !== undefined) {
enderecoPaciente.cep = endereco.cep
enderecoPaciente.rua = endereco.rua
enderecoPaciente.estado = endereco.estado
enderecoPaciente.numero = endereco.numero
enderecoPaciente.complemento = endereco.complemento
Agora, estamos tratando os erros de forma mais homogênea utilizando o Error Handler global.
Voltamos no Trello. Agora, vamos conferir se cumprimos toda a checklist da OWASP, assim aprendemos um pouco sobre as diretrizes e s o que estamos mudando.
- Checklist da OWASP
- Usar mensagens de erro genéricas;
- A aplicação deve tratar os erros sem se basear nas configurações do servidor;
Desenvolvemos um mecanismo de tratamento de erros que funciona independente de qual porta o servidor está rodando. Além disso, garantimos que todos os erros passassem por um mesmo canal para melhorar a organização do nosso código.
Sendo assim, podemos marcar as duas checklists e arrastar essa tarefa para a coluna "Feito".
No próximo vídeo, aprenderemos como lidar com os logs da aplicação.
Até lá!
O curso Desenvolvimento Seguro: lidando com erros e logs em uma aplicação possui 86 minutos de vídeos, em um total de 37 atividades. Gostou? Conheça nossos outros cursos de Segurança em DevOps, ou leia nossos artigos de DevOps.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.