Add CSRF checks to boards and threads.

jocadbz
Joca 2026-03-06 14:51:54 -03:00
parent 48363ccef9
commit 82a7e48827
Signed by: jocadbz
GPG Key ID: B1836DCE2F50BDF7
6 changed files with 23 additions and 2 deletions

View File

@ -58,6 +58,11 @@ func BoardHandler(app *App) http.HandlerFunc {
if r.Method == http.MethodPost && loggedIn { if r.Method == http.MethodPost && loggedIn {
action := r.URL.Query().Get("action") action := r.URL.Query().Get("action")
if action == "create_thread" { if action == "create_thread" {
if !app.validateCSRFToken(r, session) {
http.Error(w, "Invalid CSRF token", http.StatusForbidden)
return
}
title := r.FormValue("title") title := r.FormValue("title")
if title == "" { if title == "" {
http.Error(w, "Thread title is required", http.StatusBadRequest) http.Error(w, "Thread title is required", http.StatusBadRequest)
@ -118,6 +123,7 @@ func BoardHandler(app *App) http.HandlerFunc {
BasePath: app.Config.ThreadrDir, BasePath: app.Config.ThreadrDir,
StaticPath: app.Config.ThreadrDir + "/static", StaticPath: app.Config.ThreadrDir + "/static",
CurrentURL: r.URL.RequestURI(), CurrentURL: r.URL.RequestURI(),
CSRFToken: app.csrfToken(session),
}, },
Board: *board, Board: *board,
Threads: threads, Threads: threads,

View File

@ -26,6 +26,11 @@ func BoardsHandler(app *App) http.HandlerFunc {
} }
if r.Method == http.MethodPost && loggedIn && isAdmin { if r.Method == http.MethodPost && loggedIn && isAdmin {
if !app.validateCSRFToken(r, session) {
http.Error(w, "Invalid CSRF token", http.StatusForbidden)
return
}
name := r.FormValue("name") name := r.FormValue("name")
description := r.FormValue("description") description := r.FormValue("description")
boardType := r.FormValue("type") boardType := r.FormValue("type")
@ -106,6 +111,7 @@ func BoardsHandler(app *App) http.HandlerFunc {
BasePath: app.Config.ThreadrDir, BasePath: app.Config.ThreadrDir,
StaticPath: app.Config.ThreadrDir + "/static", StaticPath: app.Config.ThreadrDir + "/static",
CurrentURL: r.URL.RequestURI(), CurrentURL: r.URL.RequestURI(),
CSRFToken: app.csrfToken(session),
}, },
PublicBoards: publicBoards, PublicBoards: publicBoards,
PrivateBoards: privateBoards, PrivateBoards: privateBoards,

View File

@ -59,6 +59,11 @@ func ThreadHandler(app *App) http.HandlerFunc {
if r.Method == http.MethodPost && loggedIn { if r.Method == http.MethodPost && loggedIn {
action := r.URL.Query().Get("action") action := r.URL.Query().Get("action")
if action == "submit" { if action == "submit" {
if !app.validateCSRFToken(r, session) {
http.Error(w, "Invalid CSRF token", http.StatusForbidden)
return
}
content := r.FormValue("content") content := r.FormValue("content")
replyToStr := r.FormValue("reply_to") replyToStr := r.FormValue("reply_to")
if replyToStr == "" { if replyToStr == "" {
@ -164,6 +169,7 @@ func ThreadHandler(app *App) http.HandlerFunc {
BasePath: app.Config.ThreadrDir, BasePath: app.Config.ThreadrDir,
StaticPath: app.Config.ThreadrDir + "/static", StaticPath: app.Config.ThreadrDir + "/static",
CurrentURL: r.URL.RequestURI(), CurrentURL: r.URL.RequestURI(),
CSRFToken: app.csrfToken(session),
}, },
Thread: *thread, Thread: *thread,
Board: *board, Board: *board,

View File

@ -40,6 +40,7 @@
<section> <section>
<h3>Create New Thread</h3> <h3>Create New Thread</h3>
<form method="post" action="{{.BasePath}}/board/?id={{.Board.ID}}&action=create_thread"> <form method="post" action="{{.BasePath}}/board/?id={{.Board.ID}}&action=create_thread">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<label for="title">Thread Title:</label> <label for="title">Thread Title:</label>
<input type="text" id="title" name="title" required maxlength="255"><br> <input type="text" id="title" name="title" required maxlength="255"><br>
<input type="submit" value="Create Thread"> <input type="submit" value="Create Thread">

View File

@ -56,6 +56,7 @@
<section> <section>
<h3>Create New Public Board</h3> <h3>Create New Public Board</h3>
<form method="post" action="{{.BasePath}}/boards/"> <form method="post" action="{{.BasePath}}/boards/">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<label for="name">Board Name:</label> <label for="name">Board Name:</label>
<input type="text" id="name" name="name" required maxlength="255"><br> <input type="text" id="name" name="name" required maxlength="255"><br>
<label for="description">Description:</label> <label for="description">Description:</label>

View File

@ -62,6 +62,7 @@
<button type="button" onclick="clearReply()">x</button> <button type="button" onclick="clearReply()">x</button>
</div> </div>
<form method="post" action="{{.BasePath}}/thread/?id={{.Thread.ID}}&action=submit" id="reply-form"> <form method="post" action="{{.BasePath}}/thread/?id={{.Thread.ID}}&action=submit" id="reply-form">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<input type="hidden" id="reply-to-input" name="reply_to" value=""> <input type="hidden" id="reply-to-input" name="reply_to" value="">
<label for="content">Content:</label> <label for="content">Content:</label>
<textarea id="content" name="content" required></textarea><br> <textarea id="content" name="content" required></textarea><br>