How to Not Expose the Personal Data of 19,577 Canadians

A few months ago I spoke with CBC news to give an overview of a plethora of security flaws I found in PORTpass, an attempt to bring digital proof-of-vaccine to Canadians. While that article was meant for a broader audience, today I will outline their security flaws from a technical perspective for all the developers out there.

My Background

My name is Rida F’kih, I am a 21 year old Canadian software developer & reverse engineer at MaxRewards.

My development journey started at 15, from reverse-engineering to scammer-vigilantism then teaching coding to kids to web & mobile development.

What was PORTpass?

PORTpass was a private attempt by a local Calgarian entrepreneur to bring vaccine passports to Canadians.

The Data At Risk

The data at risk wasn’t light stuff.

PORTpass had the business requirement of collecting loads of personal data, collecting photos of government IDs, health card numbers, verification selfies, full names, dates of births, blood types, and more.

The data on PORTpass would be enough to be at risk of social engineering, or even have financial accounts opened in your name.

What went wrong?

First, we must familiarize ourselves with Murphy’s Law.

Murphy’s law is an adage or epigram that is typically stated as: “Anything that can go wrong will go wrong.”

— Wikipedia, Murphy’s Law

I’d argue that PORTpass was the epitome of Murphy’s Law from the perspective of the user, and criminal negligence on behalf of its creator due to their inaction after these issues were brought to light.

The Vulnerability

The primary vulnerability is called “Improper Access Control,” which is described by the Common Weakness Enumeration — a community-developed list of software & hardware weakness types — as “software [that] does not restrict or incorrectly restricts access to a resource from what should be an unauthorized actor.”

Some pseudo-code to describe a properly managed & created endpoint may appear as follows.

import express from "express";
import { loggedInUser } from "@/utils/authentication";
import { getUserInfo } from "@/utils/users";
const app = express();app.get("/user/:userId", (request, response) => {
const { userId } = request.params;
const authenticatedUserId = loggedInUser(request);

if (!authenticatedUserId || authenticatedUserId !== userId)
return response.status(401).send("Unauthorized");
else
return response.status(200).json(getUserInfo(userId));
});

Here, we would send a GET request to /user/[userId], and receive back user data only if we are authenticated into the same user we are requesting.

A poorly architected endpoint may be coded as follows.

app.get("/user/:userId", (request, response) => {
const { userId } = request.params;
if (!loggedInUser(request))
response.status(401).send("Unauthorized");
else
response.status(200).json(getUserInfo(userId));
});

Here, we’re not checking which user is authenticated, only if the user is authenticated. Thus, an authenticated user could request ANY resource, even if it doesn’t belong to them. For public posts this is appropriate, for private information like government-issued photo IDs, health care cards, and confirmation portraits, this is a massive flaw.

The Catalysts

Sequential User IDs

PORTpass users requested their resource by using sequential user IDs. This means that if you were the first user to sign up for their service, your ID would be 1, if you were the second, it would be 2, etc.

Had they used a cryptographic solution — such as UUIDv4 — scraping user information would have been significantly more difficult, however in this case formulating a script to collect all registered user data was trivial.

/**
* Gets the user profile from the PortPass API.
* @param {number|string} userId The user ID of the target user.
* @returns {Promise<any>} A user object.
*/
const getUserProfile = async (userId) => {
const profile = await axios
.get(USER_PROFILE_URI + userId.toString())
.then((response) => response?.data)
.catch(() => ({}));
return profile;
};
for (let i = 0; i < 19577; i++)
getUserProfile(i + 1).then(saveUserData);

No SSL

Since PORTpass didn’t have SSL certificates, packets that your device sends or receives could be altered or intercepted. With websites, its easy to identify a secure website by finding the little “lock icon” next to the URL, however with applications its a little more ambiguous.

Exposing Developer Tooling

PORTpass’ backend was created using Django, which has an interface which allows you to test endpoints from your browser without having to use REST tooling like Postman, or Insomnia. Optimally, PORTpass would have secured their endpoints so knowing their paths would provide no additional capabilities, however, they didn’t, and thus traversing their entire backend was trivial.

Conclusion

We all want to get our projects out there, but if we don’t have the experience necessary to accommodate for basic security considerations, getting your code reviewed or learning the basics of security should be considered good options.

In this case, a non-technical founder wanted to hastily get a product out there, so they hired a developer from overseas without the ability to evaluate the quality of code.

I’m very thankful to the CBC for their help in finally being able to hold the creator of PORTpass accountable, an address these concerns rather than simply ignoring them or claiming to be defamed.

Bart Simpson displays the same reaction I did.

Did you enjoy this content? You can read more posts like this by visiting my personal portfolio, and you can follow me on Twitter to get updates when I create new articles.

Click here to read this post on my blog.

--

--

--

21 year old software developer & reverse engineer.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

TryHackMe Advent of Cyber 3 → DAY 4

{UPDATE} Solitaire Chronicles Hack Free Resources Generator

{UPDATE} لغز وكلمة Hack Free Resources Generator

My FCC Comments

Objectivity

Neptune Mutual Social Media Competition

CovidSafe App — Part 1

Claim Your ENS Token on TokenPocket

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Rida F'kih

Rida F'kih

21 year old software developer & reverse engineer.

More from Medium

How to store user passwords in a database?

Monitoring API Call Retries

How to use Elasticsearch for a rapid Auto-Complete on a Flutter Widget (Also featuring Kibana and…

The Best CI/CD Tools for Mobile App Developers in 2022