diff --git a/Deliverables/Phase2/Implementation/Use Case 10/img/Interceptor.png b/Deliverables/Phase2/Implementation/Use Case 10/img/Interceptor.png deleted file mode 100644 index d06c6c01..00000000 Binary files a/Deliverables/Phase2/Implementation/Use Case 10/img/Interceptor.png and /dev/null differ diff --git a/Deliverables/Phase2/Implementation/Use Case 10/img/interceptor.png b/Deliverables/Phase2/Implementation/Use Case 10/img/interceptor.png new file mode 100644 index 00000000..6f781dec Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 10/img/interceptor.png differ diff --git "a/Deliverables/Phase2/Implementation/Use Case 3/Use Case 3 - Implementa\303\247\303\243o.md" "b/Deliverables/Phase2/Implementation/Use Case 3/Use Case 3 - Implementa\303\247\303\243o.md" index 55dcd784..f53b8b21 100644 --- "a/Deliverables/Phase2/Implementation/Use Case 3/Use Case 3 - Implementa\303\247\303\243o.md" +++ "b/Deliverables/Phase2/Implementation/Use Case 3/Use Case 3 - Implementa\303\247\303\243o.md" @@ -18,14 +18,88 @@ So that I can easily search for the product I want to buy - **Injeção de Código Malicioso:** -1. ASVS 5.3.3: Verificar se a saída é corretamente codificada para prevenir ataques de XSS refletidos, armazenados e baseados em DOM. +ASVS 5.3.3: Verificar se a saída é corretamente codificada para prevenir ataques de XSS refletidos, armazenados e baseados em DOM. - **Ataques de Sobrecarga:** -1. ASVS 8.1.4: Verificar se a aplicação pode detetar e alertar sobre números anormais de solicitações, como por IP, utilizador, total por hora ou dia, ou o que faz sentido para o aplicativo. +ASVS 8.1.4: Verificar se a aplicação pode detetar e alertar sobre números anormais de solicitações, como por IP, utilizador, total por hora ou dia, ou o que faz sentido para o aplicativo. + +**Nota:** Para cada caso de abuso foi analisado qual o ASVS que melhor se relaciona com o caso de abuso, tendo em conta que com a implementação deste, outros ASVS são também abordados. +Este estudo foi feito no Excel a entregar. ## Implementação da UC Esta UC já se encontra desenvolvida. Apenas a aplicação dos ASVS foram necessários. -## Implementação dos ASVS \ No newline at end of file +## Implementação dos ASVS + +### ASVS 5.3.3 + +O ASVS 5.3.3 exige que a saída seja corretamente codificada para prevenir ataques de XSS refletidos, armazenados e baseados em DOM. +Assim, existe dois pontos importantes a considerar para garantir o ASVS 5.3.3: + +#### 1. Validação de Entrada: + +Quando o utilizador filtra a lista de produtos utilizando a barra de pesquisa, a informação escrita na barra de pesquisa é enviada para +o endpoint "api/public/product/search" do "resource_server". + +![searchbar.png](img%2Fsearchbar.png) + +![endpointcall.png](img%2Fendpointcall.png) + +O "resource_server" é responsável por encontrar os produtos que contenham o texto pesquisado e enviar de volta para o frontend (client). + +Para garantir a validação de entrada, a informação enviada do frontend para o backend deve ser sanitizada no momento de recebimento para garantir que não contenha caracteres maliciosos: + +![validacao.png](img%2Fvalidacao.png) + +Ao adicionar esta validação realizada pelo Encoder, da biblioteca OWASP Java Encoder, garantimos a sanitização da entrada fornecida pelo utilizador. +
+
+#### 2. Codificação de Saída: + +Como é possível observar ao utilizar a aplicação, toda a pesquisa feita na barra de pesquisa é envaida no url para o backend, +o que pode constituir um perigo de ataques XSS. + +No entanto, o front-end tem implementado no processo de envio de pedidos https uma verificação para este tipo de ataques, bloqueando a pesquisa, +e reencaminhando o pedido para uma página de erro default definida na aplicação: + +![pagDeErro.png](img%2FpagDeErro.png) + +Para além disso, todas as respostas do backend que devolvam um objeto de Produto a ser mostrado na UI (ProductResponse) são corretamente codificadas antes de serem renderizada no front-end. + +O código Angular já está estruturado para prevenir XSS, pois limpa automaticamente os caracteres perigosos ao usar interpolação ( {{}} ). + +Ex: +![exDeInterpolacao.png](img%2FexDeInterpolacao.png) +
+
+
+ +### ASVS 8.1.4 + +Para implementar o ASVS 8.1.4 na aplicação, foi criado o componente RateLimiterInterceptor, responsável por controlar a quantidade de solicitações recebidas por IP. + +![interceptor.png](img%2Finterceptor.png) + +Este componente verifica cada pedido recebido do frontend e analisa quantas vezes este foi feito pelo +IP que o está a executar. Caso esse pedido exceda um número predefinido de vezes que foi executado por tempo, +o IP é bloqueado de executar esse pedido por um tempo também predefinido, prevenindo assim ataques de sobrecarga (DoS). + +Para efeitos demonstrativos, o intercetor foi configurado para bloquear um IP de fazer pedidos durante 1 minuto após +executar 10x o mesmo pedido. No entanto, este número deve ser ajustado conforme o endpoint e a quantidade de pedidos +espectáveis para a aplicação. + +![interceptorConfigs.png](img%2FinterceptorConfigs.png) + +Junto com o componente RateLimiterInterceptor foi adicionado ao sistema um compoennte de configuração WebMvcConfig, que define +quais os endpoints que o intercetor deve monitorar. + +![config.png](img%2Fconfig.png) + +Assim, caso o utilizador execute mais de dez pesquisas por produtos na barra de pesquisa no perioido de 1 minuto, ele ficará bloquado durante 1 minuto de utilizar a aplicação: + +![tooManyReq.png](img%2FtooManyReq.png) + +Com estas implementações (configurando adequandamente os tresholds de número máximo de pedidos por tempo), +a aplicação é capaz de detectar e alertar sobre números anormais de solicitações, atendendo ao requisito do ASVS 8.1.4 e protegendo a aplicação contra ataques de sobrecarga (DoS). \ No newline at end of file diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/config.png b/Deliverables/Phase2/Implementation/Use Case 3/img/config.png new file mode 100644 index 00000000..fa54c747 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/config.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/endpointcall.png b/Deliverables/Phase2/Implementation/Use Case 3/img/endpointcall.png new file mode 100644 index 00000000..3ead190a Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/endpointcall.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/exDeInterpolacao.png b/Deliverables/Phase2/Implementation/Use Case 3/img/exDeInterpolacao.png new file mode 100644 index 00000000..c5663768 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/exDeInterpolacao.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/interceptor.png b/Deliverables/Phase2/Implementation/Use Case 3/img/interceptor.png new file mode 100644 index 00000000..5ccd14e8 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/interceptor.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/interceptorConfigs.png b/Deliverables/Phase2/Implementation/Use Case 3/img/interceptorConfigs.png new file mode 100644 index 00000000..dcb08dc3 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/interceptorConfigs.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/pagDeErro.png b/Deliverables/Phase2/Implementation/Use Case 3/img/pagDeErro.png new file mode 100644 index 00000000..6b22d7f8 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/pagDeErro.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/searchbar.png b/Deliverables/Phase2/Implementation/Use Case 3/img/searchbar.png new file mode 100644 index 00000000..eb09af15 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/searchbar.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/tooManyReq.png b/Deliverables/Phase2/Implementation/Use Case 3/img/tooManyReq.png new file mode 100644 index 00000000..39baee46 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/tooManyReq.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/validacao.png b/Deliverables/Phase2/Implementation/Use Case 3/img/validacao.png new file mode 100644 index 00000000..f3153121 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/validacao.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 3/img/webConfigEndpoints.png b/Deliverables/Phase2/Implementation/Use Case 3/img/webConfigEndpoints.png new file mode 100644 index 00000000..6097336c Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 3/img/webConfigEndpoints.png differ diff --git "a/Deliverables/Phase2/Implementation/Use Case 5/Use Case 5 - Implementa\303\247\303\243o.md" "b/Deliverables/Phase2/Implementation/Use Case 5/Use Case 5 - Implementa\303\247\303\243o.md" index 06100b40..2c523ac5 100644 --- "a/Deliverables/Phase2/Implementation/Use Case 5/Use Case 5 - Implementa\303\247\303\243o.md" +++ "b/Deliverables/Phase2/Implementation/Use Case 5/Use Case 5 - Implementa\303\247\303\243o.md" @@ -14,7 +14,10 @@ So that I can easily change it if needed ## ASVS Associados -1. ASVS 4.3.1: Verificar se as interfaces administrativas utilizam a autenticação multifactor adequada para evitar a utilização não autorizada. +ASVS 4.3.1: Verificar se as interfaces administrativas utilizam a autenticação multifactor adequada para evitar a utilização não autorizada. + +**Nota:** Para cada caso de abuso foi analisado qual o ASVS que melhor se relaciona com o caso de abuso, tendo em conta que com a implementação deste, outros ASVS são também abordados. +Este estudo foi feito no Excel a entregar. ## Implementação da UC @@ -22,3 +25,17 @@ So that I can easily change it if needed Esta UC já se encontra desenvolvida. Apenas a aplicação dos ASVS foram necessários. ## Implementação dos ASVS + + +### 4.3.1 +Para a implementação desta ASVS o grupo pensou em implementar uma funcionalidade extra no processo de update das informações, que fornecesse uma +autenticação de dois fatores através do envio de um email para o utilizador. + +O objetivo seria a aplicação emviar um código por email, no momento em que o utilziador clicasse no butão "Update", para ele introduzir na aplicação, +e validar a sua identidade. + +![popup.png](img%2Fpopup.png) + +Esta validação, que constitui uma validação multifator, iria implementar com sucesso o ASVS 4.3.1. +No entanto, apesar dos esforços, a equipa não conseguiu terminar esta implementação. + diff --git a/Deliverables/Phase2/Implementation/Use Case 5/img/popup.png b/Deliverables/Phase2/Implementation/Use Case 5/img/popup.png new file mode 100644 index 00000000..f06fcbee Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 5/img/popup.png differ diff --git "a/Deliverables/Phase2/Implementation/Use Case 7/Use Case 7 - Implementa\303\247\303\243o.md" "b/Deliverables/Phase2/Implementation/Use Case 7/Use Case 7 - Implementa\303\247\303\243o.md" index df73bb55..e19de314 100644 --- "a/Deliverables/Phase2/Implementation/Use Case 7/Use Case 7 - Implementa\303\247\303\243o.md" +++ "b/Deliverables/Phase2/Implementation/Use Case 7/Use Case 7 - Implementa\303\247\303\243o.md" @@ -1,4 +1,4 @@ -# Implementação - Caso de Uso "Criar uma conta com os meus credenciais" +# Implementação - Caso de Uso "Criar uma conta com as minhas credenciais" ## Descrição do Caso de Uso @@ -17,25 +17,88 @@ vulnerabilidades de XSS, para comprometer a segurança do sistema ou obter acess de criação de conta (POST) de forma maliciosa, causando uma negação de serviço/aumento do tempo de resposta temporária para os utilizadores legítimos. -- **Interceção de pedidos:** Um atacante pode intercetar pedidos de criação de conta e guardar os credenciais do utilizador, +- **Interceção de pedidos:** Um atacante pode intercetar pedidos de criação de conta e guardar as credenciais do utilizador, podendo aceder a informação sensível. ## ASVS Associados - **Injeção de Código Malicioso:** -1. ASVS 5.3.3: Verificar que a aplicação, de preferência de forma automática ou, em último caso, manual, efetua a escape adequada de saída para proteger contra XSS refletido, armazenado e baseado em DOM. +ASVS 5.3.3: Verificar que a aplicação, de preferência de forma automática ou, em último caso, manual, efetua a escape adequada de saída para proteger contra XSS refletido, armazenado e baseado em DOM. - **Ataques de Sobrecarga:** -1. ASVS 8.1.4: Verify the application can detect and alert on abnormal numbers of requests, such as by IP, user, total per hour or day, or whatever makes sense for the application. +ASVS 8.1.4: Verificar se a aplicação pode detetar e alertar para números anormais de pedidos, como por IP, utilizador, total por hora ou dia, ou o que fizer sentido para a aplicação. - **Interceção de pedidos:** -1. ASVS 5.3.8: Verify that the application protects against OS command injection and that operating system calls use parameterized OS queries or use contextual command line output encoding. +ASVS 9.1.1: Verificar se todas as credenciais, tokens de autenticação e informações sensíveis são transportadas apenas através de um canal criptografado (TLS). + +**Nota:** Para cada caso de abuso foi analisado qual o ASVS que melhor se relaciona com o caso de abuso, tendo em conta que com a implementação deste, outros ASVS são também abordados. +Este estudo foi feito no Excel a entregar. ## Implementação da UC Esta UC já se encontra desenvolvida. Apenas a aplicação dos ASVS foram necessários. -## Implementação dos ASVS \ No newline at end of file +## Implementação dos ASVS + +### ASVS 5.3.3 + +No momento em que o utilizador introduz um e-mail é feito primeiro uma validação desse texto introduzido +utilizando uma gramática que valida se o texto tem o formato de um e-mail: + +![passwordGrammar.png](img%2FpasswordGrammar.png) + +Caso o texto introduzido não tenha o formato de e-mails, não lhe é permitida a sumbissão, e uma mensagem de erro é mostrada. + +![error.png](img%2Ferror.png) + +Quanto à Pasword, está não é validada no front-end, mas sim no back-end, através da utilização +do Encoder, da biblioteca OWASP Java Encoder, que faz a sanitização tanto do email como a password. + +![ecoder.png](img%2Fecoder.png) + +Assim, mesmo que utilizador tente inserir código malicioso na Password, este é validado. + +![error2.png](img%2Ferror2.png) + +Além disso, todos os e-mails e senhas são apenas armazenados na base de dados e nunca são exibidos na UI, o que ajuda a proteger a aplicação. +No entanto, a proteção contra XSS refletido, armazenado e baseado em DOM é garantida principalmente pelas medidas de sanitização e validação implementadas tanto no front-end quanto no back-end, estando assim de acordo com o ASVS 5.3.3. +
+
+### ASVS 8.1.4 + +Tal como já foi descrito no [Use Case 3 - Implementação.md](..%2FUse%20Case%203%2FUse%20Case%203%20-%20Implementa%E7%E3o.md), +para implementar o ASVS 8.1.4 na aplicação, foi criado o componente RateLimiterInterceptor, responsável por controlar a quantidade de solicitações recebidas por IP. + +Para permitir que aplicacão seja capaz de detectar e alertar sobre números anormais de solicitações, basta +adicionar o endpoint que é chamado no momento do registo ("/account/registration"), ao ficheiro de configuração +que define quais os endpoints que deve ser monitorados pelo interceptor: + +![interceptor.png](img%2Finterceptor.png) + +Assim, se um utilizador tentar enviar demasiados pedidos o seu IP irá ficar bloqueado durante o periodo de tempo configurado. + +![tooManyRequests.png](img%2FtooManyRequests.png) +
+
+### ASVS 9.1.1 + +Embora a implementação de um canal criptografado (TLS) para transportar informações sensíveis não previna a interceção +de pedidos, é capaz de proteger as mensagens de maneira que, mesmo que um atacante consiga interceptar essas mensagens, ele não consiga lê-las ou desencriptá-las. + +Assim, se todas as comunicações realizadas entre cliente e servidor forem comunicações via HTTPS com certificado TLS, podemos garantir que este caso de abuso, assim +como qualquer caso de abuso associado a interceção de mensgaens, está mitigado. + +Posto isto, a equipa implementou a comunicação baseada em HTTPS com certificado TLS para todas as +comunicações entre cliente e servidor. + +![https.png](img%2Fhttps.png) + +Deste modo, toda a informação sensível enviada para o endpoint de criação de conta ("api/public/account/registration") está protegida. + +![httpsRegister.png](img%2FhttpsRegister.png) + + + diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/ecoder.png b/Deliverables/Phase2/Implementation/Use Case 7/img/ecoder.png new file mode 100644 index 00000000..73de3391 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/ecoder.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/error.png b/Deliverables/Phase2/Implementation/Use Case 7/img/error.png new file mode 100644 index 00000000..20a7ab95 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/error.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/error2.png b/Deliverables/Phase2/Implementation/Use Case 7/img/error2.png new file mode 100644 index 00000000..7e5ba12b Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/error2.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/https.png b/Deliverables/Phase2/Implementation/Use Case 7/img/https.png new file mode 100644 index 00000000..eef7fe98 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/https.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/httpsRegister.png b/Deliverables/Phase2/Implementation/Use Case 7/img/httpsRegister.png new file mode 100644 index 00000000..be8ed8ca Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/httpsRegister.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/interceptor.png b/Deliverables/Phase2/Implementation/Use Case 7/img/interceptor.png new file mode 100644 index 00000000..dea57fe2 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/interceptor.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/passwordGrammar.png b/Deliverables/Phase2/Implementation/Use Case 7/img/passwordGrammar.png new file mode 100644 index 00000000..741cd1fa Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/passwordGrammar.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 7/img/tooManyRequests.png b/Deliverables/Phase2/Implementation/Use Case 7/img/tooManyRequests.png new file mode 100644 index 00000000..ad1267d2 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 7/img/tooManyRequests.png differ diff --git "a/Deliverables/Phase2/Implementation/Use Case 8/Use Case 8 - Implementa\303\247\303\243o.md" "b/Deliverables/Phase2/Implementation/Use Case 8/Use Case 8 - Implementa\303\247\303\243o.md" index 517fd62b..533d8d2c 100644 --- "a/Deliverables/Phase2/Implementation/Use Case 8/Use Case 8 - Implementa\303\247\303\243o.md" +++ "b/Deliverables/Phase2/Implementation/Use Case 8/Use Case 8 - Implementa\303\247\303\243o.md" @@ -16,11 +16,30 @@ So that I can use the application - **Forçar entrada por tentativa e erro:** -1. ASVS 2.2.1: Verificar se a aplicação bloqueia uma conta de utilizador após um número especificado de tentativas de autenticação malsucedidas dentro de um período de tempo especificado. +ASVS 2.2.1: Verificar se a aplicação bloqueia uma conta de utilizador após um número especificado de tentativas de autenticação malsucedidas dentro de um período de tempo especificado. +**Nota:** Para cada caso de abuso foi analisado qual o ASVS que melhor se relaciona com o caso de abuso, tendo em conta que com a implementação deste, outros ASVS são também abordados. +Este estudo foi feito no Excel a entregar. ## Implementação da UC Esta UC já se encontra desenvolvida. Apenas a aplicação dos ASVS foram necessários. -## Implementação dos ASVS \ No newline at end of file +## Implementação dos ASVS + +### ASVS 2.2.1 + +Para a implemtação desta ASVS na aplicação, podemos reutilizar o componente RateLimiterInterceptor, introduzido no [Use Case 3 - Implementação.md](..%2FUse%20Case%203%2FUse%20Case%203%20-%20Implementa%E7%E3o.md), +responsável por controlar a quantidade de solicitações recebidas por IP para o "resource_serever". + +Embora este componente tenha sido desenvolvido com a finalidade de assegurar o ASVS 8.1.4, ele é +também capaz de assegurar o ASVS 2.2.1 pois consegue bloquear o IP de um utilizador +após um número especificado de tentativas de autenticação malsucedidas num período de tempo especificado. + +Posto isto, o RateLimiterInterceptor foi implementado no componente de autenticação da aplicação +"authorization_server". + +Assim, basta configuarar o intercetor com o número de pedidos/tempo desejado para permitir mitigar +tentativas de login por bruteforce. + +![bruteforce.png](img%2Fbruteforce.png) diff --git a/Deliverables/Phase2/Implementation/Use Case 8/img/bruteforce.png b/Deliverables/Phase2/Implementation/Use Case 8/img/bruteforce.png new file mode 100644 index 00000000..f5f6b8fc Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 8/img/bruteforce.png differ diff --git "a/Deliverables/Phase2/Implementation/Use Case 9/Use Case 9 - Implementa\303\247\303\243o.md" "b/Deliverables/Phase2/Implementation/Use Case 9/Use Case 9 - Implementa\303\247\303\243o.md" index 2e0777f8..2c28d8d4 100644 --- "a/Deliverables/Phase2/Implementation/Use Case 9/Use Case 9 - Implementa\303\247\303\243o.md" +++ "b/Deliverables/Phase2/Implementation/Use Case 9/Use Case 9 - Implementa\303\247\303\243o.md" @@ -24,19 +24,73 @@ possa ser confundido com o de recuperação de palavra-passe para um ataque de p - **Ataques de Sobrecarga:** -1. ASVS 8.1.4: Verificar se a aplicação pode detetar e alertar sobre um número anormal de pedidos, como por IP, utilizador, total por hora ou dia, ou qualquer que seja sensato para a aplicação. +ASVS 8.1.4: Verificar se a aplicação pode detetar e alertar sobre um número anormal de pedidos, como por IP, utilizador, total por hora ou dia, ou qualquer que seja sensato para a aplicação. - **Injeção de Código Malicioso:** -1. ASVS 5.3.3: Verificar que a aplicação, de preferência de forma automática ou, em último caso, manual, efetua a escape adequada de saída para proteger contra XSS refletido, armazenado e baseado em DOM. +ASVS 5.3.3: Verificar que a aplicação, de preferência de forma automática ou, em último caso, manual, efetua a escape adequada de saída para proteger contra XSS refletido, armazenado e baseado em DOM. - **Interceção de pedidos:** -1. ASVS 9.1.1: Verificar se todas as credenciais, tokens de autenticação e informações sensíveis são transportadas apenas através de um canal criptografado (TLS). +ASVS 9.1.1: Verificar se todas as credenciais, tokens de autenticação e informações sensíveis são transportadas apenas através de um canal criptografado (TLS). + +**Nota:** Para cada caso de abuso foi analisado qual o ASVS que melhor se relaciona com o caso de abuso, tendo em conta que com a implementação deste, outros ASVS são também abordados. +Este estudo foi feito no Excel a entregar. ## Implementação da UC Esta UC já se encontra desenvolvida. Apenas a aplicação dos ASVS foram necessários. -## Implementação dos ASVS \ No newline at end of file +## Implementação dos ASVS + +### ASVS 8.1.4 + +Tal como já foi descrito no [Use Case 3 - Implementação.md](..%2FUse%20Case%203%2FUse%20Case%203%20-%20Implementa%E7%E3o.md) e [Use Case 7 - Implementação.md](..%2FUse%20Case%207%2FUse%20Case%207%20-%20Implementa%E7%E3o.md), +para implementar o ASVS 8.1.4 na aplicação, foi criado o componente RateLimiterInterceptor, responsável por controlar a quantidade de solicitações recebidas por IP. + +Para permitir que aplicacão seja capaz de detectar e alertar sobre números anormais de solicitações, basta +adicionar o endpoint que é chamado no momento em que é feito o pedido de recuperação de password ("/account/password/forgot"), ao ficheiro de configuração +que define quais os endpoints que deve ser monitorados pelo interceptor: + +![interceptor.png](img%2Finterceptor.png) + +Desta forma, se um utilizador tentar enviar demasiados pedidos o seu IP irá ficar bloqueado durante o periodo de tempo configurado. + +![tooMany.png](img%2FtooMany.png) +
+
+ +### ASVS 5.3.3 + +Tal como na implementação feita no [Use Case 7 - Implementação.md](..%2FUse%20Case%207%2FUse%20Case%207%20-%20Implementa%E7%E3o.md), no momento em que o utilizador introduz um e-mail +é feito primeiro uma validação do texto introduzido utilizando uma gramática que valida se o texto tem o formato de um e-mail: + +![passwordGrammar.png](img%2FpasswordGrammar.png) + +Caso o texto introduzido não tenha o formato de e-mail, não lhe é permitida a sumbissão, e uma mensagem de erro é mostrada. + +![error.png](img%2Ferror.png) + + +Além disso, todos os e-mails e senhas são apenas armazenados na base de dados e nunca são exibidos na UI, o que ajuda a proteger a aplicação. +No entanto, a proteção contra XSS refletido, armazenado e baseado em DOM é garantida principalmente pelas medidas de sanitização e validação implementadas tanto no front-end quanto no back-end, estando assim de acordo com o ASVS 5.3.3. +
+
+ +### ASVS 9.1.1 + +Embora a implementação de um canal criptografado (TLS) para transportar informações sensíveis não previna a interceção +de pedidos, é capaz de proteger as mensagens de maneira que, mesmo que um atacante consiga interceptar essas mensagens, ele não consiga lê-las ou desencriptá-las. + +Assim, se todas as comunicações realizadas entre cliente e servidor forem comunicações via HTTPS com certificado TLS, podemos garantir que este caso de abuso, assim +como qualquer caso de abuso associado a interceção de mensgaens, está mitigado. + +Posto isto, a equipa implementou a comunicação baseada em HTTPS com certificado TLS para todas as +comunicações entre cliente e servidor. + +![https.png](img%2Fhttps.png) + +Deste modo, toda a informação sensível enviada para o endpoint de esquecimento de password (/api/public/password/forgot") está protegida. + +![httpsCall.png](img%2FhttpsCall.png) diff --git a/Deliverables/Phase2/Implementation/Use Case 9/img/error.png b/Deliverables/Phase2/Implementation/Use Case 9/img/error.png new file mode 100644 index 00000000..c23584db Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 9/img/error.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 9/img/https.png b/Deliverables/Phase2/Implementation/Use Case 9/img/https.png new file mode 100644 index 00000000..eef7fe98 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 9/img/https.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 9/img/httpsCall.png b/Deliverables/Phase2/Implementation/Use Case 9/img/httpsCall.png new file mode 100644 index 00000000..f41acdf7 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 9/img/httpsCall.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 9/img/interceptor.png b/Deliverables/Phase2/Implementation/Use Case 9/img/interceptor.png new file mode 100644 index 00000000..880e8ed1 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 9/img/interceptor.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 9/img/passwordGrammar.png b/Deliverables/Phase2/Implementation/Use Case 9/img/passwordGrammar.png new file mode 100644 index 00000000..4861dce3 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 9/img/passwordGrammar.png differ diff --git a/Deliverables/Phase2/Implementation/Use Case 9/img/tooMany.png b/Deliverables/Phase2/Implementation/Use Case 9/img/tooMany.png new file mode 100644 index 00000000..201563d3 Binary files /dev/null and b/Deliverables/Phase2/Implementation/Use Case 9/img/tooMany.png differ diff --git a/authorization_server/src/main/java/com/commerce/oauth/config/ServerSecurityConfig.java b/authorization_server/src/main/java/com/commerce/oauth/config/ServerSecurityConfig.java index f065829c..0c180efa 100644 --- a/authorization_server/src/main/java/com/commerce/oauth/config/ServerSecurityConfig.java +++ b/authorization_server/src/main/java/com/commerce/oauth/config/ServerSecurityConfig.java @@ -1,5 +1,6 @@ package com.commerce.oauth.config; +import com.commerce.oauth.filter.RateLimitFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.security.SecurityProperties; @@ -14,6 +15,9 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.servlet.Filter; @Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER) @@ -26,6 +30,9 @@ public class ServerSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private PasswordEncoder userPasswordEncoder; + @Autowired + private RateLimitFilter rateLimitFilter; + @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { @@ -39,11 +46,11 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception { @Override protected void configure(final HttpSecurity http) throws Exception { - http.authorizeRequests() - .antMatchers(HttpMethod.POST, "/ouath/token").permitAll() + http.addFilterBefore(rateLimitFilter, UsernamePasswordAuthenticationFilter.class) // Adiciona o filtro antes do filtro de autenticação + .authorizeRequests() + .antMatchers(HttpMethod.POST, "/oauth/token").permitAll() .anyRequest().authenticated() .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler()) .and().csrf().disable(); } - -} \ No newline at end of file +} diff --git a/authorization_server/src/main/java/com/commerce/oauth/interceptor/RateLimitFilter.java b/authorization_server/src/main/java/com/commerce/oauth/interceptor/RateLimitFilter.java new file mode 100644 index 00000000..6a99d161 --- /dev/null +++ b/authorization_server/src/main/java/com/commerce/oauth/interceptor/RateLimitFilter.java @@ -0,0 +1,70 @@ +package com.commerce.oauth.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class RateLimitFilter implements Filter { + + private static final int MAX_REQUESTS = 10; // Número máximo de solicitações permitidas + private static final long TIME_INTERVAL = 60000; // Intervalo de tempo em milissegundos (1 minuto) + private static final Map> requestMap = new ConcurrentHashMap<>(); + + private static final Logger logger = LoggerFactory.getLogger(RateLimitFilter.class); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // Inicialização se necessário + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + String ip = request.getRemoteAddr(); + logger.info("Request from IP: " + ip); // Log IP address + + Queue requests = requestMap.computeIfAbsent(ip, k -> new LinkedList<>()); + + // Remova as solicitações mais antigas do mapa + long currentTime = System.currentTimeMillis(); + while (!requests.isEmpty() && currentTime - requests.peek() > TIME_INTERVAL) { + requests.poll(); + } + + // Verifique se o limite de solicitações foi excedido + if (requests.size() >= MAX_REQUESTS) { + logger.info("IP " + ip + " exceeded the rate limit."); // Log rate limit exceedance + response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); + return; // Bloqueia a execução do endpoint + } + + // Registre a nova solicitação + requests.offer(currentTime); + logger.info("Request registered for IP: " + ip + ". Current count: " + requests.size()); // Log request count + + chain.doFilter(req, res); // Continua com a cadeia de filtros + } + + @Override + public void destroy() { + // Cleanup se necessário + } +} diff --git a/resource_server/pom.xml b/resource_server/pom.xml index 5c2456b9..354d0627 100644 --- a/resource_server/pom.xml +++ b/resource_server/pom.xml @@ -138,6 +138,28 @@ 1.0.2 test + + + org.owasp.encoder + encoder + 1.2.3 + + + + com.github.vladimir-bukhtoyarov + bucket4j-core + 7.4.0 + + + org.springframework.boot + spring-boot-starter-cache + + + com.github.ben-manes.caffeine + caffeine + 2.9.2 + + diff --git a/resource_server/src/main/java/com/commerce/backend/api/ProductController.java b/resource_server/src/main/java/com/commerce/backend/api/ProductController.java index 0f006262..844b250c 100644 --- a/resource_server/src/main/java/com/commerce/backend/api/ProductController.java +++ b/resource_server/src/main/java/com/commerce/backend/api/ProductController.java @@ -1,17 +1,17 @@ package com.commerce.backend.api; import com.commerce.backend.error.exception.InvalidArgumentException; +import com.commerce.backend.model.request.product.CreateProductRequest; import com.commerce.backend.model.response.product.ProductDetailsResponse; import com.commerce.backend.model.response.product.ProductResponse; import com.commerce.backend.model.response.product.ProductVariantResponse; import com.commerce.backend.service.ProductService; +import org.owasp.encoder.Encode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + import java.util.List; import java.util.Objects; @@ -95,7 +95,15 @@ public ResponseEntity> getByInterested() { public ResponseEntity> searchProduct(@RequestParam("page") Integer page, @RequestParam("size") Integer size, @RequestParam("keyword") String keyword) { - List products = productService.searchProductDisplay(keyword, page, size); + + String sanitizedKeyword = Encode.forHtmlContent(keyword); + List products = productService.searchProductDisplay(sanitizedKeyword, page, size); return new ResponseEntity<>(products, HttpStatus.OK); } + + @PostMapping(value = "/product") + public ResponseEntity createNewProduct(@RequestBody CreateProductRequest productRequest) { + ProductResponse product = productService.createProduct(productRequest); + return new ResponseEntity<>(product, HttpStatus.OK); + } } diff --git a/resource_server/src/main/java/com/commerce/backend/api/PublicUserController.java b/resource_server/src/main/java/com/commerce/backend/api/PublicUserController.java index 6dfc9b81..446d0d91 100644 --- a/resource_server/src/main/java/com/commerce/backend/api/PublicUserController.java +++ b/resource_server/src/main/java/com/commerce/backend/api/PublicUserController.java @@ -27,7 +27,7 @@ public PublicUserController(UserService userService, TokenService tokenService) } @PostMapping(value = "/account/registration") - public ResponseEntity registerUser(@RequestBody @Valid RegisterUserRequest registerUserRequest) { + public ResponseEntity registerUser(@RequestBody @Valid RegisterUserRequest registerUserRequest) throws Exception { User user = userService.register(registerUserRequest); tokenService.createEmailConfirmToken(user); return new ResponseEntity<>(HttpStatus.OK); diff --git a/resource_server/src/main/java/com/commerce/backend/config/WebMvcConfig.java b/resource_server/src/main/java/com/commerce/backend/config/WebMvcConfig.java new file mode 100644 index 00000000..4d8b6965 --- /dev/null +++ b/resource_server/src/main/java/com/commerce/backend/config/WebMvcConfig.java @@ -0,0 +1,23 @@ +package com.commerce.backend.config; + +import com.commerce.backend.interceptor.RateLimitInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Autowired + private RateLimitInterceptor rateLimitInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(rateLimitInterceptor) + .addPathPatterns("/api/public/product/search", + "/api/public/account/registration", + "/api/public/account/password/forgot", + "/api/public/product"); + } +} diff --git a/resource_server/src/main/java/com/commerce/backend/interceptor/RateLimitInterceptor.java b/resource_server/src/main/java/com/commerce/backend/interceptor/RateLimitInterceptor.java new file mode 100644 index 00000000..447ce2d6 --- /dev/null +++ b/resource_server/src/main/java/com/commerce/backend/interceptor/RateLimitInterceptor.java @@ -0,0 +1,44 @@ +package com.commerce.backend.interceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class RateLimitInterceptor implements HandlerInterceptor { + + private static final int MAX_REQUESTS = 10; // Número máximo de solicitações permitidas + private static final long TIME_INTERVAL = 60000; // Intervalo de tempo em milissegundos (1 minuto) + private static final Map> requestMap = new ConcurrentHashMap<>(); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + String ip = request.getRemoteAddr(); + Queue requests = requestMap.computeIfAbsent(ip, k -> new LinkedList<>()); + + // Remove as solicitações mais antigas do mapa + long currentTime = System.currentTimeMillis(); + while (!requests.isEmpty() && currentTime - requests.peek() > TIME_INTERVAL) { + requests.poll(); + } + + // Verifiqua se o limite de solicitações foi excedido + if (requests.size() >= MAX_REQUESTS) { + response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); + return false; // Bloqueia a execução do endpoint + } + + // Regista a nova solicitação + requests.offer(currentTime); + return true; // Permite a execução do endpoint + } +} diff --git a/resource_server/src/main/java/com/commerce/backend/model/request/product/CreateProductRequest.java b/resource_server/src/main/java/com/commerce/backend/model/request/product/CreateProductRequest.java new file mode 100644 index 00000000..0518ec8d --- /dev/null +++ b/resource_server/src/main/java/com/commerce/backend/model/request/product/CreateProductRequest.java @@ -0,0 +1,18 @@ +package com.commerce.backend.model.request.product; + +import lombok.Data; + +@Data +public class CreateProductRequest { + + public Long productCategoryId; + + public String sku; + + public String name; + + private String url; + + private String longDesc; + +} diff --git a/resource_server/src/main/java/com/commerce/backend/service/ProductService.java b/resource_server/src/main/java/com/commerce/backend/service/ProductService.java index 5d63edf9..13853fdd 100644 --- a/resource_server/src/main/java/com/commerce/backend/service/ProductService.java +++ b/resource_server/src/main/java/com/commerce/backend/service/ProductService.java @@ -1,6 +1,7 @@ package com.commerce.backend.service; import com.commerce.backend.model.entity.ProductVariant; +import com.commerce.backend.model.request.product.CreateProductRequest; import com.commerce.backend.model.response.product.ProductDetailsResponse; import com.commerce.backend.model.response.product.ProductResponse; import com.commerce.backend.model.response.product.ProductVariantResponse; @@ -25,4 +26,6 @@ public interface ProductService { List getInterested(); List searchProductDisplay(String keyword, Integer page, Integer size); + + ProductResponse createProduct(CreateProductRequest productRequest); } diff --git a/resource_server/src/main/java/com/commerce/backend/service/ProductServiceImpl.java b/resource_server/src/main/java/com/commerce/backend/service/ProductServiceImpl.java index 524818de..6ae3895d 100644 --- a/resource_server/src/main/java/com/commerce/backend/service/ProductServiceImpl.java +++ b/resource_server/src/main/java/com/commerce/backend/service/ProductServiceImpl.java @@ -9,6 +9,7 @@ import com.commerce.backend.error.exception.ResourceNotFoundException; import com.commerce.backend.model.entity.Product; import com.commerce.backend.model.entity.ProductVariant; +import com.commerce.backend.model.request.product.CreateProductRequest; import com.commerce.backend.model.response.product.ProductDetailsResponse; import com.commerce.backend.model.response.product.ProductResponse; import com.commerce.backend.model.response.product.ProductVariantResponse; @@ -171,6 +172,11 @@ public List searchProductDisplay(String keyword, Integer page, .collect(Collectors.toList()); } + @Override + public ProductResponse createProduct(CreateProductRequest productRequest) { + throw new UnsupportedOperationException("not implemented yet"); + } + private Sort getSort(String sort) { switch (sort) { diff --git a/resource_server/src/main/java/com/commerce/backend/service/UserService.java b/resource_server/src/main/java/com/commerce/backend/service/UserService.java index 18840df4..1a250578 100644 --- a/resource_server/src/main/java/com/commerce/backend/service/UserService.java +++ b/resource_server/src/main/java/com/commerce/backend/service/UserService.java @@ -9,7 +9,7 @@ import com.commerce.backend.model.response.user.UserResponse; public interface UserService { - User register(RegisterUserRequest registerUserRequest); + User register(RegisterUserRequest registerUserRequest) throws Exception; UserResponse fetchUser(); diff --git a/resource_server/src/main/java/com/commerce/backend/service/UserServiceImpl.java b/resource_server/src/main/java/com/commerce/backend/service/UserServiceImpl.java index 1f491942..e0ae58f1 100644 --- a/resource_server/src/main/java/com/commerce/backend/service/UserServiceImpl.java +++ b/resource_server/src/main/java/com/commerce/backend/service/UserServiceImpl.java @@ -13,6 +13,7 @@ import com.commerce.backend.model.request.user.UpdateUserAddressRequest; import com.commerce.backend.model.request.user.UpdateUserRequest; import com.commerce.backend.model.response.user.UserResponse; +import org.owasp.encoder.Encode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.context.SecurityContextHolder; @@ -42,14 +43,28 @@ public UserServiceImpl(UserRepository userRepository, } @Override - public User register(RegisterUserRequest registerUserRequest) { + public User register(RegisterUserRequest registerUserRequest) throws Exception { if (userExists(registerUserRequest.getEmail())) { throw new InvalidArgumentException("An account already exists with this email"); } + String userEmail = registerUserRequest.getEmail(); + String userPassword = registerUserRequest.getPassword(); + + String sanitizedEmail = Encode.forHtml(userEmail); + String sanitizedPassword = Encode.forHtml(userPassword); + + if(!sanitizedPassword.equals(userPassword)){ + throw new Exception("Invalid Password: "+userPassword); + } + + if(!sanitizedEmail.equals(userEmail)){ + throw new Exception("Invalid Email: "+userEmail); + } + User user = new User(); - user.setEmail(registerUserRequest.getEmail()); - user.setPassword(passwordEncoder.encode(registerUserRequest.getPassword())); + user.setEmail(sanitizedEmail); + user.setPassword(passwordEncoder.encode(sanitizedPassword)); user.setEmailVerified(0); Optional role = roleRepository.findByName(RoleEnum.USER); diff --git a/resource_server/src/test/java/com/commerce/backend/service/UserServiceImplTest.java b/resource_server/src/test/java/com/commerce/backend/service/UserServiceImplTest.java index 15df8139..d4f2e129 100644 --- a/resource_server/src/test/java/com/commerce/backend/service/UserServiceImplTest.java +++ b/resource_server/src/test/java/com/commerce/backend/service/UserServiceImplTest.java @@ -110,7 +110,7 @@ public void setAuthenticated(boolean b) throws IllegalArgumentException { @Test - void it_should_register_a_user() { + void it_should_register_a_user() throws Exception { // given String email = faker.lorem().word();