Added security timer middleware

This commit is contained in:
Asaf Gartner 2021-08-17 09:08:33 +03:00
parent 40ba0d5455
commit c913b58e4c
3 changed files with 19 additions and 6 deletions

View File

@ -161,7 +161,6 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
blacklisted := false blacklisted := false
if blacklisted { if blacklisted {
// NOTE(asaf): Silent rejection so we don't allow attackers to harvest emails. // NOTE(asaf): Silent rejection so we don't allow attackers to harvest emails.
time.Sleep(time.Second * 3) // NOTE(asaf): Pretend to send email
return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther) return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther)
} }
c.Perf.EndBlock() c.Perf.EndBlock()
@ -208,7 +207,6 @@ func RegisterNewUserSubmit(c *RequestContext) ResponseData {
if emailAlreadyExists { if emailAlreadyExists {
// NOTE(asaf): Silent rejection so we don't allow attackers to harvest emails. // NOTE(asaf): Silent rejection so we don't allow attackers to harvest emails.
time.Sleep(time.Second * 3) // NOTE(asaf): Pretend to send email
return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther) return c.Redirect(hmnurl.BuildRegistrationSuccess(), http.StatusSeeOther)
} }

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"math/rand"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -19,11 +20,12 @@ import (
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/perf" "git.handmade.network/hmn/hmn/src/perf"
"git.handmade.network/hmn/hmn/src/templates" "git.handmade.network/hmn/hmn/src/templates"
"git.handmade.network/hmn/hmn/src/utils"
"github.com/jackc/pgx/v4/pgxpool" "github.com/jackc/pgx/v4/pgxpool"
"github.com/teacat/noire" "github.com/teacat/noire"
) )
func NewWebsiteRoutes(conn *pgxpool.Pool, perfCollector *perf.PerfCollector) http.Handler { func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, perfCollector *perf.PerfCollector) http.Handler {
router := &Router{} router := &Router{}
routes := RouteBuilder{ routes := RouteBuilder{
Router: router, Router: router,
@ -121,6 +123,16 @@ func NewWebsiteRoutes(conn *pgxpool.Pool, perfCollector *perf.PerfCollector) htt
} }
} }
securityTimerMiddleware := func(delayMs int, h Handler) Handler {
// NOTE(asaf): Will make sure that the request takes at least `delayMs` to finish. Adds a 10% random duration.
return func(c *RequestContext) ResponseData {
duration := time.Millisecond * time.Duration(delayMs+rand.Intn(utils.IntMax(1, delayMs/10)))
res := h(c)
utils.SleepContext(longRequestContext, duration)
return res
}
}
routes.GET(hmnurl.RegexPublic, func(c *RequestContext) ResponseData { routes.GET(hmnurl.RegexPublic, func(c *RequestContext) ResponseData {
var res ResponseData var res ResponseData
http.StripPrefix("/public/", http.FileServer(http.Dir("public"))).ServeHTTP(&res, c.Req) http.StripPrefix("/public/", http.FileServer(http.Dir("public"))).ServeHTTP(&res, c.Req)
@ -152,14 +164,14 @@ func NewWebsiteRoutes(conn *pgxpool.Pool, perfCollector *perf.PerfCollector) htt
mainRoutes.GET(hmnurl.RegexLoginPage, LoginPage) mainRoutes.GET(hmnurl.RegexLoginPage, LoginPage)
mainRoutes.GET(hmnurl.RegexRegister, RegisterNewUser) mainRoutes.GET(hmnurl.RegexRegister, RegisterNewUser)
mainRoutes.POST(hmnurl.RegexRegister, RegisterNewUserSubmit) mainRoutes.POST(hmnurl.RegexRegister, securityTimerMiddleware(3000, RegisterNewUserSubmit))
mainRoutes.GET(hmnurl.RegexRegistrationSuccess, RegisterNewUserSuccess) mainRoutes.GET(hmnurl.RegexRegistrationSuccess, RegisterNewUserSuccess)
mainRoutes.GET(hmnurl.RegexOldEmailConfirmation, EmailConfirmation) // TODO(asaf): Delete this a bit after launch mainRoutes.GET(hmnurl.RegexOldEmailConfirmation, EmailConfirmation) // TODO(asaf): Delete this a bit after launch
mainRoutes.GET(hmnurl.RegexEmailConfirmation, EmailConfirmation) mainRoutes.GET(hmnurl.RegexEmailConfirmation, EmailConfirmation)
mainRoutes.POST(hmnurl.RegexEmailConfirmation, EmailConfirmationSubmit) mainRoutes.POST(hmnurl.RegexEmailConfirmation, EmailConfirmationSubmit)
mainRoutes.GET(hmnurl.RegexRequestPasswordReset, RequestPasswordReset) mainRoutes.GET(hmnurl.RegexRequestPasswordReset, RequestPasswordReset)
mainRoutes.POST(hmnurl.RegexRequestPasswordReset, RequestPasswordResetSubmit) mainRoutes.POST(hmnurl.RegexRequestPasswordReset, securityTimerMiddleware(3000, RequestPasswordResetSubmit))
mainRoutes.GET(hmnurl.RegexPasswordResetSent, PasswordResetSent) mainRoutes.GET(hmnurl.RegexPasswordResetSent, PasswordResetSent)
mainRoutes.GET(hmnurl.RegexOldDoPasswordReset, DoPasswordReset) mainRoutes.GET(hmnurl.RegexOldDoPasswordReset, DoPasswordReset)
mainRoutes.GET(hmnurl.RegexDoPasswordReset, DoPasswordReset) mainRoutes.GET(hmnurl.RegexDoPasswordReset, DoPasswordReset)

View File

@ -30,13 +30,14 @@ var WebsiteCommand = &cobra.Command{
logging.Info().Msg("Hello, HMN!") logging.Info().Msg("Hello, HMN!")
backgroundJobContext, cancelBackgroundJobs := context.WithCancel(context.Background()) backgroundJobContext, cancelBackgroundJobs := context.WithCancel(context.Background())
longRequestContext, cancelLongRequests := context.WithCancel(context.Background())
conn := db.NewConnPool(config.Config.Postgres.MinConn, config.Config.Postgres.MaxConn) conn := db.NewConnPool(config.Config.Postgres.MinConn, config.Config.Postgres.MaxConn)
perfCollector := perf.RunPerfCollector(backgroundJobContext) perfCollector := perf.RunPerfCollector(backgroundJobContext)
server := http.Server{ server := http.Server{
Addr: config.Config.Addr, Addr: config.Config.Addr,
Handler: NewWebsiteRoutes(conn, perfCollector), Handler: NewWebsiteRoutes(longRequestContext, conn, perfCollector),
} }
backgroundJobsDone := zipJobs( backgroundJobsDone := zipJobs(
@ -52,6 +53,8 @@ var WebsiteCommand = &cobra.Command{
<-signals <-signals
logging.Info().Msg("Shutting down the website") logging.Info().Msg("Shutting down the website")
go func() { go func() {
logging.Info().Msg("cancelling long requests")
cancelLongRequests()
timeout, cancel := context.WithTimeout(context.Background(), 30*time.Second) timeout, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
logging.Info().Msg("shutting down web server") logging.Info().Msg("shutting down web server")