diff --git a/owasp-top10-2021-apps/a1/ecommerce-api/Makefile b/owasp-top10-2021-apps/a1/ecommerce-api/Makefile index 24da4a39a..411339ca3 100644 --- a/owasp-top10-2021-apps/a1/ecommerce-api/Makefile +++ b/owasp-top10-2021-apps/a1/ecommerce-api/Makefile @@ -47,11 +47,11 @@ lint: ## Runs project using docker-compose compose: compose-down - docker-compose -f deployments/docker-compose.yml -p secdevlabs up -d --build --force-recreate + podman-compose -f deployments/docker-compose.yml -p secdevlabs up -d --build --force-recreate ## Down project using docker-compose compose-down: - docker-compose -f deployments/docker-compose.yml -p secdevlabs down -v --remove-orphans + podman-compose -f deployments/docker-compose.yml -p secdevlabs down -v --remove-orphans ## Generates passwords and set them as environment variables generate-passwords: diff --git a/owasp-top10-2021-apps/a1/ecommerce-api/app/handlers/handlers.go b/owasp-top10-2021-apps/a1/ecommerce-api/app/handlers/handlers.go index 97afd8422..659220244 100644 --- a/owasp-top10-2021-apps/a1/ecommerce-api/app/handlers/handlers.go +++ b/owasp-top10-2021-apps/a1/ecommerce-api/app/handlers/handlers.go @@ -8,49 +8,61 @@ import ( "github.com/labstack/echo" ) -// HealthCheck is the heath check function. +// HealthCheck is the health check function. func HealthCheck(c echo.Context) error { return c.String(http.StatusOK, "WORKING\n") } // GetTicket returns the userID ticket. func GetTicket(c echo.Context) error { - // Obter o userID do contexto (definido por um middleware de autenticação) - userIDFromContext := c.Get("userID").(string) // Assumindo que o middleware adiciona userID no contexto - - // Obter o userID da URL - id := c.Param("id") - - // Verificar se o userID autenticado corresponde ao userID fornecido - if userIDFromContext != id { - return c.JSON(http.StatusForbidden, map[string]string{ - "result": "error", - "details": "Access denied. You are not authorized to view this ticket.", - }) - } - - // Consultar o banco de dados com base no userID - userDataQuery := map[string]interface{}{"userID": id} - userDataResult, err := db.GetUserData(userDataQuery) - if err != nil { - // Erro ao buscar dados do usuário no MongoDB - return c.JSON(http.StatusBadRequest, map[string]string{ - "result": "error", - "details": "Error finding this UserID.", - }) - } - - // Verificar o formato da resposta (JSON ou texto) - format := c.QueryParam("format") - if format == "json" { - return c.JSON(http.StatusOK, map[string]string{ - "result": "success", - "username": userDataResult.Username, - "ticket": userDataResult.Ticket, - }) - } - - // Resposta em texto simples - msgTicket := fmt.Sprintf("Hey, %s! This is your ticket: %s\n", userDataResult.Username, userDataResult.Ticket) - return c.String(http.StatusOK, msgTicket) -} + // Obter o userID do contexto + userIDFromContext, ok := c.Get("userID").(string) + if !ok { + return c.JSON(http.StatusUnauthorized, map[string]string{ + "result": "error", + "details": "Invalid user authentication data.", + }) + } + + // Obter o userID da URL + id := c.Param("id") + if id == "" { + return c.JSON(http.StatusBadRequest, map[string]string{ + "result": "error", + "details": "User ID is required.", + }) + } + + // Verificar se o userID autenticado corresponde ao userID fornecido + if userIDFromContext != id { + return c.JSON(http.StatusForbidden, map[string]string{ + "result": "error", + "details": "Access denied. You are not authorized to view this ticket.", + }) + } + + // Consultar o banco de dados com base no userID + userDataQuery := map[string]interface{}{"userID": id} + userDataResult, err := db.GetUserData(userDataQuery) + if err != nil { + c.Logger().Errorf("Error querying user data: %v", err) + return c.JSON(http.StatusInternalServerError, map[string]string{ + "result": "error", + "details": "An internal error occurred. Please try again later.", + }) + } + + // Verificar o formato da resposta + format := c.QueryParam("format") + if format == "json" { + return c.JSON(http.StatusOK, map[string]interface{}{ + "result": "success", + "username": userDataResult.Username, + "ticket": userDataResult.Ticket, + }) + } + + // Resposta em texto simples + msgTicket := fmt.Sprintf("Hey, %s! This is your ticket: %s\n", userDataResult.Username, userDataResult.Ticket) + return c.String(http.StatusOK, msgTicket) +} \ No newline at end of file diff --git a/owasp-top10-2021-apps/a1/ecommerce-api/app/server.go b/owasp-top10-2021-apps/a1/ecommerce-api/app/server.go index 9c0cec902..93a6d5bd4 100644 --- a/owasp-top10-2021-apps/a1/ecommerce-api/app/server.go +++ b/owasp-top10-2021-apps/a1/ecommerce-api/app/server.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "os" + "strings" apiContext "github.com/globocom/secDevLabs/owasp-top10-2021-apps/a1/ecommerce-api/app/context" "github.com/globocom/secDevLabs/owasp-top10-2021-apps/a1/ecommerce-api/app/handlers" @@ -34,27 +35,42 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c func isAuthorized(dbInstance *db.DB) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { + // Recuperar o token JWT do contexto user, ok := c.Get("user").(*jwt.Token) if !ok { - return echo.ErrUnauthorized + return echo.ErrUnauthorized // Retorna 401 se o token estiver ausente ou inválido } + // Extrair as claims do token claims, ok := user.Claims.(jwt.MapClaims) if !ok { - return echo.ErrUnauthorized + return echo.ErrUnauthorized // Retorna 401 se as claims não puderem ser extraídas } - userID := claims["id"] + // Garantir que a claim "id" exista + userID, ok := claims["id"].(string) + if !ok { + return echo.ErrUnauthorized // Retorna 401 se o campo "id" não existir ou estiver no formato errado + } + + // Recuperar o ID do ticket da rota ticketID := c.Param("id") - if !userHasAccessToTicket(dbInstance, fmt.Sprintf("%v", userID), ticketID) { - return echo.ErrUnauthorized + if ticketID == "" { + return echo.ErrBadRequest // Retorna 400 se o ticket ID não for informado } + // Verificar permissão no banco de dados + if !userHasAccessToTicket(dbInstance, userID, ticketID) { + return echo.ErrUnauthorized // Retorna 401 se o usuário não tiver permissão + } + + // Prosseguir para o próximo handler return next(c) } } } + // Checks if a user has access to a specific ticket func userHasAccessToTicket(dbInstance *db.DB, userID, ticketID string) bool { hasPermission, err := dbInstance.CheckUserPermission(userID, ticketID) @@ -66,30 +82,44 @@ func userHasAccessToTicket(dbInstance *db.DB, userID, ticketID string) bool { // Middleware: Auth checks JWT token func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - token := c.Request().Header.Get("Authorization") - if token == "" { - return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Unauthorized"}) - } - - _, err := parseToken(token) - if err != nil { - return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid token"}) - } - - c.Set("userID", "exampleUserID") // Exemplo simples - return next(c) - } + return func(c echo.Context) error { + tokenHeader := c.Request().Header.Get("Authorization") + if !strings.HasPrefix(tokenHeader, "Bearer ") { + return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid token format"}) + } + + token := strings.TrimPrefix(tokenHeader, "Bearer ") + userID, err := parseToken(token) + if err != nil { + return c.JSON(http.StatusUnauthorized, map[string]string{"error": err.Error()}) + } + + c.Set("userID", userID) + return next(c) + } } -// Simulated token parser func parseToken(token string) (string, error) { - if token == "valid-token" { - return "user123", nil - } - return "", errors.New("invalid token") + parsedToken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { + return []byte("secretKey"), nil + }) + if err != nil || !parsedToken.Valid { + return "", errors.New("invalid token") + } + + claims, ok := parsedToken.Claims.(jwt.MapClaims) + if !ok { + return "", errors.New("invalid token claims") + } + + userID, ok := claims["id"].(string) + if !ok { + return "", errors.New("invalid user ID in token") + } + return userID, nil } + func main() { configAPI := apiContext.GetAPIConfig() @@ -121,10 +151,13 @@ func main() { echoInstance.GET("/healthcheck", handlers.HealthCheck) echoInstance.POST("/register", handlers.RegisterUser) echoInstance.POST("/login", handlers.Login) - + ticketGroup := echoInstance.Group("/ticket") - ticketGroup.Use(isAuthorized(database)) - ticketGroup.GET("/:id", handlers.GetTicket) + ticketGroup.Use(middleware.JWTWithConfig(middleware.JWTConfig{ + SigningKey: []byte("secretKey"), // Substitua por uma variável de ambiente + })) + ticketGroup.Use(isAuthorized(database)) + ticketGroup.GET("/:id", handlers.GetTicket) APIport := fmt.Sprintf(":%d", configAPI.APIPort) echoInstance.Logger.Fatal(echoInstance.Start(APIport))