CVE-2022-3221: Cross-Site Request Forgery (CSRF) (CWE-352)
value: Cross-Site Request Forgery (CSRF) in GitHub repository ikus060/rdiffweb prior to 2.4.3.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2022-09-15T09:15Z
Repository: https://github.com/ikus060/rdiffweb
Language: Python
Files changed: README.md, rdiffweb/controller/pref_sshkeys.py, rdiffweb/controller/tests/test_page_prefs_ssh.py
Commit date: 2022-09-14 11:11:30 -0400
Commit message: Mitigate CSRF in profile's SSH Keys #212

Diff: diff --git a/README.md b/README.md
index 3f39290e..ff58d594 100644
--- a/README.md
+++ b/README.md
@@ -105,10 +105,16 @@ Bug reports should be reported on the Rdiffweb Gitlab at https://gitlab.com/ikus
 
 Professional support for Rdiffweb is available by contacting [IKUS Soft](https://www.ikus-soft.com/en/support/#form).
 
 # Changelog
 
+## 2.4.3 (2022-09-14)
+
+This releases include a security fix. If you are using an earlier version, you should upgrade to this release immediately.
+
+* Mitigate CSRF in profile's SSH Keys #212
+
 ## 2.4.2 (2022-09-12)
 
 This releases include a security fix. If you are using an earlier version, you should upgrade to this release immediately.
 
 * Use 'Secure' Attribute with Sensitive Cookie in HTTPS Session. [CVE-2022-3174](https://nvd.nist.gov/vuln/detail/CVE-2022-3174) #209
diff --git a/rdiffweb/controller/pref_sshkeys.py b/rdiffweb/controller/pref_sshkeys.py
index 50df14f9..4fc35557 100644
--- a/rdiffweb/controller/pref_sshkeys.py
+++ b/rdiffweb/controller/pref_sshkeys.py
@@ -109,13 +109,14 @@ class SSHKeysPlugin(Controller):
 
     def render_prefs_panel(self, panelid, action=None, **kwargs):  # @UnusedVariable
 
         # Handle action
         form = SshForm()
-        if action == "add":
+        delete_form = DeleteSshForm()
+        if action == "add" and form.is_submitted():
             self._add_key(action, form)
-        elif action == 'delete':
+        elif action == 'delete' and delete_form.is_submitted():
             self._delete_key(action, DeleteSshForm())
 
         # Get SSH keys if file exists.
         params = {'form': form}
         try:
diff --git a/rdiffweb/controller/tests/test_page_prefs_ssh.py b/rdiffweb/controller/tests/test_page_prefs_ssh.py
index 0069001d..3ef499ac 100644
--- a/rdiffweb/controller/tests/test_page_prefs_ssh.py
+++ b/rdiffweb/controller/tests/test_page_prefs_ssh.py
@@ -97,10 +97,22 @@ class SSHKeysTest(rdiffweb.test.WebCase):
         self._add_ssh_key("test@mysshkey", "lkjasdfoiuwerlk")
         self.assertStatus(200)
         self.assertInBody("Invalid SSH key.")
         self.assertEqual(0, len(list(user.authorizedkeys)))
 
+    def test_add_get_method(self):
+        # Given an authenticated user
+        user = self.app.store.get_user('admin')
+        # When querying a page with parameters (HTTP GET)
+        self.getPage(
+            "/prefs/sshkeys?action=add&title=ssh1&key=ssh-rsa+AAAAB3NzaC1yc2EAAAADAQABAAAAgQCzurRNVKwb0ZJCmUgGenoe4vth5gnHxgnzjHSUO8r7IZiouB6DAciiVUAryV6MQm5trwIXNo0QDwFxyX99exIwUlDu3OzhZHKKbb721hCID17AWZMAQIgxQdu6b27s5YgJXsaxXWvEO2lSRVOnVXoCSI7mK5St%2FCJ8O1OdXivNIQ%3D%3D+noname%0D%0A"
+        )
+        # Then page return without error
+        self.assertStatus(200)
+        # Then ssh key is not added
+        self.assertEqual(0, len(list(user.authorizedkeys)))
+
     def test_delete(self):
         # Delete existing keys
         user = self.app.store.get_user('admin')
         for key in user.authorizedkeys:
             user.delete_authorizedkey(key.fingerprint)

Vulnerable code:         if action == "add":
        elif action == 'delete':
Fixed code: ## 2.4.3 (2022-09-14)

This releases include a security fix. If you are using an earlier version, you should upgrade to this release immediately.

* Mitigate CSRF in profile's SSH Keys #212

        delete_form = DeleteSshForm()
        if action == "add" and form.is_submitted():
        elif action == 'delete' and delete_form.is_submitted():
    def test_add_get_method(self):
        # Given an authenticated user
        user = self.app.store.get_user('admin')
        # When querying a page with parameters (HTTP GET)
        self.getPage(
            "/prefs/sshkeys?action=add&title=ssh1&key=ssh-rsa+AAAAB3NzaC1yc2EAAAADAQABAAAAgQCzurRNVKwb0ZJCmUgGenoe4vth5gnHxgnzjHSUO8r7IZiouB6DAciiVUAryV6MQm5trwIXNo0QDwFxyX99exIwUlDu3OzhZHKKbb721hCID17AWZMAQIgxQdu6b27s5YgJXsaxXWvEO2lSRVOnVXoCSI7mK5St%2FCJ8O1OdXivNIQ%3D%3D+noname%0D%0A"
        )
        # Then page return without error
        self.assertStatus(200)
        # Then ssh key is not added
        self.assertEqual(0, len(list(user.authorizedkeys)))