mirror of
https://github.com/appwrite/appwrite
synced 2026-05-24 09:28:40 +00:00
Merge pull request #2401 from PineappleIOnic/fix-improve-recovery-rate-limit
Update abuse-key for password recovery to add IP
This commit is contained in:
commit
b766ec5a4a
2 changed files with 35 additions and 26 deletions
|
|
@ -1733,7 +1733,7 @@ App::post('/v1/account/recovery')
|
||||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||||
->label('sdk.response.model', Response::MODEL_TOKEN)
|
->label('sdk.response.model', Response::MODEL_TOKEN)
|
||||||
->label('abuse-limit', 10)
|
->label('abuse-limit', 10)
|
||||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
->label('abuse-key', ['url:{url},email:{param-email}', 'ip:{ip}'])
|
||||||
->param('email', '', new Email(), 'User email.')
|
->param('email', '', new Email(), 'User email.')
|
||||||
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
|
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
|
||||||
->inject('request')
|
->inject('request')
|
||||||
|
|
|
||||||
|
|
@ -37,41 +37,50 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
|
||||||
/*
|
/*
|
||||||
* Abuse Check
|
* Abuse Check
|
||||||
*/
|
*/
|
||||||
$timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $db);
|
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
|
||||||
$timeLimit->setNamespace('app_'.$project->getId());
|
$timeLimitArray = [];
|
||||||
$timeLimit
|
|
||||||
->setParam('{userId}', $user->getId())
|
|
||||||
->setParam('{userAgent}', $request->getUserAgent(''))
|
|
||||||
->setParam('{ip}', $request->getIP())
|
|
||||||
->setParam('{url}', $request->getHostname().$route->getPath())
|
|
||||||
;
|
|
||||||
|
|
||||||
//TODO make sure we get array here
|
$abuseKeyLabel = (!is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
|
||||||
|
|
||||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
foreach ($abuseKeyLabel as $abuseKey) {
|
||||||
if(!empty($value)) {
|
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $db);
|
||||||
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
|
$timeLimit->setNamespace('app_'.$project->getId());
|
||||||
}
|
$timeLimit
|
||||||
}
|
->setParam('{userId}', $user->getId())
|
||||||
|
->setParam('{userAgent}', $request->getUserAgent(''))
|
||||||
$abuse = new Abuse($timeLimit);
|
->setParam('{ip}', $request->getIP())
|
||||||
|
->setParam('{url}', $request->getHostname().$route->getPath());
|
||||||
if ($timeLimit->limit()) {
|
$timeLimitArray[] = $timeLimit;
|
||||||
$response
|
|
||||||
->addHeader('X-RateLimit-Limit', $timeLimit->limit())
|
|
||||||
->addHeader('X-RateLimit-Remaining', $timeLimit->remaining())
|
|
||||||
->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600))
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$closestLimit = null;
|
||||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||||
|
|
||||||
if (($abuse->check() // Route is rate-limited
|
foreach ($timeLimitArray as $timeLimit) {
|
||||||
|
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||||
|
if(!empty($value)) {
|
||||||
|
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$abuse = new Abuse($timeLimit);
|
||||||
|
|
||||||
|
if ($timeLimit->limit() && ($timeLimit->remaining() < $closestLimit || is_null($closestLimit))) {
|
||||||
|
$closestLimit = $timeLimit->remaining();
|
||||||
|
$response
|
||||||
|
->addHeader('X-RateLimit-Limit', $timeLimit->limit())
|
||||||
|
->addHeader('X-RateLimit-Remaining', $timeLimit->remaining())
|
||||||
|
->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($abuse->check() // Route is rate-limited
|
||||||
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not disabled
|
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not disabled
|
||||||
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
||||||
{
|
{
|
||||||
throw new Exception('Too many requests', 429);
|
throw new Exception('Too many requests', 429);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue