cspj-application/server/internal/sql_injection/sql_injection.go

244 lines
7.7 KiB
Go
Raw Normal View History

2024-11-11 17:34:37 +08:00
package sql_injection
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
2025-01-14 02:39:24 +08:00
"regexp"
2024-11-11 17:34:37 +08:00
"github.com/Vomitblood/cspj-application/server/internal/db"
)
2025-01-14 03:18:08 +08:00
// secure register endpoint
func UnsecureRegisterSql(w http.ResponseWriter, r *http.Request) {
2024-11-11 17:34:37 +08:00
// read the request body
var credentials struct {
2025-01-14 03:18:08 +08:00
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()
2025-01-14 03:18:08 +08:00
// 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
}
2025-01-14 03:18:08 +08:00
// check if the password matches, if not reject
if credentials.Password != credentials.RePassword {
http.Error(w, "Passwords do not match", http.StatusBadRequest)
return
2024-11-11 17:34:37 +08:00
}
2025-01-14 03:18:08 +08:00
// 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)
2024-11-11 17:34:37 +08:00
return
}
2025-01-14 03:18:08 +08:00
// if there is more than 0 matches, that means email already exists, reject
if existingUserCount > 0 {
http.Error(w, "Email already exists", http.StatusConflict)
2024-11-11 17:34:37 +08:00
return
}
2025-01-14 03:18:08 +08:00
// over here the validations has passed, so insert into the db
// also use concatenation here
2025-01-14 04:12:55 +08:00
insertSQL := fmt.Sprintf("INSERT INTO users (email, password, role) VALUES ('%s', '%s', 'user')", credentials.Email, credentials.Password)
2025-01-14 03:18:08 +08:00
_, err = db.DbPool.Exec(context.Background(), insertSQL)
2024-11-11 17:34:37 +08:00
if err != nil {
2025-01-14 03:18:08 +08:00
http.Error(w, "Error inserting user into the database", http.StatusInternalServerError)
log.Printf("Error inserting user: %v", err)
2024-11-11 17:34:37 +08:00
return
}
2025-01-14 03:18:08 +08:00
// send back status ok
w.WriteHeader(http.StatusOK)
w.Write([]byte("User registered successfully"))
log.Println("User registered successfully:", credentials.Email)
2024-11-11 17:34:37 +08:00
}
2025-01-14 03:18:08 +08:00
// secure register endpoint
func SecureRegisterSql(w http.ResponseWriter, r *http.Request) {
2025-01-14 02:39:24 +08:00
// 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
2025-01-14 03:18:08 +08:00
// use parameterization
2025-01-14 02:39:24 +08:00
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
2025-01-14 03:18:08 +08:00
// use parameterization
2025-01-14 02:39:24 +08:00
insertSQL := `INSERT INTO users (email, password, role) VALUES ($1, $2, $3)`
2025-01-14 04:12:55 +08:00
_, err = db.DbPool.Exec(context.Background(), insertSQL, credentials.Email, credentials.Password, "user")
2025-01-14 02:39:24 +08:00
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)
}
2025-01-14 03:18:08 +08:00
func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) {
// decode the json body
var requestData struct {
Email string `json:"email"`
Password string `json:"password"`
}
2025-01-14 04:12:55 +08:00
// decode the request body
2025-01-14 03:18:08 +08:00
if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil {
http.Error(w, "Invalid request format", http.StatusBadRequest)
2025-01-14 04:12:55 +08:00
log.Println("Failed to decode JSON body:", err)
2025-01-14 03:18:08 +08:00
return
}
2025-01-14 04:12:55 +08:00
// 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
2025-01-14 03:18:08 +08:00
var email string
2025-01-14 04:12:55 +08:00
var password string
2025-01-14 04:12:55 +08:00
// execute the query to retrieve the data
err := db.DbPool.QueryRow(context.Background(), query).Scan(&id, &email, &password)
2025-01-14 03:18:08 +08:00
if err != nil {
http.Error(w, "Invalid email or password", http.StatusUnauthorized)
2025-01-14 04:12:55 +08:00
log.Println("Error during query execution:", err)
2025-01-14 03:18:08 +08:00
return
}
2025-01-14 04:12:55 +08:00
// if the email and password match then send back the user data
response := map[string]interface{}{
2025-01-14 03:18:08 +08:00
"id": id,
"email": email,
}
if err := json.NewEncoder(w).Encode(response); err != nil {
2025-01-14 03:18:08 +08:00
http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError)
log.Printf("JSON encoding error: %v", err)
}
}
2025-01-14 03:18:08 +08:00
// very secure login endpoint
func SecureLoginSql(w http.ResponseWriter, r *http.Request) {
2024-11-11 18:47:15 +08:00
// decode the json body
var requestData struct {
2025-01-14 03:18:08 +08:00
Email string `json:"email"`
Password string `json:"password"`
2024-11-11 18:47:15 +08:00
}
// 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
2025-01-14 03:18:08 +08:00
if requestData.Email == "" || requestData.Password == "" {
2024-11-11 18:47:15 +08:00
http.Error(w, "Invalid request format", http.StatusBadRequest)
return
}
2025-01-14 03:18:08 +08:00
// retrieve list of existing emails from db
existingEmails, err := db.FetchEmails()
2024-11-11 17:34:37 +08:00
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
2025-01-14 03:18:08 +08:00
log.Printf("Failed to fetch emails: %v", err)
2024-11-11 17:34:37 +08:00
return
}
2025-01-14 03:18:08 +08:00
// check if the email exists in the allowed list
2024-11-11 17:34:37 +08:00
// this step is crucial
// server will reject ANYTHING that does not match the list
2025-01-14 03:18:08 +08:00
if !existingEmails[requestData.Email] {
http.Error(w, "Invalid email or password", http.StatusBadRequest)
2024-11-11 17:34:37 +08:00
return
}
2025-01-14 03:18:08 +08:00
// retrieve the email and password from the database
2024-11-11 17:34:37 +08:00
var id int
2025-01-14 03:18:08 +08:00
var email string
2025-01-14 04:12:55 +08:00
var storedPassword string
2025-01-14 03:18:08 +08:00
query := "SELECT id, email, password FROM users WHERE email = $1"
2025-01-14 04:12:55 +08:00
err = db.DbPool.QueryRow(context.Background(), query, requestData.Email).Scan(&id, &email, &storedPassword)
2025-01-14 03:18:08 +08:00
if err != nil {
http.Error(w, "Invalid email or password", http.StatusNotFound)
return
}
2025-01-14 04:12:55 +08:00
// compare the provided password with the stored plain-text password
if storedPassword != requestData.Password {
2025-01-14 03:18:08 +08:00
http.Error(w, "Invalid email or password", http.StatusUnauthorized)
2024-11-11 17:34:37 +08:00
return
}
// send back the user data as a json response
response := map[string]interface{}{
2025-01-14 03:18:08 +08:00
"id": id,
"email": email,
2024-11-11 17:34:37 +08:00
}
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)
}
}