@@ -37,6 +37,7 @@ import (
3737 "github.com/forceu/gokapi/internal/storage/presign"
3838 "github.com/forceu/gokapi/internal/webserver/api"
3939 "github.com/forceu/gokapi/internal/webserver/authentication"
40+ "github.com/forceu/gokapi/internal/webserver/authentication/downloadPasswordToken"
4041 "github.com/forceu/gokapi/internal/webserver/authentication/oauth"
4142 "github.com/forceu/gokapi/internal/webserver/authentication/sessionmanager"
4243 "github.com/forceu/gokapi/internal/webserver/authentication/tokengeneration"
@@ -579,26 +580,32 @@ func showDownload(w http.ResponseWriter, r *http.Request) {
579580 }
580581 }
581582
582- if file .PasswordHash != "" {
583+ if file .PasswordHash != "" && ! isValidPwCookie ( r , file ) {
583584 _ = r .ParseForm ()
584585 enteredPassword := r .PostForm .Get ("password" )
585- if configuration .HashPassword (enteredPassword , true ) != file .PasswordHash && ! isValidPwCookie (r , file ) {
586- if enteredPassword != "" {
587- view .IsFailedLogin = true
588- time .Sleep (1 * time .Second )
589- }
586+ if enteredPassword == "" {
590587 view .IsPasswordView = true
591588 err := templateFolder .ExecuteTemplate (w , "download_password" , view )
592589 helper .CheckIgnoreTimeout (err )
593590 return
594591 }
595- if ! isValidPwCookie (r , file ) {
592+
593+ ip := logging .GetIpAddress (r )
594+ ratelimiter .WaitOnDownloadPassword (ip )
595+
596+ if configuration .HashPassword (enteredPassword , true ) == file .PasswordHash {
596597 writeFilePwCookie (w , file )
597598 // redirect so that there is no post data to be resent if user refreshes page
598599 redirect (w , "d?id=" + file .Id )
599600 return
600601 }
602+ view .IsFailedLogin = true
603+ view .IsPasswordView = true
604+ err := templateFolder .ExecuteTemplate (w , "download_password" , view )
605+ helper .CheckIgnoreTimeout (err )
606+ return
601607 }
608+
602609 err := templateFolder .ExecuteTemplate (w , "download" , view )
603610 helper .CheckIgnoreTimeout (err )
604611}
@@ -1095,23 +1102,21 @@ func newAdminButtonContext(file models.FileApiOutput, user models.User) adminBut
10951102// Write a cookie if the user has entered a correct password for a password-protected file
10961103func writeFilePwCookie (w http.ResponseWriter , file models.File ) {
10971104 http .SetCookie (w , & http.Cookie {
1098- Name : "p" + file .Id ,
1099- Value : file .PasswordHash ,
1100- Expires : time .Now ().Add (5 * time .Minute ),
1105+ Name : "p" + file .Id ,
1106+ Value : downloadPasswordToken .Generate (file .Id ),
1107+ Expires : time .Now ().Add (5 * time .Minute ),
1108+ HttpOnly : true ,
1109+ SameSite : http .SameSiteStrictMode ,
11011110 })
11021111}
11031112
1104- // Checks if a cookie contains the correct password hash for a password-protected file
1105- // If incorrect, a 3-second delay is introduced unless the cookie was empty.
1113+ // Checks if a cookie contains the correct token for a password-protected file
11061114func isValidPwCookie (r * http.Request , file models.File ) bool {
11071115 cookie , err := r .Cookie ("p" + file .Id )
1108- if err == nil {
1109- if cookie .Value == file .PasswordHash {
1110- return true
1111- }
1112- time .Sleep (3 * time .Second )
1116+ if err != nil {
1117+ return false
11131118 }
1114- return false
1119+ return downloadPasswordToken . IsValid ( cookie . Value , file . Id )
11151120}
11161121
11171122// Adds a header to disable external caching
0 commit comments