go – Cookie authorization Golang


I’m writing login system in Go(Golang) using cookies.I think it’s isn’t safe enough. Can you provide some suggestions on how to improve the security.
Main file:

package main

import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
    "html/template"
    "math/rand"
    "net/http"
    "strings"
    "time"
)

func init()  {
    rand.Seed(time.Now().Unix())
}

type User struct {
    Login, Email string
}

var (
    runes  = ()rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
)

func genToken() string {
    s := make(()rune, 15)
    for i := range s {
        s(i) = runes(rand.Intn(len(runes)))
    }
    return string(s)
}


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) {
    if r.Method == "GET"{
        t, err := template.ParseFiles("signup.html")
        if err != nil{
            http.Error(w,"Internal server error",500)
        }
        t.Execute(w,nil)
    } else if r.Method == "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) {

    if r.Method == "GET"{
        t, err := template.ParseFiles("signin.html")
        if err != nil{
            http.Error(w,"Internal server error",http.StatusInternalServerError)
        }

        t.Execute(w,nil)
    } else if r.Method == "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()
            setCookie(w,"login",data("login")(0),expiresAfter)
            setCookie(w,"session_token",token,expiresAfter)
            DB.setToken(token,data("login")(0))
            http.Redirect(w,r,"/",http.StatusSeeOther)
        }
    }
}

func mainPage(w http.ResponseWriter, r *http.Request) {
    login := getCookie(r,"login")
    if !DB.checkLogin(login){

    }
    token := getCookie(r,"session_token")
    if !DB.checkToken(login,token){
        http.Redirect(w,r,"/login",http.StatusSeeOther)
    }
    t, err := template.ParseFiles("main.html")
    if err != nil{
        http.Error(w,"Internal server error",http.StatusInternalServerError)
    }
    user := DB.getUser(login)
    t.Execute(w, user)
}

func logout(w http.ResponseWriter,r *http.Request){
    login := getCookie(r,"login")
    deleteCookie(w,"login")
    deleteCookie(w,"token")
    DB.setToken("",login)
    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@/users")

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) setToken(token, login string) {
    db.DB.Exec("UPDATE users SET token = ? WHERE login = ?",token,login)
}

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 users WHERE login = ?",login)
    var dbtoken string
    if rows.Next(){
        rows.Scan(&dbtoken)
        if token == dbtoken{
            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
}