VNX-565: Reliance on Cookies without Validation and Integrity Checking
A security decision is taken on a plain, unsigned cookie that names identity or privilege (role, admin, user_id, username...), or such a cookie is written without a signature, in JavaScript/TypeScript, Python and PHP.
| Field | Value |
|---|---|
| Rule ID | VNX-565 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript, PHP, Python |
| CWE | CWE-565 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | cookie-auth cwe-565 |
| Source | internal/sast/rules/vnx-565.rego |
Overview
VNX-565 maps to CWE-565: Reliance on Cookies without Validation and Integrity Checking. It reports code that trusts a cookie the client can set to anything: a role, an admin flag, a user id or a user name read from a plain cookie and used to decide access or to look up an account.
Why This Matters
Cookies are request input. Anyone can open the browser’s developer tools, or use curl, and send role=admin or userid=1. Unless the server signed the value and checks the signature, or keeps the fact in a server-side session, a cookie says nothing about who the user is.
What Gets Flagged
const userId = req.cookies.userId; // FLAGGED: used for the lookup below
const user = await User.findByPk(userId);
res.cookie('user_id', user.id); // FLAGGED: written unsigned
obj = Account.objects.filter(userid=request.COOKIES['userid'])[0] # FLAGGED
$role = isset($_COOKIE['user_role']) ? $_COOKIE['user_role'] : 'guest'; // FLAGGED
setcookie('user_role', 'guest', time() + 3600, '/'); // FLAGGED
An identity cookie written unsigned is flagged too (res.cookie('username', user.name), setcookie('user_id', ...)).
Not flagged: signed cookies (req.signedCookies, ctx.cookies.get(name, { signed: true }), res.cookie(..., { signed: true }), Django get_signed_cookie/set_signed_cookie), a value verified on the same or next two lines (jwt.verify, itsdangerous loads), session-id, CSRF and preference cookies, cookies that are only displayed, test files, comments and string literals. VNX-472 also reports privilege fields read from req.cookies in JavaScript.
Reported by another rule, not by VNX-565:
if (req.cookies.role === 'admin') { ... } // VNX-784: a security decision on a cookie
res.cookie('role', user.role, { httpOnly: true }); // VNX-642: authorization state written to the client
if request.COOKIES.get('admin') == "1": # VNX-784
return HttpResponse("welcome admin")
html.set_cookie("admin", "1", max_age=200) # VNX-642
A cookie named for a privilege or login state (admin, role, is_staff, logged_in, access_level…) that is compared or tested on its line, or assigned to a variable that a condition uses within 15 lines, is reported by VNX-784. An Express res.cookie() or Python set_cookie() of a cookie whose whole name is a privilege flag or role is reported by VNX-642. Each problem is reported once, by the rule that names it most precisely. VNX-565 keeps lookups, identity cookies (user_id, uid, username, email), PHP setcookie() and the other shapes.
Remediation
- Keep identity and privilege in a server-side session, keyed by an unguessable session id.
- If a cookie must carry them, sign it (
cookie-parsersecret withsigned: true, Djangoset_signed_cookie, itsdangerous) or use a verified token (JWT withjwt.verify), and reject values that fail the check. - Re-check the role against the user record on the server before any privileged action.
References
- CWE-565: Reliance on Cookies without Validation and Integrity Checking
- OWASP Session Management Cheat Sheet
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-565
// nosec vnx-565
Or record an ignore rule: vulnetix ignore add --rule vnx-565 --reason "...". See Suppressing findings.