commit 5c61614a49ca566388cf4952a09786ed6717e733
parent 1d0e28b7273bea0f2647bc302308ba593c1de742
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Wed, 14 Dec 2022 12:42:53 -0500
simplify code, make it possible to reuse wait page for other endpoints
Diffstat:
1 file changed, 49 insertions(+), 35 deletions(-)
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -569,10 +569,6 @@ func createSessionCookie(value string) *http.Cookie {
return hutils.CreateCookie(hutils.AuthCookieName, value, utils.OneMonthSecs)
}
-func createSignupCookie(value string, maxAge int64) *http.Cookie {
- return hutils.CreateCookie(hutils.SignupCookieName, value, maxAge)
-}
-
// FlashResponse ...
type FlashResponse struct {
Message string
@@ -603,7 +599,7 @@ func SignupInvitationHandler(c echo.Context) error {
if _, err := database.GetUnusedInvitationByToken(invitationToken); err != nil {
return c.Redirect(http.StatusFound, "/")
}
- return signupHandler(c)
+ return waitPageWrapper(c, signupHandler, hutils.SignupCookieName)
}
func AesNB64(in string) string {
@@ -724,16 +720,41 @@ func SignupHandler(c echo.Context) error {
if config.SignupFakeEnabled.IsFalse() && config.SignupEnabled.IsFalse() {
return c.Render(http.StatusOK, "signup-invite", nil)
}
- return signupHandler(c)
+ return waitPageWrapper(c, signupHandler, hutils.SignupCookieName)
}
-// The random wait time 0-15 seconds make sure the load is evenly distributed while under DDoS.
-// Not all requests to the signup endpoint will get the captcha at the same time,
-// so you cannot just refresh the page until you get a captcha that is easier to crack.
-func signupHandler(c echo.Context) error {
+func waitPageWrapper(c echo.Context, clb echo.HandlerFunc, cookieName string) error {
start := time.Now().UnixNano()
var signupToken string
- if cc, err := c.Cookie(hutils.SignupCookieName); err == nil {
+ if cc, err := c.Cookie(hutils.SignupCookieName); err != nil {
+ // No cookie found, we create one and display the waiting page.
+ waitTime := utils.Random(5, 15)
+ signupToken = utils.GenerateToken10()
+ payload := map[string]string{
+ "token": signupToken,
+ "count": "1",
+ "now": utils.FormatInt64(time.Now().UnixMilli()),
+ "unix": utils.FormatInt64(time.Now().Unix() + waitTime - 1), // unix time at which the wait time is over
+ }
+ by, _ := json.Marshal(payload)
+ encryptedVal, _ := utils.EncryptAES(by, []byte(config.Global.MasterKey()))
+ valB64 := base64.URLEncoding.EncodeToString(encryptedVal)
+ c.SetCookie(hutils.CreateCookie(cookieName, valB64, utils.OneMinuteSecs*5))
+
+ var data1 signupWaitData
+ // Generate css frames
+ step := 100.0 / float64(waitTime)
+ pct := 0.0
+ for i := int64(0); i <= waitTime; i++ {
+ data1.Frames = append(data1.Frames, fmt.Sprintf(`%.2f%% { content: "%d"; }`, pct, waitTime-i))
+ pct += step
+ }
+ data1.WaitTime = waitTime
+ data1.SignupToken = signupToken
+ return c.Render(http.StatusOK, "signup-wait", data1)
+
+ } else {
+ // Cookie was found, incr counter then call callback
valB64 := cc.Value
val, err := base64.URLEncoding.DecodeString(valB64)
if err != nil {
@@ -752,16 +773,19 @@ func signupHandler(c echo.Context) error {
if c.Request().Method == http.MethodGet {
count := utils.DoParseInt64(payload["count"])
unix := utils.DoParseInt64(payload["unix"])
- if time.Now().Unix() < unix {
+ // If you reload the page before the wait time is over, we kill the circuit.
+ if time.Now().Unix() < unix {
// Kill circuit
if conn, ok := c.Request().Context().Value("conn").(net.Conn); ok {
config.ConnMap.Close(conn)
}
return c.String(http.StatusFound, "DDoS filter killed your path")
}
+
+ // If the wait time is over, and you reload the protected page more than 4 times, we make you wait 1min
if count >= 4 {
- c.SetCookie(createSignupCookie(valB64, utils.OneMinuteSecs))
+ c.SetCookie(hutils.CreateCookie(cookieName, valB64, utils.OneMinuteSecs))
return c.String(http.StatusFound, "You tried to reload the page to many times. Now you have to wait one minute.")
}
newPayload := map[string]string{
@@ -772,30 +796,20 @@ func signupHandler(c echo.Context) error {
by, _ := json.Marshal(newPayload)
newEncryptedVal, _ := utils.EncryptAES(by, []byte(config.Global.MasterKey()))
newValB64 := base64.URLEncoding.EncodeToString(newEncryptedVal)
- c.SetCookie(createSignupCookie(newValB64, utils.OneMinuteSecs*5))
+ c.SetCookie(hutils.CreateCookie(cookieName, newValB64, utils.OneMinuteSecs*5))
}
- } else {
- // No cookie found, we create one and display the waiting page.
- waitTime := utils.Random(5, 15)
- signupToken = utils.GenerateToken10()
- payload := map[string]string{"token": signupToken, "count": "1", "now": utils.FormatInt64(time.Now().UnixMilli()), "unix": utils.FormatInt64(time.Now().Unix() + waitTime - 1)}
- by, _ := json.Marshal(payload)
- encryptedVal, _ := utils.EncryptAES(by, []byte(config.Global.MasterKey()))
- valB64 := base64.URLEncoding.EncodeToString(encryptedVal)
- c.SetCookie(createSignupCookie(valB64, utils.OneMinuteSecs*5))
-
- var data1 signupWaitData
- // Generate css frames
- step := 100.0 / float64(waitTime)
- pct := 0.0
- for i := int64(0); i <= waitTime; i++ {
- data1.Frames = append(data1.Frames, fmt.Sprintf(`%.2f%% { content: "%d"; }`, pct, waitTime-i))
- pct += step
- }
- data1.WaitTime = waitTime
- data1.SignupToken = signupToken
- return c.Render(http.StatusOK, "signup-wait", data1)
}
+ c.Set("start", start)
+ c.Set("signupToken", signupToken)
+ return clb(c)
+}
+
+// The random wait time 0-15 seconds make sure the load is evenly distributed while under DDoS.
+// Not all requests to the signup endpoint will get the captcha at the same time,
+// so you cannot just refresh the page until you get a captcha that is easier to crack.
+func signupHandler(c echo.Context) error {
+ start := c.Get("start").(int64)
+ signupToken := c.Get("signupToken").(string)
var data signupData
config.SignupPageLoad.Inc()