added sql injection simulation for authentication on server
This commit is contained in:
		
							parent
							
								
									7232aed4ef
								
							
						
					
					
						commit
						0afbdd97d9
					
				| 
						 | 
					@ -33,6 +33,7 @@ PGPASSWORD=asdfpassword
 | 
				
			||||||
 | 
					
 | 
				
			||||||
!only listening on localhost is supported. DO NOT run this on a public ip.  
 | 
					!only listening on localhost is supported. DO NOT run this on a public ip.  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `/health`
 | 
				
			||||||
- `/setup-demo-db`
 | 
					- `/setup-demo-db`
 | 
				
			||||||
- `/nuke-db`
 | 
					- `/nuke-db`
 | 
				
			||||||
- `/fetch-all-users`
 | 
					- `/fetch-all-users`
 | 
				
			||||||
| 
						 | 
					@ -40,7 +41,9 @@ PGPASSWORD=asdfpassword
 | 
				
			||||||
### SQL Injection
 | 
					### SQL Injection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `/sql-execute`
 | 
					- `/sql-execute`
 | 
				
			||||||
 | 
					- `/login-sql`
 | 
				
			||||||
- `/secure-sql-execute`
 | 
					- `/secure-sql-execute`
 | 
				
			||||||
 | 
					- `/secure-login-sql`
 | 
				
			||||||
- `/secure-get-user`
 | 
					- `/secure-get-user`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### 1. Parameterization of Queries
 | 
					#### 1. Parameterization of Queries
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,20 +45,20 @@ func ConnectToDb() (*pgxpool.Pool, error) {
 | 
				
			||||||
func SetupDemoDb(w http.ResponseWriter, r *http.Request) {
 | 
					func SetupDemoDb(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	// create table and insert demo data
 | 
						// create table and insert demo data
 | 
				
			||||||
	createTableSQL := `
 | 
						createTableSQL := `
 | 
				
			||||||
    CREATE TABLE IF NOT EXISTS users (
 | 
						CREATE TABLE IF NOT EXISTS users (
 | 
				
			||||||
        id SERIAL PRIMARY KEY,
 | 
						id SERIAL PRIMARY KEY,
 | 
				
			||||||
        username VARCHAR(50) UNIQUE NOT NULL,
 | 
						username VARCHAR(50) UNIQUE NOT NULL,
 | 
				
			||||||
        email VARCHAR(100) NOT NULL,
 | 
						email VARCHAR(100) NOT NULL,
 | 
				
			||||||
        password VARCHAR(100) NOT NULL
 | 
						password VARCHAR(100) NOT NULL
 | 
				
			||||||
    );`
 | 
					);`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// also avoid duplicate entries
 | 
						// also avoid duplicate entries
 | 
				
			||||||
	insertDataSQL := `
 | 
						insertDataSQL := `
 | 
				
			||||||
    INSERT INTO users (username, email, password) VALUES
 | 
						INSERT INTO users (username, email, password) VALUES
 | 
				
			||||||
        ('alice', 'alice@example.com', 'asdfalicepassword'),
 | 
						('alice', 'alice@example.com', 'asdfalicepassword'),
 | 
				
			||||||
        ('bob', 'bob@example.com', 'asdfbobpassword'),
 | 
						('bob', 'bob@example.com', 'asdfbobpassword'),
 | 
				
			||||||
        ('charlie', 'charlie@example.com', 'asdfcharliepassword')
 | 
						('charlie', 'charlie@example.com', 'asdfcharliepassword')
 | 
				
			||||||
    ON CONFLICT (username) DO NOTHING;`
 | 
						ON CONFLICT (username) DO NOTHING;`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// execute create table
 | 
						// execute create table
 | 
				
			||||||
	_, err := DbPool.Exec(context.Background(), createTableSQL)
 | 
						_, err := DbPool.Exec(context.Background(), createTableSQL)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,9 @@ func ServeApi() {
 | 
				
			||||||
	http.HandleFunc("/nuke-db", db.NukeDb)
 | 
						http.HandleFunc("/nuke-db", db.NukeDb)
 | 
				
			||||||
	http.HandleFunc("/fetch-all-users", db.FetchAllUsers)
 | 
						http.HandleFunc("/fetch-all-users", db.FetchAllUsers)
 | 
				
			||||||
	http.HandleFunc("/execute-sql", sql_injection.ExecuteSql)
 | 
						http.HandleFunc("/execute-sql", sql_injection.ExecuteSql)
 | 
				
			||||||
 | 
						http.HandleFunc("/login-sql", sql_injection.LoginSql)
 | 
				
			||||||
	http.HandleFunc("/secure-execute-sql", sql_injection.SecureExecuteSql)
 | 
						http.HandleFunc("/secure-execute-sql", sql_injection.SecureExecuteSql)
 | 
				
			||||||
 | 
						http.HandleFunc("/secure-login-sql", sql_injection.SecureLoginSql)
 | 
				
			||||||
	http.HandleFunc("/secure-get-user", sql_injection.SecureGetUser)
 | 
						http.HandleFunc("/secure-get-user", sql_injection.SecureGetUser)
 | 
				
			||||||
	log.Println("Server is running on http://localhost:5000")
 | 
						log.Println("Server is running on http://localhost:5000")
 | 
				
			||||||
	if err := http.ListenAndServe(":5000", nil); err != nil {
 | 
						if err := http.ListenAndServe(":5000", nil); err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +46,50 @@ func ExecuteSql(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	w.Write([]byte(response))
 | 
						w.Write([]byte(response))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// unsecure login
 | 
				
			||||||
 | 
					// login endpoint with sql injection vulnerability
 | 
				
			||||||
 | 
					func LoginSql(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						// parse the request body to get username and password
 | 
				
			||||||
 | 
						var credentials struct {
 | 
				
			||||||
 | 
							Username string `json:"username"`
 | 
				
			||||||
 | 
							Password string `json:"password"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// decode the json body
 | 
				
			||||||
 | 
						if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "Invalid request format", http.StatusBadRequest)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer r.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// construct the unsafe query
 | 
				
			||||||
 | 
						query := fmt.Sprintf(
 | 
				
			||||||
 | 
							"SELECT id, username FROM users WHERE username = '%s' AND password = '&s'",
 | 
				
			||||||
 | 
							credentials.Username,
 | 
				
			||||||
 | 
							credentials.Password,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// execute the query without sanitizing the input
 | 
				
			||||||
 | 
						var id int
 | 
				
			||||||
 | 
						var username string
 | 
				
			||||||
 | 
						err := db.DbPool.QueryRow(context.Background(), query).Scan(&id, &username)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "Invalid credentials", http.StatusUnauthorized)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if the user is found, return success response
 | 
				
			||||||
 | 
						response := map[string]interface{}{
 | 
				
			||||||
 | 
							"message":  "Login successful",
 | 
				
			||||||
 | 
							"user_id":  id,
 | 
				
			||||||
 | 
							"username": username,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := json.NewEncoder(w).Encode(response); err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "Failed to encode response", http.StatusInternalServerError)
 | 
				
			||||||
 | 
							log.Printf("JSON encoding error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// secure version
 | 
					// secure version
 | 
				
			||||||
// only allow parameterized queries with validation
 | 
					// only allow parameterized queries with validation
 | 
				
			||||||
func SecureExecuteSql(w http.ResponseWriter, r *http.Request) {
 | 
					func SecureExecuteSql(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
| 
						 | 
					@ -95,7 +139,7 @@ func SecureExecuteSql(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	// return json response
 | 
						// return json response
 | 
				
			||||||
	jsonResp, err := json.Marshal(response)
 | 
						jsonResp, err := json.Marshal(response)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError)
 | 
							http.Error(w, "Failed to encode response", http.StatusInternalServerError)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +147,41 @@ func SecureExecuteSql(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	w.Write(jsonResp)
 | 
						w.Write(jsonResp)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// secure login
 | 
				
			||||||
 | 
					func SecureLoginSql(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
						var credentials struct {
 | 
				
			||||||
 | 
							Username string `json:"username"`
 | 
				
			||||||
 | 
							Password string `json:"password"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "Invalid request format", http.StatusBadRequest)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer r.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// secure version using parameterized queries
 | 
				
			||||||
 | 
						query := "SELECT id, username FROM users WHERE username = $1 AND password = $2"
 | 
				
			||||||
 | 
						var id int
 | 
				
			||||||
 | 
						var username string
 | 
				
			||||||
 | 
						err := db.DbPool.QueryRow(context.Background(), query, credentials.Username, credentials.Password).Scan(&id, &username)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "Invalid credentials", http.StatusUnauthorized)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// send back the response if great success
 | 
				
			||||||
 | 
						response := map[string]interface{}{
 | 
				
			||||||
 | 
							"message":  "Login successful",
 | 
				
			||||||
 | 
							"user_id":  id,
 | 
				
			||||||
 | 
							"username": username,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := json.NewEncoder(w).Encode(response); err != nil {
 | 
				
			||||||
 | 
							http.Error(w, "Failed to encode response", http.StatusInternalServerError)
 | 
				
			||||||
 | 
							log.Printf("JSON encoding error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// even more secure
 | 
					// even more secure
 | 
				
			||||||
func SecureGetUser(w http.ResponseWriter, r *http.Request) {
 | 
					func SecureGetUser(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	// decode the json body
 | 
						// decode the json body
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue