Files
lmxopcua/code-reviews/Security/findings.md
T
Joseph Doherty d23e585cdb review(Security): fix login open-redirect (High) + stale LDAP doc
Code review at HEAD 7286d320. Security-001 (High): guard returnUrl with a local-URL
check before redirect (open-redirect/phishing vector) + regression test. Security-002:
update stale LdapOptions dev-LDAP doc reference.
2026-06-19 10:22:59 -04:00

3.9 KiB

Code Review — Security

Field Value
Module src/Server/ZB.MOM.WW.OtOpcUa.Security
Reviewer Claude Code
Review date 2026-06-19
Commit reviewed 7286d320
Status Reviewed
Open findings 0

Checklist coverage

A comprehensive review completes every category, recording "No issues found" where a category produced nothing rather than leaving it blank.

# Category Result
1 Correctness & logic bugs Security-001 (open redirect on successful login)
2 OtOpcUa conventions No issues found
3 Concurrency & thread safety No issues found
4 Error handling & resilience No issues found
5 Security Security-001 (open redirect), Security-002 (stale doc)
6 Performance & resource management No issues found
7 Design-document adherence No issues found
8 Code organization & conventions No issues found
9 Testing coverage Security-001 was untested (regression test added)
10 Documentation & comments Security-002 (stale LdapOptions comment)

Findings

Security-001

Field Value
Severity High
Category Security
Location src/Server/ZB.MOM.WW.OtOpcUa.Security/Endpoints/AuthEndpoints.cs:135
Status Resolved

Description: The /auth/login endpoint (form POST path) redirects to returnUrl on successful authentication without validating that the URL is local to the application. An attacker can craft a phishing link such as https://admin.example.com/login?returnUrl=https%3A%2F%2Fevil.com — the shared LoginCard component echoes the value verbatim into the hidden returnUrl form field (the LoginCard.razor security comment explicitly states: "the consuming app's POST handler MUST validate it is a local/relative URL before redirecting to prevent open-redirect"). After a successful bind the endpoint issues Results.Redirect("https://evil.com"), silently forwarding the authenticated user to an attacker-controlled site.

The RedirectToLogin.razor Blazor component's own returnUrl generation is safe (Nav.ToBaseRelativePath always produces a relative path), but nothing prevents an attacker from directly constructing the URL. The normal cookie-auth challenge path also produces only relative paths. The gap is the absence of server-side local-URL validation on the consumed form field before redirecting.

Recommendation: Replace Results.Redirect(returnUrl) with a local-URL guard: use Uri.IsWellFormedUriString(returnUrl, UriKind.Relative) to validate the URL is relative before redirecting; fall back to "/" for absolute or malformed values. Results.LocalRedirect could alternatively be used but throws on invalid input (which the endpoint would need to catch).

Resolution: Fixed 2026-06-19. Added IsLocalUrl helper that rejects absolute and protocol-relative URLs; success-path now falls back to "/" on invalid input. Regression test Login_with_absolute_returnUrl_does_not_open_redirect added to AuthEndpointsIntegrationTests.


Security-002

Field Value
Severity Low
Category Documentation & comments
Location src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/LdapOptions.cs:9
Status Resolved

Description: The XML doc comment on LdapOptions references C:\publish\glauth\auth.md as the source for dev LDAP defaults. Per CLAUDE.md (§ LDAP Authentication) the per-VM NSSM GLAuth at C:\publish\glauth\ is obsolete — the shared GLAuth now runs on the Linux Docker host at 10.100.0.35:3893. A developer following the stale reference would look for a file that no longer exists on the machine.

Recommendation: Update the doc comment to remove the stale local-path reference and direct readers to docs/security.md and CLAUDE.md §"LDAP Authentication" for dev LDAP setup.

Resolution: Fixed 2026-06-19. Stale C:\publish\glauth\auth.md reference replaced with pointer to docs/security.md.