package sql_injection import ( "context" "encoding/json" "fmt" "log" "net/http" "regexp" "github.com/Vomitblood/cspj-application/server/internal/db" ) // secure register endpoint func UnsecureRegisterSql(w http.ResponseWriter, r *http.Request) { // read the request body var credentials struct { Email string `json:"email"` Password string `json:"password"` RePassword string `json:"rePassword"` } if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil { http.Error(w, "Invalid request format", http.StatusBadRequest) return } defer r.Body.Close() // check if the email is an email using regex, if not reject emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` re := regexp.MustCompile(emailRegex) if !re.MatchString(credentials.Email) { http.Error(w, "Invalid email format", http.StatusBadRequest) return } // check if the password matches, if not reject if credentials.Password != credentials.RePassword { http.Error(w, "Passwords do not match", http.StatusBadRequest) return } // get the number of emails that matches // construct the sql query using concatenation, BAD emailCheckSQL := fmt.Sprintf("SELECT COUNT(*) FROM users WHERE email = '%s'", credentials.Email) var existingUserCount int err := db.DbPool.QueryRow(context.Background(), emailCheckSQL).Scan(&existingUserCount) if err != nil { http.Error(w, "Error checking email in the database", http.StatusInternalServerError) log.Printf("Error checking email: %v", err) return } // if there is more than 0 matches, that means email already exists, reject if existingUserCount > 0 { http.Error(w, "Email already exists", http.StatusConflict) return } // over here the validations has passed, so insert into the db // also use concatenation here insertSQL := fmt.Sprintf("INSERT INTO users (email, password, role) VALUES ('%s', '%s', 'user')", credentials.Email, credentials.Password) _, err = db.DbPool.Exec(context.Background(), insertSQL) if err != nil { http.Error(w, "Error inserting user into the database", http.StatusInternalServerError) log.Printf("Error inserting user: %v", err) return } // send back status ok w.WriteHeader(http.StatusOK) w.Write([]byte("User registered successfully")) log.Println("User registered successfully:", credentials.Email) } // secure register endpoint func SecureRegisterSql(w http.ResponseWriter, r *http.Request) { // read the request body var credentials struct { Email string `json:"email"` Password string `json:"password"` RePassword string `json:"rePassword"` } if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil { http.Error(w, "Invalid request format", http.StatusBadRequest) return } defer r.Body.Close() // check if the email is an email using regex, if not reject emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` re := regexp.MustCompile(emailRegex) if !re.MatchString(credentials.Email) { http.Error(w, "Invalid email format", http.StatusBadRequest) return } // check if the password matches, if not reject if credentials.Password != credentials.RePassword { http.Error(w, "Passwords do not match", http.StatusBadRequest) return } // get the number of emails that matches // use parameterization var existingUserCount int emailCheckSQL := `SELECT COUNT(*) FROM users WHERE email = $1` err := db.DbPool.QueryRow(context.Background(), emailCheckSQL, credentials.Email).Scan(&existingUserCount) if err != nil { http.Error(w, "Error checking email in the database", http.StatusInternalServerError) log.Printf("Error checking email: %v", err) return } // if there is more than 0 matches, that means email already exists, reject if existingUserCount > 0 { http.Error(w, "Email already exists", http.StatusConflict) return } // over here the validations has passed, so insert into the db // use parameterization insertSQL := `INSERT INTO users (email, password, role) VALUES ($1, $2, $3)` _, err = db.DbPool.Exec(context.Background(), insertSQL, credentials.Email, credentials.Password, "user") if err != nil { http.Error(w, "Error inserting user into the database", http.StatusInternalServerError) log.Printf("Error inserting user: %v", err) return } // send back status ok w.WriteHeader(http.StatusOK) w.Write([]byte("User registered successfully")) log.Println("User registered successfully:", credentials.Email) } func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) { // decode the json body var requestData struct { Email string `json:"email"` Password string `json:"password"` } // decode the request body if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { http.Error(w, "Invalid request format", http.StatusBadRequest) log.Println("Failed to decode JSON body:", err) return } // make insecure sql command query := fmt.Sprintf("SELECT id, email, password FROM users WHERE email = '%s' AND password = '%s'", requestData.Email, requestData.Password) var id int var email string var password string // execute the query to retrieve the data err := db.DbPool.QueryRow(context.Background(), query).Scan(&id, &email, &password) if err != nil { http.Error(w, "Invalid email or password", http.StatusUnauthorized) log.Println("Error during query execution:", err) return } // if the email and password match then send back the user data response := map[string]interface{}{ "id": id, "email": email, } if err := json.NewEncoder(w).Encode(response); err != nil { http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError) log.Printf("JSON encoding error: %v", err) } } // very secure login endpoint func SecureLoginSql(w http.ResponseWriter, r *http.Request) { // decode the json body var requestData struct { Email string `json:"email"` Password string `json:"password"` } // declare new json decoder with custom property jsonDecoder := json.NewDecoder(r.Body) // rejects any unknown fields in the json, more strict jsonDecoder.DisallowUnknownFields() if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { http.Error(w, "Invalid request format", http.StatusBadRequest) log.Println("Failed to decode JSON body or extra fields present:", err) return } // validate that user is provided if requestData.Email == "" || requestData.Password == "" { http.Error(w, "Invalid request format", http.StatusBadRequest) return } // retrieve list of existing emails from db existingEmails, err := db.FetchEmails() if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) log.Printf("Failed to fetch emails: %v", err) return } // check if the email exists in the allowed list // this step is crucial // server will reject ANYTHING that does not match the list if !existingEmails[requestData.Email] { http.Error(w, "Invalid email or password", http.StatusBadRequest) return } // retrieve the email and password from the database var id int var email string var storedPassword string query := "SELECT id, email, password FROM users WHERE email = $1" err = db.DbPool.QueryRow(context.Background(), query, requestData.Email).Scan(&id, &email, &storedPassword) if err != nil { http.Error(w, "Invalid email or password", http.StatusNotFound) return } // compare the provided password with the stored plain-text password if storedPassword != requestData.Password { http.Error(w, "Invalid email or password", http.StatusUnauthorized) return } // send back the user data as a json response response := map[string]interface{}{ "id": id, "email": email, } if err := json.NewEncoder(w).Encode(response); err != nil { http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError) log.Printf("JSON encoding error: %v", err) } }