From bb6363bdcdad69c26a5e3a035afed3ceebef8655 Mon Sep 17 00:00:00 2001 From: meiryleneavelino Date: Wed, 13 Nov 2024 15:05:10 -0300 Subject: [PATCH 1/6] =?UTF-8?q?Solu=C3=A7=C3=A3o=20Meiry=20Broken=20Acess?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../a1/ecommerce-api/app/handlers/handlers.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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 ea71f28d2..83890fc44 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 @@ -14,9 +14,20 @@ func HealthCheck(c echo.Context) error { } // GetTicket returns the userID ticket. +//Precisamos garantir que essa função só seja acessada por usuários autenticados +// O userID corresponde ao UserID do solicitante??? func GetTicket(c echo.Context) error { - id := c.Param("id") + + authuserID := c.Get("userID").string //Id do usuário autenticado + + id := c.Param("id") //extrai o ID do usuário da URL + + if authuserID != id{ + return c.JSON(http.StatusForbidden, map[string]string{"result": "error", "details": "Access denied."}) + } + userDataQuery := map[string]interface{}{"userID": id} + userDataResult, err := db.GetUserData(userDataQuery) if err != nil { // could not find this user in MongoDB (or MongoDB err connection) From 2966f0c5c4042c59f99cbedb04c7fd204f27600f Mon Sep 17 00:00:00 2001 From: meiryleneavelino Date: Wed, 13 Nov 2024 15:34:42 -0300 Subject: [PATCH 2/6] =?UTF-8?q?Solu=C3=A7=C3=A3o=20Meiry=20Broken=20Acess?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- owasp-top10-2021-apps/a1/ecommerce-api/app/handlers/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 83890fc44..3be2921c8 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 @@ -18,7 +18,7 @@ func HealthCheck(c echo.Context) error { // O userID corresponde ao UserID do solicitante??? func GetTicket(c echo.Context) error { - authuserID := c.Get("userID").string //Id do usuário autenticado + authuserID := c.Get("userID").(string)//Id do usuário autenticado id := c.Param("id") //extrai o ID do usuário da URL From 84d54b090179d3d355a34e4536cee4b347c54496 Mon Sep 17 00:00:00 2001 From: meiryleneavelino Date: Wed, 13 Nov 2024 16:35:44 -0300 Subject: [PATCH 3/6] =?UTF-8?q?Solu=C3=A7=C3=A3o-Broken=20Acess?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../a1/ecommerce-api/app/handlers/handlers.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 3be2921c8..096002418 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 @@ -18,11 +18,17 @@ func HealthCheck(c echo.Context) error { // O userID corresponde ao UserID do solicitante??? func GetTicket(c echo.Context) error { - authuserID := c.Get("userID").(string)//Id do usuário autenticado + //authuserId := c.Get("userID").(string)//Id do usuário autenticado + + authuserID,ok:= c.Get("userID").(string) + if !ok { + return c.JSON(http.StatusUnauthorized, map[string]string{"result": "error", "details": "User not authenticated"}) + } id := c.Param("id") //extrai o ID do usuário da URL + - if authuserID != id{ + if authuserId != id{ return c.JSON(http.StatusForbidden, map[string]string{"result": "error", "details": "Access denied."}) } From 84e309a57ac3fa4a5c49f7b849d9b9a7a5800bfc Mon Sep 17 00:00:00 2001 From: meiryleneavelino Date: Wed, 13 Nov 2024 17:33:42 -0300 Subject: [PATCH 4/6] =?UTF-8?q?Solu=C3=A7=C3=A3o-Broken=20Acess?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- owasp-top10-2021-apps/a1/ecommerce-api/app/handlers/handlers.go | 1 + 1 file changed, 1 insertion(+) 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 096002418..b2097036c 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 @@ -21,6 +21,7 @@ func GetTicket(c echo.Context) error { //authuserId := c.Get("userID").(string)//Id do usuário autenticado authuserID,ok:= c.Get("userID").(string) + if !ok { return c.JSON(http.StatusUnauthorized, map[string]string{"result": "error", "details": "User not authenticated"}) } From de628540d9998f21d20301f37f1222d2cc1dedfc Mon Sep 17 00:00:00 2001 From: meiryleneavelino Date: Tue, 26 Nov 2024 21:16:54 -0300 Subject: [PATCH 5/6] tentativa --- .../a1/ecommerce-api/app/db/mongo.go | 62 +++++---- .../a1/ecommerce-api/app/handlers/handlers.go | 75 +++++------ .../a1/ecommerce-api/app/server.go | 124 ++++++++++++------ 3 files changed, 156 insertions(+), 105 deletions(-) diff --git a/owasp-top10-2021-apps/a1/ecommerce-api/app/db/mongo.go b/owasp-top10-2021-apps/a1/ecommerce-api/app/db/mongo.go index d60a4e5f4..d5a1d097f 100644 --- a/owasp-top10-2021-apps/a1/ecommerce-api/app/db/mongo.go +++ b/owasp-top10-2021-apps/a1/ecommerce-api/app/db/mongo.go @@ -10,7 +10,8 @@ import ( // Collections names used in MongoDB. var ( - UserCollection = "users" + UserCollection = "users" + TicketsCollection = "tickets" // Adicione a coleção de tickets ) // DB is the struct that represents mongo session. @@ -34,6 +35,7 @@ type Database interface { UpdateAll(query, updateQuery bson.M, collection string) error Upsert(query bson.M, obj interface{}, collection string) (*mgo.ChangeInfo, error) SearchOne(query bson.M, selectors []string, collection string, obj interface{}) error + CheckUserPermission(userID, ticketID string) (bool, error) // Adicionado para refletir a sugestão } var config = &mongoConfig{ @@ -45,8 +47,6 @@ var config = &mongoConfig{ // Connect connects to mongo and returns the session. func Connect() (*DB, error) { - - // fmt.Printf("config:%#v", config) dialInfo := &mgo.DialInfo{ Addrs: []string{config.Address}, Timeout: time.Second * 60, @@ -64,31 +64,36 @@ func Connect() (*DB, error) { return nil, err } - //go autoReconnect(session) - return &DB{Session: session}, nil } -// autoReconnect checks mongo's connection each second and, if an error is found, reconnect to it. -func autoReconnect(session *mgo.Session) { - var err error - for { - err = session.Ping() - if err != nil { - session.Refresh() - err = session.Ping() - if err == nil { - } else { - } +// CheckUserPermission verifies if a user has access to a specific ticket. +func (db *DB) CheckUserPermission(userID, ticketID string) (bool, error) { + session := db.Session.Clone() + defer session.Close() + c := session.DB(config.DatabaseName).C(TicketsCollection) + + query := bson.M{ + "userID": userID, + "ticketID": ticketID, + } + + var result bson.M + err := c.Find(query).One(&result) + if err != nil { + if err == mgo.ErrNotFound { + return false, nil } - time.Sleep(time.Second * 1) + return false, err } + + return true, nil } // Insert inserts a new document. func (db *DB) Insert(obj interface{}, collection string) error { session := db.Session.Clone() - c := session.DB("").C(collection) + c := session.DB(config.DatabaseName).C(collection) defer session.Close() return c.Insert(obj) } @@ -96,26 +101,25 @@ func (db *DB) Insert(obj interface{}, collection string) error { // Update updates a single document. func (db *DB) Update(query, updateQuery interface{}, collection string) error { session := db.Session.Clone() - c := session.DB("").C(collection) + c := session.DB(config.DatabaseName).C(collection) defer session.Close() - err := c.Update(query, updateQuery) - return err + return c.Update(query, updateQuery) } // UpdateAll updates all documents that match the query. -func (db *DB) UpdateAll(query, updateQuery interface{}, collection string) error { +func (db *DB) UpdateAll(query, updateQuery bson.M, collection string) error { session := db.Session.Clone() - c := session.DB("").C(collection) + c := session.DB(config.DatabaseName).C(collection) defer session.Close() _, err := c.UpdateAll(query, updateQuery) return err } -// Search searchs all documents that match the query. If selectors are present, the return will be only the chosen fields. +// Search searches all documents that match the query. If selectors are present, the return will be only the chosen fields. func (db *DB) Search(query bson.M, selectors []string, collection string, obj interface{}) error { session := db.Session.Clone() defer session.Close() - c := session.DB("").C(collection) + c := session.DB(config.DatabaseName).C(collection) var err error if selectors != nil { @@ -133,11 +137,11 @@ func (db *DB) Search(query bson.M, selectors []string, collection string, obj in return err } -// SearchOne searchs for the first element that matchs with the given query. +// SearchOne searches for the first element that matches with the given query. func (db *DB) SearchOne(query bson.M, selectors []string, collection string, obj interface{}) error { session := db.Session.Clone() defer session.Close() - c := session.DB("").C(collection) + c := session.DB(config.DatabaseName).C(collection) var err error if selectors != nil { @@ -155,10 +159,10 @@ func (db *DB) SearchOne(query bson.M, selectors []string, collection string, obj return err } -// Upsert inserts a document or update it if it already exists. +// Upsert inserts a document or updates it if it already exists. func (db *DB) Upsert(query bson.M, obj interface{}, collection string) (*mgo.ChangeInfo, error) { session := db.Session.Clone() - c := session.DB("").C(collection) + c := session.DB(config.DatabaseName).C(collection) defer session.Close() return c.Upsert(query, obj) } 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 b2097036c..97afd8422 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 @@ -14,42 +14,43 @@ func HealthCheck(c echo.Context) error { } // GetTicket returns the userID ticket. -//Precisamos garantir que essa função só seja acessada por usuários autenticados -// O userID corresponde ao UserID do solicitante??? func GetTicket(c echo.Context) error { - - //authuserId := c.Get("userID").(string)//Id do usuário autenticado - - authuserID,ok:= c.Get("userID").(string) - - if !ok { - return c.JSON(http.StatusUnauthorized, map[string]string{"result": "error", "details": "User not authenticated"}) - } - - id := c.Param("id") //extrai o ID do usuário da URL - - - if authuserId != id{ - return c.JSON(http.StatusForbidden, map[string]string{"result": "error", "details": "Access denied."}) - } - - userDataQuery := map[string]interface{}{"userID": id} - - userDataResult, err := db.GetUserData(userDataQuery) - if err != nil { - // could not find this user in MongoDB (or MongoDB err connection) - return c.JSON(http.StatusBadRequest, map[string]string{"result": "error", "details": "Error finding this UserID."}) - } - - format := c.QueryParam("format") - if format == "json" { - return c.JSON(http.StatusOK, map[string]string{ - "result": "success", - "username": userDataResult.Username, - "ticket": userDataResult.Ticket, - }) - } - - 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 (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) } 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 b274f67c9..9c0cec902 100644 --- a/owasp-top10-2021-apps/a1/ecommerce-api/app/server.go +++ b/owasp-top10-2021-apps/a1/ecommerce-api/app/server.go @@ -5,34 +5,92 @@ import ( "fmt" "html/template" "io" + "net/http" "os" 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/db" "github.com/globocom/secDevLabs/owasp-top10-2021-apps/a1/ecommerce-api/app/handlers" + "github.com/globocom/secDevLabs/owasp-top10-2021-apps/a1/ecommerce-api/app/db" "github.com/labstack/echo" "github.com/labstack/echo/middleware" + "github.com/dgrijalva/jwt-go" ) // TemplateRegistry defines the template registry struct -// Ref: https://medium.freecodecamp.org/how-to-setup-a-nested-html-template-in-the-go-echo-web-framework-670f16244bb4 type TemplateRegistry struct { templates map[string]*template.Template } -// Render implement e.Renderer interface -// Ref: https://medium.freecodecamp.org/how-to-setup-a-nested-html-template-in-the-go-echo-web-framework-670f16244bb4 +// Render implements e.Renderer interface func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error { tmpl, ok := t.templates[name] if !ok { - err := errors.New("Template not found -> " + name) - return err + return fmt.Errorf("template not found: %s", name) } return tmpl.ExecuteTemplate(w, "base.html", data) } -func main() { +// Middleware: Checks if a user is authorized for a specific ticket +func isAuthorized(dbInstance *db.DB) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + user, ok := c.Get("user").(*jwt.Token) + if !ok { + return echo.ErrUnauthorized + } + + claims, ok := user.Claims.(jwt.MapClaims) + if !ok { + return echo.ErrUnauthorized + } + + userID := claims["id"] + ticketID := c.Param("id") + if !userHasAccessToTicket(dbInstance, fmt.Sprintf("%v", userID), ticketID) { + return echo.ErrUnauthorized + } + + 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) + if err != nil { + return false + } + return hasPermission +} + +// 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) + } +} +// Simulated token parser +func parseToken(token string) (string, error) { + if token == "valid-token" { + return "user123", nil + } + return "", errors.New("invalid token") +} + +func main() { configAPI := apiContext.GetAPIConfig() if err := checkRequirements(configAPI); err != nil { @@ -40,16 +98,20 @@ func main() { os.Exit(1) } + // Inicializa conexão com o banco de dados + database, err := db.Connect() + if err != nil { + fmt.Printf("Erro ao conectar ao banco de dados: %v\n", err) + os.Exit(1) + } + defer database.Session.Close() + echoInstance := echo.New() echoInstance.HideBanner = true - // Instantiate a template registry with an array of template set - // Ref: https://medium.freecodecamp.org/how-to-setup-a-nested-html-template-in-the-go-echo-web-framework-670f16244bb4 templates := make(map[string]*template.Template) templates["form.html"] = template.Must(template.ParseFiles("views/form.html", "views/base.html")) - echoInstance.Renderer = &TemplateRegistry{ - templates: templates, - } + echoInstance.Renderer = &TemplateRegistry{templates: templates} echoInstance.Use(middleware.Logger()) echoInstance.Use(middleware.Recover()) @@ -57,32 +119,28 @@ func main() { echoInstance.GET("/", handlers.FormPage) echoInstance.GET("/healthcheck", handlers.HealthCheck) - echoInstance.GET("/ticket/:id", handlers.GetTicket) echoInstance.POST("/register", handlers.RegisterUser) echoInstance.POST("/login", handlers.Login) + ticketGroup := echoInstance.Group("/ticket") + ticketGroup.Use(isAuthorized(database)) + ticketGroup.GET("/:id", handlers.GetTicket) + APIport := fmt.Sprintf(":%d", configAPI.APIPort) echoInstance.Logger.Fatal(echoInstance.Start(APIport)) - } func checkRequirements(configAPI *apiContext.APIConfig) error { - - // check if all environment variables are properly set. if err := checkEnvVars(); err != nil { return err } - - // check if MongoDB is accessible and credentials received are working. if err := checkMongoDB(); err != nil { return err } - return nil } func checkEnvVars() error { - envVars := []string{ "MONGO_HOST", "MONGO_DATABASE_NAME", @@ -90,36 +148,24 @@ func checkEnvVars() error { "MONGO_DATABASE_PASSWORD", } - var envIsSet bool - var allEnvIsSet bool - var errorString string - - env := make(map[string]string) - allEnvIsSet = true - for i := 0; i < len(envVars); i++ { - env[envVars[i]], envIsSet = os.LookupEnv(envVars[i]) - if !envIsSet { - errorString = errorString + envVars[i] + " " - allEnvIsSet = false + var missingVars []string + for _, v := range envVars { + if _, exists := os.LookupEnv(v); !exists { + missingVars = append(missingVars, v) } } - if allEnvIsSet == false { - finalError := fmt.Sprintf("check environment variables: %s", errorString) - return errors.New(finalError) + if len(missingVars) > 0 { + return fmt.Errorf("missing environment variables: %v", missingVars) } return nil } func checkMongoDB() error { - _, err := db.Connect() - if err != nil { - mongoError := fmt.Sprintf("check mongoDB: %s", err) - return errors.New(mongoError) + return fmt.Errorf("check MongoDB: %v", err) } - return nil } From 2bf96f2b78a34cc3803554f70fe31553b19157d4 Mon Sep 17 00:00:00 2001 From: meiryleneavelino Date: Wed, 27 Nov 2024 18:03:25 -0300 Subject: [PATCH 6/6] ajustes broken acess control --- .../a1/ecommerce-api/Makefile | 4 +- .../a1/ecommerce-api/app/handlers/handlers.go | 92 +++++++++++-------- .../a1/ecommerce-api/app/server.go | 87 ++++++++++++------ 3 files changed, 114 insertions(+), 69 deletions(-) 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))