Cookie-Autorisierung Golang
Aug 24 2020
Ich schreibe ein Anmeldesystem in Go (Golang) mit Cookies. Ich denke, es ist nicht sicher genug. Können Sie einige Vorschläge zur Verbesserung der Sicherheit machen?
Hauptdatei:
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
"html/template"
"math/rand"
"net/http"
"strings"
"time"
)
var (
runes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
t *template.Template
)
func init() {
rand.Seed(time.Now().Unix())
t, _ = template.ParseFiles("main.html","signup.html","signin.html")
}
type User struct {
Login, Email string
}
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) {
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()
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)
}
Datenbankdatei:
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 session_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
}
Datenbankbenutzertabelle:
+----------+--------------+------+-----+---------+----------------+
| 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 | |
+----------+--------------+------+-----+---------+----------------+
Datenbanksitzungstabelle:
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| login | varchar(255) | YES | | NULL | |
| token | varchar(15) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
Antworten
2 Ludisposed Aug 27 2020 at 13:17
Verwenden Sie niemals zufällige PSEUDO-Werte für Geheimnisse!
Dies kann von einem Angreifer ausgenutzt werden, indem der Startwert erraten wird. In diesem Fall:
rand.Seed(time.Now().Unix())
Und dann einen möglichen Cookie-Wert für die Anmeldung generieren.
Verwenden Sie stattdessen kryptografisch sichere Implementierungen, die Sie in Golang verwenden sollten crypto/rand