Added security timer middleware
This commit is contained in:
parent
40ba0d5455
commit
c913b58e4c
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue