4
\$\begingroup\$

I still write login system in Go(Golang) using cookies.But my system is still not secure enough.Can you review my code and provide some suggestions on how to improve the security?Previous question.

Main file:

package main
import (
 "crypto/rand"
 "encoding/base64"
 "fmt"
 "golang.org/x/crypto/bcrypt"
 "html/template"
 "log"
 "net/http"
 "strings"
 "time"
)
var (
 t *template.Template
)
func init() {
 t, _ = template.ParseFiles("main.html","signup.html","signin.html")
}
type User struct {
 Login, Email string
}
func genToken(n int) string {
 b := make([]byte, n)
 if _, err := rand.Read(b);err != nil{
 log.Fatal(err)
 }
 return base64.URLEncoding.EncodeToString(b)
}
func setCookie(w http.ResponseWriter, name, value string,d int) {
 cookie := http.Cookie{
 Name: name,
 Value: value,
 }
 if d != 0{
 expires := time.Now().AddDate(0,0,d)
 cookie.Expires = expires
 }
 http.SetCookie(w, &cookie)
}
func getCookie(r *http.Request, name string) string {
 c, err := r.Cookie(name)
 if err != nil {
 return ""
 }
 return c.Value
}
func deleteCookie(w http.ResponseWriter,name string){
 cookie := http.Cookie{
 Name: name,
 MaxAge: -1,
 }
 http.SetCookie(w, &cookie)
}
func signup(w http.ResponseWriter, r *http.Request) {
 switch r.Method {
 case "GET":
 t.ExecuteTemplate(w,"signup.html",nil)
 case "POST":
 r.ParseForm()
 data := r.Form
 var error string
 if data["login"][0] == ""{
 error = "Login can't be empty"
 } else if data["email"][0] == ""{
 error = "Email can't be empty"
 } else if data["password"][0] == ""{
 error = "Password cant't be empty"
 } else if len(data["login"][0]) < 4{
 error = "Login must be at least 4 characters"
 } else if DB.checkLogin(data["login"][0]){
 error = "User with such login already exists"
 } else if !strings.ContainsRune(data["email"][0],'@'){
 error = "Email must contain @"
 } else if DB.checkEmail(data["email"][0]) {
 error = "User with such email already exists"
 } else if len(data["password"][0]) < 8{
 error = "Password must be at least 8 characters"
 } else if data["password2"][0] != data["password"][0]{
 error = "Passwords don't match"
 }
 if error != ""{
 values :=&User{}
 values.Login = data["login"][0]
 values.Email = data["email"][0]
 t, err := template.ParseFiles("signup.html")
 if err != nil{
 http.Error(w,"Internal server error",500)
 }
 t.Execute(w,values)
 fmt.Fprintln(w,"<hr><span style='color:red;'>" + error + "</span>")
 } else {
 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(data["password"][0]),10)
 if err != nil{
 http.Error(w,"Internal server error",500)
 }
 DB.newUser(data["login"][0],data["email"][0],string(hashedPassword))
 http.Redirect(w,r,"/login",http.StatusSeeOther)
 }
 }
}
func signin(w http.ResponseWriter, r *http.Request) {
 switch r.Method {
 case "GET":
 t.ExecuteTemplate(w,"signin.html",nil)
 case "POST":
 r.ParseForm()
 data := r.Form
 var error string
 if !DB.checkLogin(data["login"][0]){
 error = "User with such login doesn't exists"
 } else {
 if !DB.checkPassword(data["login"][0],data["password"][0]){
 error = "Wrong password"
 }
 }
 if error != ""{
 values :=&User{}
 values.Login = data["login"][0]
 t, err := template.ParseFiles("signin.html")
 if err != nil{
 http.Error(w,"Internal server error",http.StatusInternalServerError)
 }
 t.Execute(w,values)
 fmt.Fprintln(w,"<hr><span style='color:red;'>" + error + "</span>")
 } else {
 expiresAfter := 0
 if r.FormValue("remember") == "1"{
 expiresAfter = 30
 }
 token := genToken(32)
 setCookie(w,"login",data["login"][0],expiresAfter)
 setCookie(w,"session_token",token,expiresAfter)
 DB.newSession(data["login"][0],token)
 http.Redirect(w,r,"/",http.StatusSeeOther)
 }
 }
}
func mainPage(w http.ResponseWriter, r *http.Request) {
 login := getCookie(r,"login")
 token := getCookie(r,"session_token")
 if !DB.checkToken(login,token){
 http.Redirect(w,r,"/login",http.StatusSeeOther)
 }
 user := DB.getUser(login)
 t.ExecuteTemplate(w,"main.html",user)
}
func logout(w http.ResponseWriter,r *http.Request){
 login := getCookie(r,"login")
 token := getCookie(r,"session_token")
 deleteCookie(w,"login")
 deleteCookie(w,"session_token")
 DB.deleteSession(login,token)
 http.Redirect(w,r,"/login",http.StatusSeeOther)
}
func main() {
 http.HandleFunc("/register", signup)
 http.HandleFunc("/login", signin)
 http.HandleFunc("/", mainPage)
 http.HandleFunc("/logout",logout)
 http.ListenAndServe(":8080", nil)
}

Database file:

package main
import (
 "database/sql"
 "golang.org/x/crypto/bcrypt"
 "log"
 _ "github.com/go-sql-driver/mysql"
)
var DB = newDB("root:root574@/signin")
type db struct {
 DB *sql.DB
}
func newDB(name string) *db {
 conn, err := sql.Open("mysql", name)
 if err != nil {
 log.Fatal(err)
 }
 if err = conn.Ping(); err != nil {
 log.Fatal(err)
 }
 return &db{DB: conn}
}
func (db db) newUser(login, email, password string) {
 db.DB.Exec("INSERT INTO users(login,email,password) VALUES (?,?,?)", login, email, password)
}
func (db db) newSession(login, token string) {
 db.DB.Exec("INSERT INTO sessions(login,token) VALUES (?,?)",login,token)
}
func (db db) deleteSession(login, token string) {
 db.DB.Exec("DELETE FROM sessions WHERE login = ? and token = ?",login,token)
}
func (db db) checkLogin(login string) bool {
 var rows, _ = db.DB.Query("SELECT id FROM users WHERE login = ?", login)
 if rows.Next() {
 return true
 }
 rows.Close()
 return false
}
func (db db) checkEmail(email string) bool {
 var rows, _ = db.DB.Query("SELECT id FROM users WHERE email = ?", email)
 if rows.Next() {
 return true
 }
 rows.Close()
 return false
}
func (db db) checkPassword(login, password string) bool{
 var rows, _ = db.DB.Query("SELECT password FROM users WHERE login = ?", login)
 var dbpassword string
 rows.Next()
 rows.Scan(&dbpassword)
 rows.Close()
 if bcrypt.CompareHashAndPassword([]byte(dbpassword),[]byte(password)) != nil{
 return false
 }
 return true
}
func (db db) checkToken(login, token string) bool {
 var rows, _ = db.DB.Query("SELECT token FROM sessions WHERE login = ? and token = ?",login,token)
 if rows.Next(){
 return true
 }
 rows.Close()
 return false
}
func (db db) getUser(login string) *User {
 var rows, _ = db.DB.Query("select email FROM users WHERE login = ?",login)
 user := &User{}
 rows.Next()
 rows.Scan(&user.Email)
 rows.Close()
 user.Login = login
 return user
}

Database users table:

+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| login | varchar(255) | YES | | NULL | |
| email | varchar(255) | YES | | NULL | |
| password | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+

Database sessions table:

+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| login | varchar(255) | YES | | NULL | |
| token | varchar(44) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
asked Aug 28, 2020 at 9:31
\$\endgroup\$
4
  • 1
    \$\begingroup\$ What are your concerns? I also don't quite understand what is "cookie-based" about your auth, looks like vanilla session-based auth to me. \$\endgroup\$ Commented Sep 12, 2020 at 7:36
  • \$\begingroup\$ @D.SM I think it looks like this,isn't it?.Can you provide some suggestion on how to improve the security? \$\endgroup\$ Commented Sep 13, 2020 at 7:57
  • \$\begingroup\$ Cookies are used to implement sessions. Your auth uses sessions. You didn't say what your concerns were. \$\endgroup\$ Commented Sep 14, 2020 at 9:05
  • \$\begingroup\$ @D.SM I'm concerned about security \$\endgroup\$ Commented Sep 14, 2020 at 12:08

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.