Como lidar com entradas de formulário de maneira eficiente com Express-Validator em ExpressJs
O autor selecionou a Fundação OWASP para receber uma doação como parte do programa Write for DOnations.
Introdução
Os desenvolvedores web modernos sempre lidam com entradas, sejam elas formulários web, uploads de arquivos ou qualquer outra entrada em seus sites. Portanto, torna-se essencial para eles garantir que as informações que recebem dos usuários sejam legítimas e inofensivas.
Com os avanços no desenvolvimento web, os invasores se desenvolveram mais e encontraram maneiras de explorar sites de diferentes aspectos, como entradas de formulários. Ataques como Cross-Site Scripting (XSS) e SQL Injection tornaram-se mais comuns e prejudiciais do que nunca. Portanto, qualquer desenvolvedor precisa garantir que os dados que recebe de um usuário não sejam maliciosos ou sejam higienizados para garantir a integridade de seu aplicativo web e servidor.
Neste tutorial, você aprenderá como usar o pacote Express-Validator de JavaScript para validar e limpar as entradas do usuário em formulários da web para garantir a consistência dos dados e a segurança do servidor.
Pré-requisitos
Para entender este tutorial corretamente, você deve ter a seguinte configuração:
-
Instale Node Js com versão 14.x ou superior em seu sistema. Você pode usar este tutorial DO para instalar a versão mais recente do Node Js em seu sistema.
Você deve saber como codificar em JavaScript.
Você deve ter o Express JS versão 4.x ou superior instalado em seu sistema. Você pode usar isso para configurar um servidor Express.
Depois de atender a esses requisitos, você poderá continuar com o tutorial.
Importância da validação de entrada no Desenvolvimento Web
Validação e higienização de entrada são tarefas importantes para um desenvolvedor pelos vários motivos mencionados abaixo:
Prevenção contra ataques de injeção: considere um cenário em que seu aplicativo esteja vulnerável a ataques de injeção de SQL. Esta vulnerabilidade surge devido ao mau tratamento do código SQL durante a autenticação do usuário através de um formulário de autenticação. Um invasor pode explorar isso passando algum código SQL malicioso em vez de credenciais de usuário e obtendo acesso ao servidor, que encerra o jogo para o aplicativo.
Integridade e consistência dos dados: quando a entrada do usuário é validada, ela cria consistência nos dados armazenados nos servidores, tornando complicado trabalhar com os dados. Por exemplo, se um usuário puder enviar dados de texto na entrada por idade, isso criará inconsistência nos dados armazenados no servidor.
Compatibilidade de dados: o tipo de dados deve ser consistente quando os dados são usados em vários endpoints em uma grande organização. Por exemplo, se os usuários puderem inserir dados inúteis em vez de um e-mail adequado em suas credenciais de e-mail, isso poderá causar complicações quando a organização precisar entrar em contato com o usuário.
Experiência do usuário aprimorada: quando as entradas são validadas, um desenvolvedor pode criar uma lógica para enviar feedback apropriado e imediato aos usuários, permitindo-lhes corrigir a entrada inválida fornecida. Isso melhora a experiência geral do usuário.
Existem muitos motivos para garantir que, como desenvolvedor, você lide com as entradas em seus formulários e sites da web de maneira eficiente e segura. Nas seções a seguir, você aprenderá como criar lógica de validação para suas entradas de formulário usando o pacote Express-Validator
em um aplicativo Express Js.
Terminologia usada na validação e higienização de insumos
Validador – Um validador é uma função que recebe informações e executa certas verificações com base em alguns critérios.
Sanitizer – Um sanitizer é uma função usada para modificar ou limpar dados de entrada para garantir que sejam seguros e sigam os formatos exigidos.
Cadeia de Validação – Uma cadeia de validação no Express-Validator é uma sequência de validadores ou higienizadores aplicados em uma entrada. Por exemplo, suponha que você tenha um formulário no qual deseja que os usuários insiram seus e-mails e, para manter a consistência dos dados no banco de dados, não queira que nenhum espaço em branco seja permitido na entrada (nas extremidades esquerda ou direita). Você pode usar uma cadeia de validação como .isEmail()
para obter this.trim()
[Você aprenderá sobre isso em uma seção posterior]. Isso primeiro verificará se a entrada é um email; se forem dados de e-mail, o desinfetante trim()
removerá os espaços em branco de ambas as extremidades da entrada.
Instalando o Express-Validator e integrando-o com um Express Server
Agora, você aprenderá a implementar técnicas de validação e higienização de entrada nas seções a seguir deste tutorial. Primeiramente, você configurará um projeto npm e, em seguida, instalará Express Js, Express-Validator e outras dependências necessárias para seguir o tutorial.
Para criar um servidor Express, primeiro você precisa criar um projeto npm. Primeiramente, abra um terminal e digite os seguintes comandos:
cd <project directory>npm init
Em seguida, serão solicitadas informações solicitando informações sobre o aplicativo; você pode inserir os detalhes específicos ou continuar pressionando ‘Enter’. Agora, crie um arquivo index.js na pasta do projeto; este será o ponto de entrada do servidor. Por último, você deve instalar o pacote Express Js para criar um servidor Express. Isso pode ser feito com o seguinte comando.
npm install express
Agora você tem o Express Js instalado em seu projeto. A próxima tarefa é instalar o
Express-Validator,
o que pode ser feito com o seguinte comando.npm install Express-Validator
Satisfazemos as dependências básicas até agora; entretanto, você pode instalar
nodemon
em seu projeto, o que é útil para manter um aplicativo Node Js em execução mesmo se ocorrer algum erro. Digite o seguinte comando para instalá-lo.npm install nodemon
Agora instalamos todos os requisitos e podemos começar a trabalhar no servidor e validar os inputs nos formulários. Este projeto será a estrutura base utilizada em todas as seções posteriores, onde cada seção explica um tópico diferente.
A próxima seção explica como o Express-Validator
funciona nos bastidores e como você pode usá-lo em formulários para realizar validações de entrada em vários campos.
Técnicas básicas de validação de formulário no Express-Validator
Express-Validator
é uma combinação de middleware fornecido pelo módulo Express JS e pelo módulo Validator.js, que fornece validadores e sanitizadores para tipos de dados de string. Express-Validator
fornece a funcionalidade para validar dados de entrada usando uma cadeia de validação.
Nesta seção, você aprenderá a usar os validadores em Express-Validator
usando o projeto Node Js configurado na seção anterior. Para isso, você criará uma rota no Express Server que permite aos usuários se cadastrarem em uma aplicação web com suas credenciais (nome, email, senha). Em seguida, você criará um middleware para validar as entradas sempre que um usuário se inscrever em seu servidor.
Abra o arquivo index.js e digite o seguinte código nele.
const express = require("express");
const app = express();
const { body, validationResult } = require("express-validator");
// Express.js middleware to use JSON objects
app.use(express.json());
app.post(
"/signup",
// using validation to verify valid inputs (MIDDLEWARE)
[
[
body("name").notEmpty(),
body("email").isEmail(),
body("password").notEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
res.status(200).json({success:'Successful Sign Up!'})
}
);
// Server Listening at port 3000
const port = 3000;
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}`);
});
Aqui, importamos o Express Js e depois criamos um aplicativo Express. Em seguida, usamos a destruição de JavaScript para importar as funções body e validaçãoResult do Express-Validator
.
Informações O método body()
é usado para criar cadeias de validação para validar e selecionar dados de entrada de cargas úteis de solicitação (req) de usuários, como dados enviados por uma solicitação POST. O método validationResult()
armazena o resultado de uma cadeia de validação de uma solicitação HTTP como um objeto JavaScript. Se este objeto estiver vazio, significa que a carga passou em todos os testes de validação; caso contrário, armazena informações sobre a carga útil e os critérios que não atendeu.
Em seguida, usamos o middleware JSON do Express J para trabalhar com objetos JSON.
Por fim, criamos uma rota Express usando a solicitação HTTP - POST (Você pode trabalhar com qualquer outro tipo de solicitação, como GET, PUT, etc.). A rota que criamos aqui representa uma rota para um formulário de inscrição em um servidor web. O formulário recebe entrada usando HTML do lado do cliente, e o servidor lida com isso acessando os campos de entrada do formulário da web usando o corpo do HTML DOM.
O método body()
do Express-Validator
busca os valores dos componentes HTML tendo o atributo name igual ao argumento do método body()
; ou seja, body(“password”)
irá buscar o valor do componente de entrada HTML tendo o atributo name como senha.
Então, nesses valores, você pode usar os validadores fornecidos pelo Express-Validator
. Neste exemplo, usamos dois validadores, isEmail()
e notEmpty()
. Esses validadores determinam se a entrada é email e não vazia, respectivamente. Se a entrada não corresponder aos critérios do(s) validador(es), o Express-Validator lançará um erro como um objeto na forma de validationResult()
. Porém, se a entrada corresponder aos critérios dos validadores aplicados, o objeto validationResult()
estará vazio. Isso é então usado dentro da definição da função da rota ‘/signup’ para criar uma verificação simples usando a instrução if
. Suponha que o array validationResult()
não esteja vazio. Nesse caso, um erro com código de resposta 400 é enviado ao cliente. Caso contrário, neste exemplo, uma mensagem de inscrição bem-sucedida é enviada ao cliente (isso é apenas para fins exemplificativos; você pode executar qualquer tarefa, como armazenar os dados de entrada em um banco de dados, usar as credenciais de entrada para autenticar um usuário , etc.). Em seguida, hospedamos o servidor em localhost na porta 3000.
Esses validadores básicos foram usados neste exemplo; entretanto, você pode usar qualquer outro validador oferecido pelo pacote Express-Validator
. Na tabela a seguir, você pode encontrar alguns validadores comumente usados, que podem ser usados da mesma maneira que isEmail()
e notEmpty()
.
- isDate
Verifica se o Input é um objeto Date.
- isEmpty
Verifica se a entrada é uma string vazia.
- isHash(algorithm)
Verifica se a entrada é um hash. O argumento usa o algoritmo de hashing como MD5, SHA, etc.
- isJWT
Verifica se a entrada é um JWT (JavaScript Web Token).
- isURL
Verifica se a entrada é uma URL.
- isBoolean
Verifica se a entrada é booleana (verdadeiro ou falso).
- isNumeric
Verifica se a entrada é do tipo de dados numérico.
- isAlphanumeric
Verifica se a entrada é alfanumérica.
- isInt
Verifica se a entrada é do tipo Inteiro.
- isDecimal
Verifica se a Entrada está em Base Decimal.
- isFloat
Verifica se a entrada é um número de ponto flutuante.
Agora, na seção seguinte, você aprenderá como validar entradas de arquivos para fazer uploads de arquivos através de seu formulário (como uma fotografia ou assinatura) seguros e controlados.
Tratamento da validação de entrada de arquivo em formulários web (opcional)
Na seção anterior, você aprendeu como usar o Express-Validator
para validar entradas geralmente passadas para formulários web. Nesta seção, você aprenderá como lidar com entradas de arquivos e validá-los antes de enviá-los para um servidor para evitar o upload de arquivos maliciosos em seu servidor. Além desse benefício de segurança, você pode restringir o tamanho de entrada do arquivo, garantindo que nenhum arquivo grande ocupe seu servidor.
Usaremos o pacote multer
do Node Js para lidar com uploads de arquivos usando formulários. Você pode usar este extenso tutorial DO para entender como funciona o multer
, Fazendo upload de arquivos com multer em Node.js e Express. Agora, o Express-Validator
não fornece funcionalidade específica para manipulação de entrada de arquivos; entretanto, você pode combinar a biblioteca multer
como uma extensão do Express-Validator
para lidar com a validação de entrada de arquivo para seus formulários web. Antes de usar multer
, você deve instalar o pacote em seu projeto Node Js, o que pode ser feito seguindo o comando.
cd <project folder>
npm install multer
Agora, você verá como integrar o multer
com seu servidor Express usando o Express-Validator
. No código a seguir, você implementará multer
e Express-Validator
e colocará algumas restrições nos arquivos enviados.
const express = require("express");
const app = express();
const { body, validationResult } = require("express-validator");
const multer = require("multer");
// Express.js middleware to use JSON objects
app.use(express.json());
// CREATING MIDDLEWARE USING MULTER TO HANDLE UPLOADS
// creating a storage object for multer
const storage = multer.diskStorage({
// providing the destinations for files to be stored in server
destination: "./uploads/",
});
// defining file storage to local disk and putting constraints on files being uploaded
const upload = multer({
storage: storage,
limits: { fileSize: 1*1024*1024 }, // file size in bytes
// you can add other constraints according to your requirement such as file type, etc.
});
app.post(
"/fileUpload",
// input validation for files to ensure only a single file is uploaded in one request
upload.single("fileUpload"),
// using validation to verify valid inputs
[
body("fileDescription").notEmpty(),
// you can add as many validators as you require
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const file = req.file;
// perform any other tasks on the file or other form inputs
res.status(200).json({ success: "Successful Sign Up!" });
}
);
// Server Listening at port 3000
const port = 3000;
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}`);
});
Aqui, definimos as importações necessárias e o middleware Express JSON para trabalhar com objetos JSON em uma rede. Em seguida, definimos o servidor de armazenamento para que multer
funcione. O exemplo é explicado em detalhes abaixo:
Primeiro, definimos uma categoria de armazenamento.
Multer
fornece armazenamento em disco e armazenamento em memória (buffer). Neste exemplo, usamos armazenamento em disco para armazenar arquivos carregados no disco local de um servidor em vez de desperdiçar memória.Ao usar o armazenamento em disco, é necessário fornecer o local do diretório onde os arquivos carregados serão armazenados. Isso pode ser feito usando a palavra-chave de destino, que considera o caminho como valor. Aqui, usamos uma pasta de uploads no próprio diretório do projeto.
Depois disso, criamos um objeto de upload que trata do upload de arquivos. Isso chama a importação
multer
e recebe um objeto JavaScript como entrada. Este objeto JavaScript contém várias restrições/validações a serem satisfeitas antes de armazenar o arquivo de entrada no disco. Armazenamento é um argumento necessário que tomará como entrada o objeto de armazenamento, definido antes deste.Adicionamos outra validação usando a opção de limites para garantir que nenhum arquivo seja maior que 1 MB. Observe que ele conta o tamanho em bytes.
Informações: Você pode fazer outras validações/verificações no objeto limites de acordo com sua necessidade.
Em seguida, criamos uma rota Express para lidar com o upload de arquivos e adicionamos um middleware
upload.single()
, garantindo que apenas um arquivo seja carregado em uma solicitação. Ele utiliza o campo de entrada denominado fileUpload do HTML DOM como argumento obrigatório.Depois disso, adicionamos o middleware Express-Validator, conforme explicado na seção anterior.
Por fim, criamos a lógica para tratar a rota e verificar se há erros na validação de entrada.
Você pode acessar os arquivos carregados a partir de sua lógica de rota a partir do objeto req.file.
Aqui, você aprendeu como lidar com a validação de entrada de arquivo usando Multer
e integrá-lo ao middleware de validação Express-Validator
. Além disso, você pode adicionar muitos outros métodos de validação avançados ou personalizados em Multer
e Express-Validator. Na próxima seção, você aprenderá como higienizar valores de entrada, o que é necessário para evitar ataques como SQL Injection.
Sanitização de entradas de formulário
Até agora, você aprendeu como realizar validações de entrada em dados e arquivos de entrada. Agora, você ampliará esse conhecimento aprendendo como higienizar insumos (validados). Express-Validator
fornece muitos desinfetantes da biblioteca Validator.js.
Para explicar os desinfetantes, usaremos uma abordagem semelhante às ‘Técnicas básicas de validação de formulário em Express-Validator
e adicionaremos desinfetantes na rota.
const express = require("express");
const app = express();
const { body, validationResult } = require("express-validator");
// Express.js middleware to use JSON objects
app.use(express.json());
app.post(
"/sanitizedInput",
// sanitizing inputs
[
[
body("name").notEmpty().trim(),
body("email").isEmail().trim(),
body("dob").toDate(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// remaining logic for your route
res.status(200).json({success:'Successful Sign Up!'})
}
);
// Server Listening at port 3000
const port = 3000;
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}`);
});
Você cria outro servidor Express importando os pacotes necessários e usando o middleware Express JSON para manipular objetos JSON. Em seguida, você criará uma rota para lidar com a entrada higienizada (/sanitizedInput
) e escreverá a lógica da rota.
Novamente, você precisa adicionar o middleware Express-Validator
. Aqui, usamos três campos de entrada: nome
, e-mail
e dob
. Primeiro, verificamos se o nome não está vazio usando notEmpty()
e depois aplicamos o desinfetante trim()
nele, que remove espaços em branco de ambas as extremidades do valor de entrada. Da mesma forma, para o campo de entrada de e-mail, primeiro verificamos se é um e-mail válido usando isEmail()
e, em seguida, aplicamos o desinfetante trim()
para remover espaços em branco de ambas as extremidades.
No campo de entrada dob
, usamos o sanitizador toDate()
(sem qualquer validador) para converter a string de entrada em um objeto de data. Se a string não for uma data real, toDate()
retornará um valor nulo.
Depois disso, você realizará a verificação para garantir que não há erros armazenados no objeto validationResult()
e poderá continuar com o restante da lógica da sua rota.
Nota: Usar vários validadores e sanitizadores em uma única entrada, como feito com body("name").notEmpty().trim()
, cria uma cadeia de validadores/sanitizadores. Tal cadeia ou uso sequencial de validadores/sanitizadores cria uma cadeia de validação.
Existem muitos outros desinfetantes disponíveis com o Express-Validator
, como blacklist()
, whitelist()
, etc. seus requerimentos.
Conclusão
Neste tutorial, você aprendeu como lidar com eficiência com entradas de formulário em seu servidor. Você construiu servidores usando a biblioteca Express-Validator
que usava validação de entrada e técnicas de sanitização. Você pode aproveitar esse conhecimento e estender ainda mais sua validação de entrada usando diferentes validadores e sanitizadores fornecidos pela mesma biblioteca.
Express-Validator
e Multer
permitem criar validadores personalizados, que você pode experimentar como um exercício para aumentar sua produtividade em projetos complexos.