Made memory-based sessions work correctly, add a proof-of-work captcha to deter spamming

This commit is contained in:
Tracker-Friendly 2024-07-20 16:45:21 +01:00
parent 55bec8e5f8
commit 0a22d2fd39
4 changed files with 58 additions and 17 deletions

1
go.mod
View File

@ -3,6 +3,7 @@ module hectabit.org/burgernotes
go 1.22
require (
github.com/catalinc/hashcash v1.0.0
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
github.com/mattn/go-sqlite3 v1.14.22

2
go.sum
View File

@ -2,6 +2,8 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/catalinc/hashcash v1.0.0 h1:DiI2kBNCczy7y3xJnLddIl7KGx0yP4B7irFZZ+yzzwc=
github.com/catalinc/hashcash v1.0.0/go.mod h1:ldWL6buwYCK4VqIkLbZuFbGUoJceSafm8duCEQYw9Jw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=

63
main.go
View File

@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/catalinc/hashcash"
"log"
"os"
"regexp"
@ -24,6 +25,7 @@ import (
var (
conn *sql.DB
mem *sql.DB
host string
port int
secretKey string
@ -133,7 +135,7 @@ func checkUsernameTaken(username string) (int, bool, error) {
func getSession(session string) (int, int, error) {
var id int
var sessionId int
err := conn.QueryRow("SELECT sessionid, id FROM sessions WHERE session = ? LIMIT 1", session).Scan(&sessionId, &id)
err := mem.QueryRow("SELECT sessionid, id FROM sessions WHERE session = ? LIMIT 1", session).Scan(&sessionId, &id)
if err != nil {
return 0, 0, err
}
@ -143,7 +145,7 @@ func getSession(session string) (int, int, error) {
func getSessionFromId(sessionId int) (string, int, error) {
var id int
var session string
err := conn.QueryRow("SELECT session, id FROM sessions WHERE sessionid = ? LIMIT 1", sessionId).Scan(&session, &id)
err := mem.QueryRow("SELECT session, id FROM sessions WHERE sessionid = ? LIMIT 1", sessionId).Scan(&session, &id)
if err != nil {
return "", 0, err
}
@ -208,11 +210,22 @@ func migrateDb() {
if strings.ToLower(answer) == "y" {
_, err = conn.Exec("ALTER TABLE users DROP COLUMN versionTwoLegacyPassword")
if err != nil {
log.Println("[WARN] Unknown while migrating database (1/2):", err)
log.Println("[WARN] Unknown while migrating database (1/3):", err)
log.Println("[INFO] This is likely because your database is already migrated. This is not a problem, and Burgernotes does not need this removed - it is just for cleanup")
return
}
_, err = conn.Exec("CREATE TABLE oauth (id INTEGER NOT NULL, oauthProvider TEXT NOT NULL, encryptedPasswd TEXT NOT NULL)")
if err != nil {
log.Println("[WARN] Unknown while migrating database (2/3):", err)
log.Println("[INFO] This is likely because your database is already migrated. This is not a problem, but if it is not, it may cause issues with OAuth2")
return
}
_, err = conn.Exec("DROP TABLE sessions")
if err != nil {
log.Println("[WARN] Unknown while migrating database (3/3):", err)
log.Println("[INFO] This is likely because your database is already migrated. This is not a problem, and Burgernotes does not need this removed - it is just for cleanup")
return
}
} else if answer == ":3" {
log.Println("[:3] :3")
} else {
@ -269,10 +282,30 @@ func main() {
defer func(conn *sql.DB) {
err := conn.Close()
if err != nil {
log.Println("[ERROR] Unknown in main() defer:", err)
log.Println("[ERROR] Unknown in main() conn defer:", err)
}
}(conn)
mem, err = sql.Open("sqlite3", ":memory: cache=shared")
if err != nil {
log.Fatalln("[FATAL] Cannot open session database:", err)
}
defer func(mem *sql.DB) {
err := mem.Close()
if err != nil {
log.Println("[ERROR] Unknown in main() mem defer:", err)
}
}(mem)
_, err = mem.Exec("CREATE TABLE sessions (sessionid INTEGER PRIMARY KEY AUTOINCREMENT, session TEXT NOT NULL, id INTEGER NOT NULL, device TEXT NOT NULL DEFAULT '?')")
if err != nil {
if err.Error() == "table sessions already exists" {
log.Println("[INFO] Session table already exists")
} else {
log.Fatalln("[FATAL] Cannot create session table:", err)
}
}
if len(os.Args) > 1 {
if os.Args[1] == "init_db" {
initDb()
@ -327,6 +360,18 @@ func main() {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return
}
stamp, ok := data["stamp"].(string)
if !ok {
c.JSON(400, gin.H{"error": "Invalid JSON"})
return
}
pow := hashcash.New(20, 16, "I love burgernotes!")
ok = pow.Check(stamp)
if !ok {
c.JSON(400, gin.H{"error": "Invalid hashcash stamp"})
return
}
if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) {
c.JSON(422, gin.H{"error": "Invalid username or password"})
@ -379,7 +424,7 @@ func main() {
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONSALT"})
return
}
_, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent"))
_, err = mem.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent"))
if err != nil {
log.Println("[ERROR] Unknown in /api/signup session Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONINSERT"})
@ -448,7 +493,7 @@ func main() {
return
}
_, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent"))
_, err = mem.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent"))
if err != nil {
log.Println("[ERROR] Unknown in /api/login session Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONINSERT"})
@ -1121,7 +1166,7 @@ func main() {
return
}
_, err = conn.Exec("DELETE FROM sessions WHERE id = ?", userid)
_, err = mem.Exec("DELETE FROM sessions WHERE id = ?", userid)
if err != nil {
log.Println("[ERROR] Unknown in /api/deleteaccount session Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-SESSIONDELETE"})
@ -1151,7 +1196,7 @@ func main() {
return
}
rows, err := conn.Query("SELECT sessionid, session, device FROM sessions WHERE id = ? ORDER BY id DESC", userid)
rows, err := mem.Query("SELECT sessionid, session, device FROM sessions WHERE id = ? ORDER BY id DESC", userid)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
c.JSON(200, []map[string]interface{}{})
@ -1236,7 +1281,7 @@ func main() {
c.JSON(403, gin.H{"error": "Session does not belong to user"})
return
} else {
_, err := conn.Exec("DELETE FROM sessions WHERE sessionid = ?", sessionId)
_, err := mem.Exec("DELETE FROM sessions WHERE sessionid = ?", sessionId)
if err != nil {
log.Println("[ERROR] Unknown in /api/sessions/remove Exec():", err)
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-DBDELETE"})

View File

@ -1,6 +1,6 @@
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS notes;
DROP TABLE IF EXISTS sessions;
DROP TABLE IF EXISTS oauth;
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -18,13 +18,6 @@ CREATE TABLE notes (
title TEXT NOT NULL
);
CREATE TABLE sessions (
sessionid INTEGER PRIMARY KEY AUTOINCREMENT,
session TEXT NOT NULL,
id INTEGER NOT NULL,
device TEXT NOT NULL DEFAULT '?'
);
CREATE TABLE oauth (
id INTEGER NOT NULL,
oauthProvider TEXT NOT NULL,