This issue was reported over a year ago by my colleague @Rythian2277 to Roblox via Hackerone and yet it still has not been fixed. I am open-sourcing my findings here due to the potential massive privacy violations that can take place if used maliciously.
The rest of this post is a copy-paste from GitHub.
Inspiration
Sometime last year (2021) I sought out to make an anti-exploit that would solve
the issue of exploiters just hopping on another account or alt and continuing
their exploiting. While in the shower I realized os.clock() provides a very
good way to achieve this. The function returns the number of seconds since the
CPU first started. On Windows machines this count would persist even after the
user shutdown their computer and only reset when the computer was restarted. The
odds of 2 players on a game having started their computer at the same exact time
in my opinion, very low. Just to ensure that no 2 players were accidentally
identified as the same computer more data points can be used such as if the
device has a touchscreen and other information provided by Roblox.
Shoutout to the guy
that tried banning a whole timezone while I was developing this.
Implementation
For the purpose of explanation the term session will refer to the time the
CPU is running up until restart.
Obtain some data points on the device. For this implementation the following
data points are used. It is important the CPU Start is used.
CPU Start: tick() - os.clock()
Timezone: os.date("%Z")
Is Daylight Savings Time: os.date("*t").isdst
Has Accelerometer: UserInputService.AccelerometerEnabled
Has Touchscreen: UserInputService.TouchEnabled
Organize the data points in a table, the structure of the table does not
matter as long as it is consistent.
Convert the table to a string, any method that can convert a table to a
string is acceptable. Easiest would be JSON encoding as Roblox provides HttpService:JSONEncode().
This step is optional but is used for easier storage of the data points.
Hash the string using any algorithm. This demonstration uses a SHA1 hash.
Demonstration
Using Rojo sync in the project
Playtest and check output for fingerprint hash
Logout and test on another account, both accounts should have the same hash
Restart the computer
Repeat steps 2 & 3. A new hash will be generated for the session.
Proposed Solution
A simple fix for this would be to have os.clock() return the time since the
Roblox client started rather than the CPU.
Potential
Experimented only on a small-scale with a handful of accounts the hashes and
user IDs of players can be linked together in a database where any user ID or
any hash when queried could return all associated user IDs and hashes.
If employed on a large game the people able to access the database would be able
to track players across multiple accounts even if no wrongdoing occurred. This
would be a a blantant violation of privacy.
This would only be the beginning of such a scenario. As many Roblox users also
use Discord and often these Discord servers use bots which verify users by
linking their Roblox account to their Discord. This now opens the possibility of
finding all of a Discord user’s Roblox accounts or vise versa. This would even
allow for somebody to potentially query Steam, Twitter, Twitch, Spotify
accounts, etc. as Discord allows users to link those accounts as well.
Would implementing this be against the ToS?
From my point of view, this is just a smart way of tracking players so there shouldn’t be any issues with Roblox, right?
I’ve seen countless instances of players making alt accounts just to secretly harass others and this could be a potential way for staff members to identify the main account
EDIT: From my point of view, this IS NOT against the ToS, as long as you do not share the unique identifiers. Below is a practical example:
NOT against the ToS: Saving the unique identifiers to a datastore, using them to generate a list of a player’s possible alt accounts and then sending the LIST OF ACCOUNTS to staff members via webhooks, data analytics, server-to-client etc
AGAINST the ToS: Doing the thing above but ALSO SENDING THE UNIQUE IDENTIFIERS.
PS: To comply with some contry’s laws you can also have that data auto delete every 30 days or so.
PS2: Implementing this in a game is UNTRACEABLE. So not matter what someone says, even if this turns out to be against the ToS, nobody will ever find out as long as you do not say anything.
If you wish to use this in a game to detect alt accounts of exploiters or troll’s etc, there is no way to detect this unless Roblox does an audit and checks your server scripts (or unless you confess)
(at the time the message above was written, there is/was no official confirmation that this IS or NOT against the ToS, so please take it with a grain of salt)
This is insane, I didn’t know this was even possible! This is a massive violation of privacy, however Roblox denies any help for cross-account banning so I’m 50/50 on this.
I don’t see Roblox fixing this issue anytime soon, they’ll just most likely hide anything relating to this if it becomes a larger issue.
This is very interesting. Never seen anything done like this before.
Would also using Localization:GetCountryRegionForPlayerAsync improve its efficiency? Might decrease the false positives if you can narrow it down to a users region.
This is actually really interesting. You’ve essentially made a method of digital fingerprinting using client-accessible API.
Although, what’s your reason behind using a SHA1 hash? You’re already JSON encoding the data so you could easily just compare the stored string. With SHA1 your only benefit is that the stored amount of data is smaller, but it’s insignificant in this case as you’re only storing a small number of fingerprint values. I feel like this system could be significantly more lightweight if you only compare the raw JSON strings.
VPNs would be tricked by the use of LocalizationService:GetCountryRegionForPlayerAsync(). Since the use of VPNs are more common than someone just changing their system time most players unless aware they were being tracked would not change their system just to avoid tracking. In the end, exploiters would be able to spoof all data points I can think of as they would have to be obtained on the client so I see this privacy vulnerability being more dangerous for innocent players.
Yeah but that can easily be outsmarted. You just don’t share the identifiers.
If someone requests that info, you generate a list of possible alt accounts without also providing the identifiers
This is not meant to be foolproof. It’s just a way to possibly identify harmful alt accounts for games.
What the developers do with this is their decision, i doubt anyone in their right mind would use this for their anti cheat or anything like that. This is simply something that can be turned into a moderation tool for dev’s staff members to use
I just checked and it’s not necessarily against the ToS. Sharing this info is against the ToS but using it as a developer is not!
As long as only the developer has access to this, it should be fine.
You can use this data to generate a list of alt accounts on the server and then send that list however you want (webhook, server to client, data analytics, your own servers etc)
As long as you only share the list of names generated using that data, its fine. What would NOT be fine, is also sharing that data together with the list of accounts.
Pretty interesting, but from my perspective I don’t think Roblox will take action unless people are using this to do things like leak high-profile user’s alts, YouTubers could get heavily targeted by that.
Not saying that you’re wrong, but I feel like this sentence is said too often in any posts just referencing exploiters. Sure people are frustrated, but I’m certain everybody knows why they’re frustrated.