Introduction
The src/main/server/routes/index.js file handles critical data modification operations for this web service, but a flaw in the /changeData endpoint at line 54 created a severe security risk. The route handler blindly accepted any incoming POST request and passed the request body directly to the changeData() function without verifying who was making the request or where it originated from.
Here's the vulnerable code pattern:
router.post("/changeData", function (req, res) {
res.send(changeData({ ...req.body }));
});
This three-line handler represents a textbook example of missing authorization—any attacker on the internet could send a crafted request to modify data they shouldn't have access to. For developers building Express.js APIs, this vulnerability demonstrates why every state-changing endpoint needs proper access controls.
The Vulnerability Explained
The vulnerability stems from the /changeData endpoint's complete lack of origin validation or authorization checks. When a POST request arrives at this endpoint, the handler immediately destructures req.body and passes it to the changeData() function, which presumably modifies application or user data.
What Made This Dangerous
The problematic code:
router.post("/changeData", function (req, res) {
res.send(changeData({ ...req.body }));
});
Three critical security controls were missing:
- No origin verification – The endpoint didn't check whether requests came from the legitimate application or a malicious third-party site
- No user authentication – No verification that the requester was a logged-in user
- No authorization check – No validation that the user had permission to modify the specific data in the request
Attack Scenario
An attacker could exploit this vulnerability with a simple cross-origin request:
POST /changeData HTTP/1.1
Host: localhost:30088
Content-Type: application/json
Origin: https://evil-attacker.com
{
"type": "userSettings",
"userId": "victim-user-123",
"data": {
"email": "attacker@evil.com",
"role": "admin"
}
}
Since the endpoint performed no validation, this request would be processed identically to a legitimate request from the application itself. The attacker could:
- Modify other users' account settings
- Escalate privileges by changing role assignments
- Corrupt application configuration data
- Potentially achieve account takeover by changing email addresses
Because this is a web service with publicly accessible route handlers, any remote attacker could craft and send these malicious requests.
The Fix
The fix adds origin validation to ensure requests only come from trusted local sources. Here's the before and after comparison:
Before (Vulnerable)
router.post("/changeData", function (req, res) {
res.send(changeData({ ...req.body }));
});
After (Fixed)
router.post("/changeData", function (req, res) {
const origin = req.headers.origin || req.headers.referer || "";
if (origin && !/^(file:|http:\/\/localhost|http:\/\/127\.0\.0\.1)/.test(origin)) {
return res.status(403).json({ success: false, message: "Forbidden" });
}
res.json(changeData({ ...req.body }));
});
How the Fix Works
-
Extract origin information: The code retrieves the
Originheader first, falling back toRefererif not present, or an empty string as a default -
Regex-based validation: The pattern
/^(file:|http:\/\/localhost|http:\/\/127\.0\.0\.1)/only allows requests from:
-file:protocol (local file access, typically Electron apps)
-http://localhost(local development)
-http://127.0.0.1(local loopback) -
Reject unauthorized origins: If the origin exists and doesn't match the whitelist, the endpoint immediately returns a
403 Forbiddenresponse with a JSON error message -
Response format improvement: The fix also changes
res.send()tores.json()for consistent JSON response formatting
This approach ensures that only requests originating from the local machine can modify data, blocking all cross-origin attacks from remote sources.
Prevention & Best Practices
Implement Defense in Depth
Origin validation is one layer, but robust security requires multiple controls:
// Example: Layered security approach
router.post("/changeData",
authenticateUser, // Verify user identity
authorizeDataAccess, // Check permissions
validateOrigin, // Verify request source
validateRequestBody, // Sanitize input
function (req, res) {
res.json(changeData({ ...req.body }));
}
);
Use Middleware for Consistent Enforcement
Create reusable middleware for origin validation:
const validateLocalOrigin = (req, res, next) => {
const origin = req.headers.origin || req.headers.referer || "";
const trustedPattern = /^(file:|https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?)/;
if (origin && !trustedPattern.test(origin)) {
return res.status(403).json({ error: "Forbidden" });
}
next();
};
// Apply to sensitive routes
router.post("/changeData", validateLocalOrigin, changeDataHandler);
Additional Recommendations
- Implement CSRF protection – Use tokens for all state-changing operations
- Add authentication middleware – Verify user identity before processing requests
- Implement authorization checks – Validate that users can only modify their own data
- Use allowlists over denylists – Explicitly permit known-good origins rather than blocking known-bad ones
- Log rejected requests – Monitor for attack patterns and attempted exploits
Key Takeaways
- The
/changeDataendpoint processed all requests without any authorization, making it trivially exploitable by remote attackers - Origin headers can be spoofed by non-browser clients, so origin validation should be combined with authentication for sensitive operations
- The regex pattern
^(file:|http:\/\/localhost|http:\/\/127\.0\.0\.1)restricts access to local sources only, which is appropriate for this application's architecture - Changing from
res.send()tores.json()ensures consistent API response formatting, improving client-side error handling - Every state-changing endpoint needs explicit access controls—never assume requests are legitimate just because they reach your server
How Orbis AppSec Detected This
- Source: HTTP request body (
req.body) containing user-controlled data - Sink:
changeData({ ...req.body })insrc/main/server/routes/index.js:54 - Missing control: No origin validation, authentication, or authorization checks before processing the data modification request
- CWE: CWE-346 (Origin Validation Error)
- Fix: Added regex-based origin header validation to restrict requests to trusted local sources (
file:,localhost,127.0.0.1), returning 403 Forbidden for unauthorized origins
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability in the /changeData endpoint demonstrates how a missing origin check can expose critical functionality to remote attackers. The fix implements a straightforward but effective validation pattern that restricts access to trusted local sources.
For developers building Express.js APIs, remember that every endpoint handling sensitive operations needs explicit access controls. Origin validation is a useful layer of defense, but should be combined with proper authentication and authorization for comprehensive security. When in doubt, apply the principle of least privilege—deny by default and explicitly permit only what's necessary.