Introduction#

Meet Me is a dating app that I built in a team of five for a software engineering course. The app lets users:

  • create an account
  • add photos
  • match with other users
  • chat with each other
  • report users

We did not focus heavily on security during the project, but we still followed several good practices we learned during our studies.

The application uses Symfony, which already provides useful security features such as:

  • password hashing
  • secure authentication
  • CSRF protection
  • other built-in safeguards

Once the project was finished, I decided to review it from a security point of view and look for possible vulnerabilities.

I found one. A serious one.

Quick overview of the app#

Home page#

Home page

Swipe page with notifications#

Swipe page with notifications

Messaging page#

Messaging page

Vulnerability summary#

Item Value
Vulnerability type Stored XSS
Entry point Username field
Where it is stored Database
Dangerous behaviour innerHTML used in notifications
Impact JavaScript execution in the victim’s browser

Root cause#

Before the fix, the username field was not validated correctly.

This meant an attacker could place an HTML or JavaScript payload directly inside their username. That value was then:

  1. stored in the database
  2. reused in different parts of the application
  3. injected back into the interface

The main issue came from the fact that the notification system used innerHTML. Because of that, the malicious username was interpreted as HTML instead of plain text, which allowed client-side JavaScript execution.

In other words, this was a stored XSS vulnerability.

Example of exploitation#

1. The attacker puts the payload in their username#

The attacker registers with a malicious username containing the payload.

Payload inside the username field

2. The attacker interacts with a victim#

Then the attacker only needs to match with the victim or send a message after a match. Payload visible in chat

3. The payload executes on the victim side#

When the malicious content is rendered through the vulnerable notification logic, the JavaScript runs in the victim’s browser.

Payload executed on the victim side

Possible impact#

This kind of XSS can be used to:

  • redirect the victim to a fake page
  • modify the interface
  • run actions in the victim’s session
  • steal data depending on the surrounding context and protections in place

Payload used for the proof of concept#

<img src=x onerror="document.body.style.filter='hue-rotate(180deg)';document.body.insertAdjacentHTML('afterbegin','<div style=&quot;position:fixed;top:0;left:0;right:0;z-index:99999;padding:14px;background:#111;color:#0f0;font:700 18px monospace;text-align:center&quot;>-----POC-----</div>')">

Why it worked#

The attack worked because three things happened together:

  • user-controlled data was accepted without proper filtering
  • the value was stored and reused later
  • the application inserted that value with innerHTML instead of safe text rendering

Fix idea#

A proper fix is to combine several protections:

  • validate and sanitize user input
  • escape output correctly
  • avoid innerHTML for untrusted content
  • review every place where usernames are rendered

Final note#

This vulnerability was found after the end of the project during a personal security review.

It is a good example of a common mistake in web applications: even when a framework provides strong built-in protections. This type of vunerabilities can happen when you develop an app without a serious security audit.