├── README.md ├── api └── api.go ├── go.mod ├── go.sum ├── helpers └── helpers.go ├── main.go ├── migrations └── migrations.go └── vulnerableDB └── vulnerable-db.go /README.md: -------------------------------------------------------------------------------- 1 | # Golang banking app 2 | 3 | This is app for the Duomly Golang Course - Learn Golang by building banking App 4 | 5 | ## Run 6 | 7 | - Setup a PostgreSQL db 8 | - You need to setup your connection string in the two files, vulnerable-db.go, and migrations.go 9 | - Start migration by commenting migration in main.go and commenting API 10 | - Type (that will migrate to your db): 11 | ``` 12 | go run main.go 13 | ``` 14 | - Comment migration and uncomment api 15 | - Type: 16 | ``` 17 | go run main.go 18 | ``` -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | 10 | "duomly.com/go-bank-backend/helpers" 11 | "duomly.com/go-bank-backend/vulnerableDB" 12 | "github.com/gorilla/mux" 13 | ) 14 | 15 | type Login struct { 16 | Username string 17 | Password string 18 | } 19 | 20 | type Response struct { 21 | Data []vulnerableDB.User 22 | } 23 | 24 | type ErrResponse struct { 25 | Message string 26 | } 27 | 28 | func login(w http.ResponseWriter, r *http.Request) { 29 | body, err := ioutil.ReadAll(r.Body) 30 | helpers.HandleErr(err) 31 | 32 | var formattedBody Login 33 | err = json.Unmarshal(body, &formattedBody) 34 | helpers.HandleErr(err) 35 | login := vulnerableDB.VulnerableLogin(formattedBody.Username, formattedBody.Password) 36 | 37 | if len(login) > 0 { 38 | resp := Response{Data: login} 39 | json.NewEncoder(w).Encode(resp) 40 | } else { 41 | resp := ErrResponse{Message: "Wrong username or password"} 42 | json.NewEncoder(w).Encode(resp) 43 | } 44 | } 45 | 46 | func StartApi() { 47 | router := mux.NewRouter() 48 | router.HandleFunc("/login", login).Methods("POST") 49 | fmt.Println("App is working on port :8888") 50 | log.Fatal(http.ListenAndServe(":8888", router)) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module duomly.com/go-bank-backend 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gorilla/mux v1.7.4 7 | github.com/jinzhu/gorm v1.9.12 8 | github.com/lib/pq v1.1.1 9 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 2 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 3 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 4 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 5 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 6 | github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= 7 | github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 8 | github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= 9 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 10 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 11 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 12 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 13 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 14 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 15 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 16 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 17 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 18 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= 19 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 20 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 21 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 22 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 23 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 24 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 25 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 26 | -------------------------------------------------------------------------------- /helpers/helpers.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | 7 | "golang.org/x/crypto/bcrypt" 8 | ) 9 | 10 | 11 | func HandleErr(err error) { 12 | if err != nil { 13 | panic(err.Error()) 14 | } 15 | } 16 | 17 | func HashAndSalt(pass []byte) string { 18 | hashed, err := bcrypt.GenerateFromPassword(pass, bcrypt.MinCost) 19 | HandleErr(err) 20 | 21 | return string(hashed) 22 | } 23 | 24 | func HashOnlyVulnerable(pass []byte) string { 25 | hash := md5.New() 26 | hash.Write(pass) 27 | return hex.EncodeToString(hash.Sum(nil)) 28 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "duomly.com/go-bank-backend/api" 4 | 5 | func main() { 6 | // migrations.Migrate() 7 | api.StartApi() 8 | } 9 | -------------------------------------------------------------------------------- /migrations/migrations.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "duomly.com/go-bank-backend/helpers" 5 | "github.com/jinzhu/gorm" 6 | _ "github.com/jinzhu/gorm/dialects/postgres" 7 | ) 8 | 9 | type User struct { 10 | gorm.Model 11 | Username string 12 | Email string 13 | Password string 14 | } 15 | 16 | type Account struct { 17 | gorm.Model 18 | Type string 19 | Name string 20 | Balance uint 21 | UserID uint 22 | } 23 | 24 | func connectDB() *gorm.DB { 25 | db, err := gorm.Open("postgres", "host=127.0.0.1 port=5432 user=user dbname=dbname password=password sslmode=disable") 26 | helpers.HandleErr(err) 27 | return db 28 | } 29 | 30 | // This is correct way of creating password 31 | // func HashAndSalt(pass []byte) string { 32 | // hashed, err := bcrypt.GenerateFromPassword(pass, bcrypt.MinCost) 33 | // helpers.HandleErr(err) 34 | 35 | // return string(hashed) 36 | // } 37 | 38 | func createAccounts() { 39 | db := connectDB() 40 | 41 | users := [2]User{ 42 | {Username: "Martin", Email: "martin@martin.com"}, 43 | {Username: "Michael", Email: "michael@michael.com"}, 44 | } 45 | 46 | for i := 0; i < len(users); i++ { 47 | // Correct one way 48 | // generatedPassword := helpers.HashAndSalt([]byte(users[i].Username)) 49 | generatedPassword := helpers.HashOnlyVulnerable([]byte(users[i].Username)) 50 | user := User{Username: users[i].Username, Email: users[i].Email, Password: generatedPassword} 51 | db.Create(&user) 52 | 53 | account := Account{Type: "Daily Account", Name: string(users[i].Username + "'s" + " account"), Balance: uint(10000 * int(i+1)), UserID: user.ID} 54 | db.Create(&account) 55 | } 56 | defer db.Close() 57 | } 58 | 59 | 60 | func Migrate() { 61 | db := connectDB() 62 | db.AutoMigrate(&User{}, &Account{}) 63 | defer db.Close() 64 | 65 | createAccounts() 66 | } -------------------------------------------------------------------------------- /vulnerableDB/vulnerable-db.go: -------------------------------------------------------------------------------- 1 | package vulnerableDB 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | "duomly.com/go-bank-backend/helpers" 8 | _ "github.com/lib/pq" 9 | ) 10 | 11 | type User struct { 12 | ID int 13 | Username string 14 | Email string 15 | Accounts []Account 16 | } 17 | 18 | type Account struct { 19 | ID int 20 | Name string 21 | Balance int 22 | } 23 | 24 | func connectDB() *sql.DB { 25 | db, err := sql.Open("postgres", "host=127.0.0.1 port=5432 user=user dbname=dbname password=password sslmode=disable") 26 | helpers.HandleErr(err) 27 | return db 28 | } 29 | 30 | func dbCall(query string) *sql.Rows { 31 | db := connectDB() 32 | 33 | call, err := db.Query(query) 34 | 35 | helpers.HandleErr(err) 36 | return call 37 | } 38 | 39 | // func correctAuthUser(accPass string, typedPass string) bool { 40 | // accPassByte := []byte(accPass) 41 | // typedPassByte := []byte(typedPass) 42 | // err := bcrypt.CompareHashAndPassword(accPassByte, typedPassByte) 43 | // if err != nil { 44 | // return false 45 | // } 46 | // return true 47 | // } 48 | 49 | func VulnerableLogin(username string, pass string) []User { 50 | // This is a very vulnerable way of doing it, you should always hash and salt, you should compare them with CompareHashAndPassword as well 51 | password := helpers.HashOnlyVulnerable([]byte(pass)) 52 | results := dbCall("SELECT id, username, email FROM users x WHERE username='" + username + "' AND password='" + password + "'") 53 | var users []User 54 | 55 | for results.Next() { 56 | var user User 57 | err := results.Scan(&user.ID, &user.Username, &user.Email) 58 | helpers.HandleErr(err) 59 | accounts := dbCall("SELECT id, name, balance FROM accounts x WHERE user_id=" + fmt.Sprint(user.ID) + "") 60 | var userAccounts []Account 61 | 62 | for accounts.Next() { 63 | var account Account 64 | err := accounts.Scan(&account.ID, &account.Name, &account.Balance) 65 | helpers.HandleErr(err) 66 | userAccounts = append(userAccounts, account) 67 | } 68 | 69 | user.Accounts = userAccounts 70 | users = append(users, user) 71 | } 72 | defer results.Close() 73 | return users 74 | } --------------------------------------------------------------------------------