VYPR
High severityNVD Advisory· Published Dec 22, 2022· Updated Apr 9, 2025

Cross-Site Request Forgery (CSRF) in ikus060/rdiffweb

CVE-2022-4646

Description

Cross-Site Request Forgery (CSRF) in GitHub repository ikus060/rdiffweb prior to 2.5.4.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2022-4646 is a CSRF vulnerability in rdiffweb prior to 2.5.4, allowing attackers to perform unauthorized actions like forced logout without user consent.

Root

Cause

rdiffweb versions prior to 2.5.4 lack CSRF verification on the /logout endpoint. The fix, introduced in commit e6f0d8002129, changes the logout request from GET to POST and adds CSRF token validation [2]. Without this protection, an attacker can craft a malicious page or link that, when visited by an authenticated user, triggers a logout without the user's knowledge.

Exploitation

The vulnerability is exploited by tricking an authenticated rdiffweb user into clicking a crafted link or visiting a malicious page that performs a cross-site request. Since the original /logout endpoint accepted GET requests with no anti-CSRF token [2], the attacker's request would be processed as if initiated by the legitimate user. This requires no additional privileges beyond the user being logged into rdiffweb.

Impact

Successful exploitation forces the user to log out, causing a denial of service by interrupting their session. While a forced logout alone may seem minor, it can be combined with other attacks (e.g., capturing session tokens after the user logs in again) or cause inconvenience in environments where continuous access is critical. The vulnerability is classified as a Cross-Site Request Forgery (CSRF) with low severity [3].

Mitigation

Rdiffweb version 2.5.4, released on 2022-12-22, includes the fix by requiring POST requests with CSRF verification for the logout action [1][2]. Users should update to this version or later. The vulnerability was reported via huntr.dev and addressed in the same release [3].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
rdiffwebPyPI
< 2.5.42.5.4

Affected products

2
  • ghsa-coords
    Range: < 2.5.4
  • ikus060/ikus060/rdiffwebv5
    Range: unspecified

Patches

1
e6f0d8002129

Add CSRF verification on `/logout`

https://github.com/ikus060/rdiffwebPatrik DufresneDec 19, 2022via ghsa
10 files changed · +28 20
  • rdiffweb/controller/tests/test_controller.py+1 1 modified
    @@ -77,7 +77,7 @@ def test_static_files(self, path):
             """
             Check if the theme is properly configure.
             """
    -        self.getPage('/logout')
    +        self.getPage('/logout', method="POST")
             self.getPage(path)
             self.assertStatus(200)
             # Test with invalid method.
    
  • rdiffweb/controller/tests/test_page_admin_users.py+1 1 modified
    @@ -325,7 +325,7 @@ def test_delete_user_admin(self):
             """
             # Create another admin user
             self._add_user('admin2', '', 'pr3j5Dwi', '', UserObject.ADMIN_ROLE)
    -        self.getPage("/logout")
    +        self.getPage("/logout", method="POST")
             self.assertStatus(303)
             self.assertHeaderItemValue('Location', self.baseurl + '/')
             self._login('admin2', 'pr3j5Dwi')
    
  • rdiffweb/controller/tests/test_page_login.py+3 3 modified
    @@ -183,7 +183,7 @@ def test_login_twice(self):
     
         def test_login_persistent(self):
             # Given a user authenticated with persistent
    -        self.getPage('/logout/')
    +        self.getPage('/logout', method="POST")
             self.assertStatus(303)
             self.getPage(
                 '/login/', method='POST', body={'login': self.USERNAME, 'password': self.PASSWORD, 'persistent': '1'}
    @@ -312,7 +312,7 @@ class LogoutPageTest(rdiffweb.test.WebCase):
         def test_getpage_without_login(self):
             # Given an unauthenticated user
             # When Accessing logout page directly
    -        self.getPage('/logout')
    +        self.getPage('/logout', method="POST")
             # Then user is redirect to root '/'
             self.assertStatus('303 See Other')
             self.assertHeaderItemValue('Location', self.baseurl + '/')
    @@ -332,7 +332,7 @@ def test_getpage_with_login(self):
             self.getPage("/prefs/general")
             self.assertStatus('200 OK')
             # When logout
    -        self.getPage('/logout')
    +        self.getPage('/logout', method="POST")
             # Then a new session id is generated
             self.assertNotEqual(prev_session_id, self.session_id)
             # Then user is redirected to root page
    
  • rdiffweb/controller/tests/test_page_mfa.py+1 1 modified
    @@ -52,7 +52,7 @@ def setUp(self):
     
         def test_get_without_login(self):
             # Given an unauthenticated user
    -        self.getPage("/logout")
    +        self.getPage("/logout", method="POST")
             self.assertStatus(303)
             # When requesting /mfa/
             self.getPage("/mfa/")
    
  • rdiffweb/controller/tests/test_secure_headers.py+1 1 modified
    @@ -58,7 +58,7 @@ def test_cookie_with_https(self):
                 ('/invalid', 404),
                 ('/browse/invalid', 404),
                 ('/login', 301),
    -            ('/logout', 303),
    +            ('/logout', 405),
             ]
         )
         def test_cookie_with_https_http_error(self, url, expected_error_code):
    
  • rdiffweb/templates/layout.html+10 7 modified
    @@ -85,13 +85,16 @@
                       <i aria-hidden="true" class="fa fa-user"></i>
                       {{ username }}
                     </a>
    -                <div aria-labelledby="navbarDropdown" class="dropdown-menu">
    -                  <h6 class="dropdown-header">{% trans %}Signed in as{% endtrans %} {{ username }}</h6>
    -                  <a class="dropdown-item" href="{{ url_for('prefs', 'general') }}">{% trans %}Edit profile{% endtrans %}</a>
    -                  <a class="dropdown-item" href="{{ url_for('prefs', 'notification') }}">{% trans %}Notifications{% endtrans %}</a>
    -                  <div class="dropdown-divider"></div>
    -                  <a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a>
    -                </div>
    +                <form action="{{ url_for('logout') }}" method="post">
    +                  <div aria-labelledby="navbarDropdown" class="dropdown-menu">
    +                    <h6 class="dropdown-header">{% trans %}Signed in as{% endtrans %} {{ username }}</h6>
    +                    <a class="dropdown-item" href="{{ url_for('prefs', 'general') }}">{% trans %}Edit profile{% endtrans %}</a>
    +                    <a class="dropdown-item" href="{{ url_for('prefs', 'notification') }}">{% trans %}Notifications{% endtrans %}</a>
    +                    <div class="dropdown-divider"></div>
    +                    {# Logout button #}
    +                    <button type="submit" class="dropdown-item">{% trans %}Logout{% endtrans %}</button>
    +                  </div>
    +                </form>
                   </li>
                 </ul>
               </div>
    
  • rdiffweb/templates/mfa.html+6 4 modified
    @@ -11,10 +11,12 @@ <h2>{% trans %}Login Verification{% endtrans %}</h2>
               </p>
               {{ form }}
             </form>
    -        <a class="btn-link btn-sm btn-block text-center"
    -           href="{{ url_for('logout')}}">
    -          {% trans %}Login with a different account{% endtrans %}
    -        </a>
    +        {# Logout button #}
    +        <form action="{{ url_for('logout') }}" method="post">
    +          <button type="submit" class="btn btn-link btn-sm btn-block text-center">
    +            {% trans %}Login with a different account{% endtrans %}
    +          </button>
    +        </form>
           </div>
         </div>
       </div>
    
  • rdiffweb/test.py+1 1 modified
    @@ -176,7 +176,7 @@ def getJson(self, *args, **kwargs):
             return json.loads(self.body.decode('utf8'))
     
         def _login(self, username=USERNAME, password=PASSWORD):
    -        self.getPage("/logout")
    +        self.getPage("/logout", method="POST")
             self.getPage("/login/", method='POST', body={'login': username, 'password': password})
             self.assertStatus('303 See Other')
     
    
  • rdiffweb/tools/auth_form.py+2 0 modified
    @@ -104,6 +104,8 @@ def run(self, login_url='/login/', logout_url='/logout', persistent_timeout=4320
     
             # Clear session when browsing /logout
             if request.path_info == logout_url or request.path_info.startswith(logout_url):
    +            if request.method != 'POST':
    +                raise cherrypy.HTTPError(405)
                 self.logout()
                 raise cherrypy.HTTPRedirect('/')
     
    
  • README.md+2 1 modified
    @@ -111,7 +111,8 @@ Professional support for Rdiffweb is available by contacting [IKUS Soft](https:/
     ## Next Release - 2.5.4
     
     * 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
    +* Create proper symbolic link of `chartkick.js` on Ubuntu Jammy to fix loading of Charts in web interface
    +* Add CSRF verification on `/logout` credits to [reza.duty](https://rezaduty.me)
     
     ## 2.5.3 (2022-12-05)
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.