Cross-Site Request Forgery (CSRF)
Summary
BDServer lacks CSRF protection and uses GET requests for state-changing operations. This combination allows attackers to perform unauthorized actions via image tags, links, or email content without requiring JavaScript or form submissions. Additional vulnerable endpoints likely exist given the prevalence of this pattern throughout the codebase.
Vulnerability Details
| Property | Value |
|---|---|
| Type | Cross-Site Request Forgery (CSRF) via HTTP Method Misuse |
| CVSS | 7.4 (High) |
| CWE | CWE-352 (Cross-Site Request Forgery), CWE-749 (Exposed Dangerous Method) |
Root Cause
BDServer uses GET requests for operations that modify server state. This violates HTTP semantics where GET should be idempotent and only POST/PUT/DELETE should modify data. Combined with no CSRF protection (no tokens, no SameSite cookies, no origin validation), this allows attackers to trigger state changes via simple image tags or links.
The vulnerable endpoints are handled in do_GET:
| Endpoint | Action |
|---|---|
/comment |
Create or edit comments |
/delete_comment |
Delete comments |
/update_account |
Modify user profile |
/grant_publication_rights |
Grant or revoke permissions |
/create_invite |
Generate invite codes |
/cancel_invite |
Delete invite codes |
/log_out |
Terminate sessions |
/sign_petition |
Sign petitions |
/read_notification |
Delete notifications |
/do_install_plugin/ |
Install plugins and restart server |
/do_delete_plugin/ |
Delete plugins |
Forms in the application don't specify method="POST", so they default to GET. For example, from Render.py:1828:
<form action="update_account">
Without a method attribute, forms default to GET.
Action buttons are implemented as <a> links rather than form submissions. The Button() helper generates anchor tags, so clicking "Log Out" or "Delete" triggers a GET request:
<a class="button" href="/log_out?cookie=...">Log Out</a>
Traditional CSRF requires JavaScript to auto-submit forms. GET-based CSRF is more dangerous because it works without JavaScript, requires no user interaction beyond viewing a page, works in HTML emails, and bypasses most Content Security Policies since browsers don't block image loads.
Reproducing the Vulnerability
These examples assume a local test server running on localhost:8080. The attack works by embedding image tags that trigger GET requests when a logged-in user views the page.
Profile Modification
An attacker can modify a victim's profile by having them view a page containing:
<img src="http://localhost:8080/update_account?bio=Account%20Compromised&website=https://evil.com" style="display:none" />
When a logged-in user views this page, their profile bio and website are silently changed. The image tag triggers an automatic GET request with the victim's session cookie.
Comment Deletion
Multiple comments can be deleted by chaining image tags:
<img src="http://localhost:8080/delete_comment?url=/articles/Welcome_To_BDServer&number=0" />
<img src="http://localhost:8080/delete_comment?url=/articles/Welcome_To_BDServer&number=1" />
Admin Plugin Manipulation
If an administrator visits a malicious page, an attacker can manipulate server plugins:
<img src="http://localhost:8080/do_delete_plugin/plugin_hash" />
<img src="http://localhost:8080/do_install_plugin/malicious_plugin_hash" />
This can delete security plugins and install malicious ones, triggering a server restart.
Proof of Concept
- Log into BDServer at
http://localhost:8080and note the current profile settings - Open
poc.htmlin the same browser - Return to BDServer and check the profile: the bio and website fields have been changed
The PoC embeds an invisible image tag that triggers a GET request to /update_account. The browser sends the session cookie with the request, authenticating the action.
Remediation
Use POST for State-Changing Operations
RFC 9110 defines GET as a "safe" method: it should only retrieve data, never modify it. Browsers rely on this assumption. They prefetch links, cache responses, allow embedding in image tags, and let users bookmark URLs. These behaviors are fine for reading data but cause problems when GET requests delete comments or modify profiles.
POST exists specifically for operations that change state. Browsers handle POST differently: they don't prefetch it, don't cache it, and can't trigger it from an image tag. Switching to POST eliminates the attack vectors described in this report.
Move all state-modifying endpoints from do_GET to do_POST in Run.py. Update forms to specify method="POST" and replace link-based buttons with form submissions.
Implement CSRF Tokens
CSRF tokens provide defense in depth beyond using POST. The server generates a random token for each session and embeds it in forms as a hidden field. When the form is submitted, the server verifies the token matches the session. An attacker cannot forge requests without first obtaining the victim's token, which requires a separate vulnerability like XSS.
Set Secure Cookie Attributes
The session cookie currently lacks security attributes (Render.py:80). Setting SameSite=Strict prevents browsers from sending cookies with cross-site requests, providing defense in depth against CSRF. Modern browsers default to SameSite=Lax which provides some protection, but this should not be relied upon.
Disclaimer
This assessment was performed on a best-effort basis against BDServer commit dc86854 and reflects the state of the software at the time of testing. The findings and remediations are provided for informational purposes and should be independently validated before implementation. This report does not guarantee all vulnerabilities have been identified, nor does it guarantee the suggested fixes will be effective in all environments.