satellite/{consoleapi,web}: limit user input
This change limits the length of user input fields like search, email, username. It also limits the receivable size of request payloads. This is to prevent potential DDoS attacks resulting from receiving large payloads. Improvements are also made to the accounts page and register success pages to display long names/emails better. Issue: https://github.com/storj/storj-private/issues/201 Change-Id: I5d36eb83609b3605335a8d150b275c89deaf3b43
This commit is contained in:
parent
61ad5751f6
commit
2e3ed973de
@ -1062,6 +1062,8 @@ func (a *Auth) serveJSONError(w http.ResponseWriter, err error) {
|
||||
|
||||
// getStatusCode returns http.StatusCode depends on console error class.
|
||||
func (a *Auth) getStatusCode(err error) int {
|
||||
var maxBytesError *http.MaxBytesError
|
||||
|
||||
switch {
|
||||
case console.ErrValidation.Has(err), console.ErrCaptcha.Has(err), console.ErrMFAMissing.Has(err), console.ErrMFAPasscode.Has(err), console.ErrMFARecoveryCode.Has(err), console.ErrChangePassword.Has(err):
|
||||
return http.StatusBadRequest
|
||||
@ -1071,6 +1073,8 @@ func (a *Auth) getStatusCode(err error) int {
|
||||
return http.StatusConflict
|
||||
case errors.Is(err, errNotImplemented):
|
||||
return http.StatusNotImplemented
|
||||
case errors.As(err, &maxBytesError):
|
||||
return http.StatusRequestEntityTooLarge
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
@ -1078,6 +1082,8 @@ func (a *Auth) getStatusCode(err error) int {
|
||||
|
||||
// getUserErrorMessage returns a user-friendly representation of the error.
|
||||
func (a *Auth) getUserErrorMessage(err error) string {
|
||||
var maxBytesError *http.MaxBytesError
|
||||
|
||||
switch {
|
||||
case console.ErrCaptcha.Has(err):
|
||||
return "Validation of captcha was unsuccessful"
|
||||
@ -1104,6 +1110,8 @@ func (a *Auth) getUserErrorMessage(err error) string {
|
||||
return err.Error()
|
||||
case errors.Is(err, errNotImplemented):
|
||||
return "The server is incapable of fulfilling the request"
|
||||
case errors.As(err, &maxBytesError):
|
||||
return "Request body is too large"
|
||||
default:
|
||||
return "There was an error processing your request"
|
||||
}
|
||||
|
@ -343,8 +343,7 @@ func (p *Payments) ApplyCouponCode(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
// limit the size of the body to prevent excessive memory usage
|
||||
bodyBytes, err := io.ReadAll(io.LimitReader(r.Body, 1*1024*1024))
|
||||
bodyBytes, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
p.serveJSONError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"storj.io/common/errs2"
|
||||
"storj.io/common/memory"
|
||||
"storj.io/common/storj"
|
||||
"storj.io/storj/private/web"
|
||||
"storj.io/storj/satellite/abtesting"
|
||||
@ -106,6 +107,8 @@ type Config struct {
|
||||
OauthAccessTokenExpiry time.Duration `help:"how long oauth access tokens are issued for" default:"24h"`
|
||||
OauthRefreshTokenExpiry time.Duration `help:"how long oauth refresh tokens are issued for" default:"720h"`
|
||||
|
||||
BodySizeLimit memory.Size `help:"The maximum body size allowed to be received by the API" default:"100.00 KB"`
|
||||
|
||||
// RateLimit defines the configuration for the IP and userID rate limiters.
|
||||
RateLimit web.RateLimiterConfig
|
||||
|
||||
@ -239,6 +242,9 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
|
||||
// the earliest in the HTTP chain.
|
||||
router.Use(newTraceRequestMiddleware(logger, router))
|
||||
|
||||
// limit body size
|
||||
router.Use(newBodyLimiterMiddleware(logger.Named("body-limiter-middleware"), config.BodySizeLimit))
|
||||
|
||||
if server.config.GeneratedAPIEnabled {
|
||||
consoleapi.NewProjectManagement(logger, mon, server.service, router, &apiAuth{&server})
|
||||
consoleapi.NewAPIKeyManagement(logger, mon, server.service, router, &apiAuth{&server})
|
||||
@ -980,3 +986,18 @@ func newTraceRequestMiddleware(log *zap.Logger, root *mux.Router) mux.Middleware
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// newBodyLimiterMiddleware returns a middleware that places a length limit on each request's body.
|
||||
func newBodyLimiterMiddleware(log *zap.Logger, limit memory.Size) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.ContentLength > limit.Int64() {
|
||||
web.ServeJSONError(log, w, http.StatusRequestEntityTooLarge, errs.New("Request body is too large"))
|
||||
return
|
||||
}
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, limit.Int64())
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
3
scripts/testdata/satellite-config.yaml.lock
vendored
3
scripts/testdata/satellite-config.yaml.lock
vendored
@ -190,6 +190,9 @@ compensation.withheld-percents: 75,75,75,50,50,50,25,25,25,0,0,0,0,0,0
|
||||
# url link for for beta satellite support
|
||||
# console.beta-satellite-support-url: ""
|
||||
|
||||
# The maximum body size allowed to be received by the API
|
||||
# console.body-size-limit: 100.00 KB
|
||||
|
||||
# whether or not captcha is enabled
|
||||
# console.captcha.login.hcaptcha.enabled: false
|
||||
|
||||
|
@ -262,6 +262,12 @@ onMounted(() => {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: var(--c-grey-6);
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (width <= 500px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="register-success-area__container">
|
||||
<MailIcon />
|
||||
<h2 class="register-success-area__container__title" aria-roledescription="title">You're almost there!</h2>
|
||||
<div v-if="showManualActivationMsg" class="register-success-area__container__sub-title">
|
||||
<div v-if="showManualActivationMsg" class="register-success-area__container__sub-title fill">
|
||||
If an account with the email address
|
||||
<p class="register-success-area__container__sub-title__email">{{ userEmail }}</p>
|
||||
exists, a verification email has been sent.
|
||||
@ -197,6 +197,10 @@ onBeforeUnmount(() => {
|
||||
text-align: center;
|
||||
margin-bottom: 27px;
|
||||
|
||||
&.fill {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
&__email {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
:style="style"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
maxlength="72"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
@input="processSearchQuery"
|
||||
|
@ -9,6 +9,7 @@
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
readonly
|
||||
maxlength="72"
|
||||
@input="processSearchQuery"
|
||||
@focus="removeReadOnly"
|
||||
@blur="addReadOnly"
|
||||
|
@ -14,6 +14,7 @@
|
||||
<VInput
|
||||
label="Full Name"
|
||||
placeholder="Enter Full Name"
|
||||
max-symbols="72"
|
||||
:error="fullNameError"
|
||||
:init-value="userInfo.fullName"
|
||||
@setData="setFullName"
|
||||
|
@ -50,6 +50,7 @@
|
||||
<div class="forgot-area__content-area__container__input-wrapper">
|
||||
<VInput
|
||||
label="Email Address"
|
||||
max-symbols="72"
|
||||
placeholder="user@example.com"
|
||||
:error="emailError"
|
||||
@setData="setEmail"
|
||||
|
@ -115,6 +115,7 @@
|
||||
<div class="register-area__input-wrapper first-input">
|
||||
<VInput
|
||||
label="Full Name"
|
||||
max-symbols="72"
|
||||
placeholder="Enter Full Name"
|
||||
:error="fullNameError"
|
||||
role-description="name"
|
||||
@ -124,6 +125,7 @@
|
||||
<div class="register-area__input-wrapper">
|
||||
<VInput
|
||||
label="Email Address"
|
||||
max-symbols="72"
|
||||
placeholder="user@example.com"
|
||||
:error="emailError"
|
||||
role-description="email"
|
||||
@ -134,6 +136,7 @@
|
||||
<div class="register-area__input-wrapper">
|
||||
<VInput
|
||||
label="Company Name"
|
||||
max-symbols="72"
|
||||
placeholder="Acme Corp."
|
||||
:error="companyNameError"
|
||||
role-description="company-name"
|
||||
@ -143,6 +146,7 @@
|
||||
<div class="register-area__input-wrapper">
|
||||
<VInput
|
||||
label="Position"
|
||||
max-symbols="72"
|
||||
placeholder="Position Title"
|
||||
:error="positionError"
|
||||
role-description="position"
|
||||
|
Loading…
Reference in New Issue
Block a user