Skip to content

🐛 Unauthenticated Attendee PII Exposure via Public Check-In List Endpoint #1224

Description

@geo-chen

(reported via email on 24 May - no response)

I am reporting a vulnerability in Hi.Events (develop branch, v1.8.0-beta) that exposes attendee personally identifiable information (PII) to anyone who knows a check-in list URL, with no additional authentication required.

Affected component: public check-in list endpoints (/api/public/check-in-lists/)
CWE: CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor)
CVSS 3.1 Score: 7.5 (High) -- AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Summary:

Hi.Events public check-in endpoints use a capability-URL pattern where the check-in list short_id (e.g., cil_MVHr4rojhEodi) serves as the sole access control. Anyone who knows this URL can retrieve the full attendee list for the event, including email addresses, first and last names, order IDs, and ticket details. No session token, API key, or login is required.

The same URL also grants the ability to create and delete check-in records, allowing an attacker to check attendees in or out without any additional credential.

Reproduction steps (tested against local Docker, port 9113):

  1. Obtain the check-in list short_id (from a shared QR code, link, browser history, or network capture).
  2. Send a GET request to /api/public/check-in-lists/{short_id}/attendees with no Authorization header.
  3. Receive HTTP 200 with a paginated list of all attendees including email, first_name, last_name, order_id, and product_id.
  4. Optionally, send POST /api/public/check-in-lists/{short_id}/check-ins with an attendee public_id to check them in (HTTP 200).
  5. Optionally, send DELETE /api/public/check-in-lists/{short_id}/check-ins/{check_in_short_id} to undo a check-in (HTTP 204).

All three operations succeed with no authentication token.

Observed response (step 2):

{"data":[{"id":1,"email":"[victim@example.com](mailto:victim@example.com)","first_name":"Victim","last_name":"User","public_id":"A-IEZK8PB","product_id":1,"status":"ACTIVE","order_id":1,"check_in":null}]}

Root cause:

The public check-in routes in backend/routes/api.php have no auth:api middleware. The AttendeeWithCheckInPublicResource includes email, first_name, last_name, and order_id in its output. There is no secondary secret or PIN required to access the attendee list beyond the URL itself.

The short_id entropy (62^13, approximately 2^77 bits) provides brute-force resistance, but the URL is routinely shared with all event volunteers and may appear in browser history, logs, screenshots, or network captures.

Recommended fix options:

Option 1 (minimal change): Remove email from AttendeeWithCheckInPublicResource so that email addresses are not returned on the public endpoint. Other fields (first_name, last_name, public_id) may be acceptable for check-in staff.

Option 2 (stronger): Introduce a separate PIN or short-lived token required to retrieve the attendee list, keeping the capability URL only for check-in operations that do not return PII.

Option 3: Add a per-check-in-list configurable setting that lets organizers control whether the attendee list (including email) is exposed on the public endpoint.

Impact:

Attendee email addresses and full names are personal data under GDPR and similar regulations. An organizer may not have consent to share this data with anyone who obtains the check-in URL. The exposure is silent (no log visible to the organizer) and affects all events that use the check-in list feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions