CVE-2022-4723: Allocation of Resources Without Limits or Throttling (CWE-770)
value: Allocation of Resources Without Limits or Throttling in GitHub repository ikus060/rdiffweb prior to 2.5.5.
CWE description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
CVSS v3 base score: 6.5
Published: 2022-12-27T15:15Z
Repository: https://github.com/ikus060/rdiffweb
Language: Python
Files changed: README.md, rdiffweb/controller/page_pref_mfa.py, rdiffweb/core/config.py, rdiffweb/tools/ratelimit.py
Commit date: 2022-12-23 14:02:50 -0500
Commit message: Ratelimit "Resend code to my email" in Two-Factor Authentication view

Diff: diff --git a/README.md b/README.md
index e5a4ae850..d621ee939 100644
--- a/README.md
+++ b/README.md
@@ -111,10 +111,11 @@ Professional support for Rdiffweb is available by contacting [IKUS Soft](https:/
 ## Next Release - 2.5.5
 
 * Fix loading of Charts in Status page
 * Ensure Gmail and other mail client doesn't create hyperlink automatically for any nodification sent by Rdiffweb to avoid phishing - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
 * Sent email notification to user when a new SSH Key get added - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
+* Ratelimit "Resend code to my email" in Two-Factor Authentication view - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
 
 ## 2.5.4 (2022-12-19)
 
 * Discard `X-Forwarded-Host` headers credit to [Anishka Shukla](https://github.com/anishkashukla)
 * Create proper symbolic link of `chartkick.js` on Ubuntu Jammy to fix loading of Charts in web interface
diff --git a/rdiffweb/controller/page_pref_mfa.py b/rdiffweb/controller/page_pref_mfa.py
index fb4d9ffd4..1f20e68b4 100644
--- a/rdiffweb/controller/page_pref_mfa.py
+++ b/rdiffweb/controller/page_pref_mfa.py
@@ -105,10 +105,11 @@ class MfaToggleForm(AbstractMfaForm):
         return super().validate()
 
 
 class PagePrefMfa(Controller):
     @cherrypy.expose
+    @cherrypy.tools.ratelimit(methods=['POST'])
     def default(self, action=None, **kwargs):
         form = MfaToggleForm(obj=self.app.currentuser)
         if form.is_submitted():
             if form.validate():
                 if form.resend_code.data:
diff --git a/rdiffweb/core/config.py b/rdiffweb/core/config.py
index cc2d78fdc..1b9f0e5f2 100644
--- a/rdiffweb/core/config.py
+++ b/rdiffweb/core/config.py
@@ -469,11 +469,11 @@ def get_parser():
     parser.add(
         '--rate-limit',
         metavar='LIMIT',
         type=int,
         default=20,
-        help='maximum number of requests per hour that can be made on sensitive endpoints. When this limit is reached, an HTTP 429 message is returned to the user or the user is logged out. This security measure is used to limit brute force attacks on the login page and the RESTful API.',
+        help='maximum number of requests per hour that can be made on sensitive endpoints. When this limit is reached, an HTTP 429 message is returned to the user or the user is logged out. This security measure is used to limit brute force attacks on the login page and the RESTful API. default: 20 requests / hour',
     )
 
     parser.add(
         '--session-idle-timeout',
         metavar='MINUTES',
diff --git a/rdiffweb/tools/ratelimit.py b/rdiffweb/tools/ratelimit.py
index 0ec87ed89..6fab2cfec 100644
--- a/rdiffweb/tools/ratelimit.py
+++ b/rdiffweb/tools/ratelimit.py
@@ -148,11 +148,14 @@ def check_ratelimit(
         storage_class = conf.get('storage_class', RamRateLimit)
         datastore = storage_class(**conf)
         cherrypy.request.app._ratelimit_datastore = datastore
 
     # If user is authenticated, use the username else use the ip address
-    token = (request.login or request.remote.ip) + '.' + (scope or request.path_info)
+    identifier = request.remote.ip
+    if hasattr(cherrypy.serving, 'session') and cherrypy.serving.session.get('_cp_username', None):
+        identifier = cherrypy.serving.session.get('_cp_username', None)
+    token = identifier + '.' + (scope or request.path_info)
 
     # Get hits count using datastore.
     hits = datastore.get_and_increment(token, delay, hit)
     if debug:
         cherrypy.log(

Vulnerable code:         help='maximum number of requests per hour that can be made on sensitive endpoints. When this limit is reached, an HTTP 429 message is returned to the user or the user is logged out. This security measure is used to limit brute force attacks on the login page and the RESTful API.',
    token = (request.login or request.remote.ip) + '.' + (scope or request.path_info)
Fixed code: * Ratelimit "Resend code to my email" in Two-Factor Authentication view - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
    @cherrypy.tools.ratelimit(methods=['POST'])
        help='maximum number of requests per hour that can be made on sensitive endpoints. When this limit is reached, an HTTP 429 message is returned to the user or the user is logged out. This security measure is used to limit brute force attacks on the login page and the RESTful API. default: 20 requests / hour',
    identifier = request.remote.ip
    if hasattr(cherrypy.serving, 'session') and cherrypy.serving.session.get('_cp_username', None):
        identifier = cherrypy.serving.session.get('_cp_username', None)
    token = identifier + '.' + (scope or request.path_info)