DEV Community

Cover image for How I Found and Fixed an Open Redirect Vulnerability in My Startup
Tochukwu Nwosa
Tochukwu Nwosa

Posted on

How I Found and Fixed an Open Redirect Vulnerability in My Startup

While reviewing parts of the MyTreda codebase recently, I came across a security issue that wasn't causing any visible problems. The app worked exactly as expected: users logged in and got redirected back to where they meant to go. Everything seemed fine.

But after taking a closer look, I realized there was a hidden security risk sitting inside the authentication flow.

The Setup

Like many web applications, MyTreda allows users to be redirected after authentication. A typical flow looked something like this:

/auth/login?callbackUrl=/dashboard
Enter fullscreen mode Exit fullscreen mode

After a successful login, the application would redirect the user to the URL specified in callbackUrl. This creates a smoother user experience because users return directly to the page they were trying to access. The implementation seemed straightforward.

The Problem

The issue was that the application trusted the value provided in callbackUrl. That meant an attacker could potentially create a URL like this:

/auth/login?callbackUrl=https://malicious-site.com
Enter fullscreen mode Exit fullscreen mode

The flow would look like this:

  1. The user clicks the link.
  2. The user sees the legitimate MyTreda login page.
  3. The user logs in successfully.
  4. The application redirects them to the attacker's website.

The login is real, and the domain is trusted. The only part that isn't legitimate is where you actually end up.

This type of vulnerability is known as an Open Redirect.

Why Open Redirects Matter

At first glance, an open redirect may not seem serious. After all, the attacker isn't gaining access to your database or bypassing authentication.

But open redirects are often used in phishing attacks. An attacker can leverage trust in your domain to convince users that a malicious link is legitimate.

Instead of sending users directly to:

https://malicious-site.com
Enter fullscreen mode Exit fullscreen mode

they can send:

https://mytreda.com/auth/login?callbackUrl=https://malicious-site.com
Enter fullscreen mode Exit fullscreen mode

Many users will trust the second URL because it starts with a legitimate domain. That trust can be exploited.

How I Found It

I wasn't responding to a bug report or investigating a security incident. I was simply reviewing old code.

As products evolve, it's easy for assumptions made during development to remain unnoticed. In this case, the assumption was:

The callback URL will always be one of our routes.

Unfortunately, assumptions are not validation. Any value supplied by a user should be considered untrusted until proven otherwise.

The Fix

The solution was simple. Instead of allowing arbitrary URLs, I restricted redirects to internal application routes only.

For example:

✅ Allowed:

/dashboard
/products
/settings
Enter fullscreen mode Exit fullscreen mode

❌ Rejected:

https://malicious-site.com
http://example.com
//attacker-site.com
Enter fullscreen mode Exit fullscreen mode

One particularly important case was rejecting URLs that start with //. Although they look like relative paths, browsers interpret them as protocol-relative URLs that point to external domains.

A simplified version of the validation looked like this:

function isValidCallbackUrl(url: string): boolean {
  return url.startsWith('/') && !url.startsWith('//');
}
Enter fullscreen mode Exit fullscreen mode

Now users can only be redirected to routes within the application.

Lessons Learned

This bug reminded me of something important: not every important issue breaks functionality.

The application worked perfectly. Users weren't reporting problems, and nothing looked wrong from the outside. Yet there was still a security vulnerability waiting to be exploited.

When maintaining software, it's easy to focus on:

  • features
  • performance
  • bug fixes
  • UI improvements

But security often lives in the assumptions we forget to question, and sometimes the most valuable fixes are the ones users never notice.

Final Thoughts

Shipping features is only half the job. The other half is revisiting decisions you already made.

As MyTreda grows, I'm spending more time auditing existing code, improving security, and reducing risk alongside shipping new features.

This fix took only a few minutes to implement. Finding it required slowing down and looking at the code with fresh eyes.

And sometimes, that's where the most important improvements come from.


Have you ever found a security issue in your own code that had been hiding in plain sight? I'd love to hear about it in the comments.

Top comments (0)