JobToday and it's endpoints

JobToday is a website to find blue collar jobs easily using features like location based search of candidates and in app chat.

We will exploit this location feature to get the exact coordinates of an arbitrary user.


The API

We can query the endpoint https://api.jobtoday.com/v1/seekers with the following data.

interface GetParams {
experienceInMonthsFrom?: string;
experienceInMonthsTo?: string;
keyword: string;
lat: string;
lng: string;
pageIndex: string;
pageSize: string;
sortBy?: string;
toRadius: string;
};

In the intercepted calls from the Website it also sends a few tokens in the header, surprisingly they are not used to authenticate or otherwise and we can query directly the api.

We then get a list of results that match the keyword param. This can interestingly be the name of the candidate or some field on the profile.

The response has the following format

interface Response {
meta: JobSeekersMeta;
data: Array<JobSeeker>;
};
interface JobSeekersMeta {
code: number;
errors: Array<any>;
totalRecords: number;
pageSize: number;
pageIndex: number;
requestSpecificData: any;
};
interface JobSeeker {
distance: number,
languagesWithLevels: Array<any>,
id: number,
status: number,
userType: number,
firstname: string,
lastname: string,
avatarUrl640: string;
avatarUrl160: string;
lastOnline: number;
experienceLength: number;
previousExperience: Array<any>;
address: string;
education: string;
languages: string;
about: string;
createDate: number;
countryCode: string;
hasPhone: boolean;
chatUnlocked: boolean;
isPhoneVerified: boolean;
city: string;
employmentTypes: Array<any>;
online: string;
avatarUrl: string;
profileImagesUrls: Array<string>;
key: string;
isBanned: boolean;
}

Finding the users

On the response object we get a list of users that include the field distance this is the distance in matters the user is from the coordinates sent in the request GetParams.lat, GetParams.lng so for every user we get a radios of where they are.

To optimise the smallest radios for all users I made a script that would query the API for #50000 users, then it would save this user list to a sqlite db including the coordinates of the request. It will then move the request coordinates to a close point and do the same query, with this new response it would match the users and find the ones that got closer and update the coordinates for those users accordingly.

After a running a couple hours in an area of Seville, Spain (~6km^2) if found ~11,000 users and estimate an average location of each user to a radius of ~2km. This is not particularly useful but we can optimise it much further if we focus on a single user.

If we triangulate the position of a user recursively getting three new coordinates close to the user radius we can reduce the distance to around 10 meters in ~10 minutes

You can find the code used here:

Recommendations

  • Implement ip based rate limiting

Over the course of a few hours a make in the range of ~2k requests coming from the same ip. This could be easily limited to avoid attack like this.

  • Implement auth

There was no kind of user auth needed to query candidate data. Requiring a token an validate that the user is not banned and that the account type is recruiter will make this harder and easier to identify

  • Limit page size to 50

On the request GetParams the parameter pageSize is a number that tells the server how many results to send back. In the original request it uses a reasonable number of 50, nonetheless I have been able to query up to 5000 users in a single request, taking around 20 seconds to complete and 1.4mb of size

  • Reply min distance of 500m

There is no minimum on how close the distance can get and thus you can retrieve the exact location the user used. There is no use for a location closer than 0.5km and it will give the user much better privacy added with the next recommendation

  • Fuzzy user coordinates

GPS coordinates are accurate around 20 meters so if we set a minimum radius of 0.5km we should create a fuzzed version on the location in a random point inside this radius. And send this location when requested publicly by the API

  • Restrict search criteria not to include names

I found odd that you can search candidates by names, this allowed me to find friends by first name and validate the location the previous steps found, noteless I don’t see any reason why a employer would need that.