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