1
\$\begingroup\$

I'm working on a single-player browser-based RPG in my free time. I do allow for saving the user's game on the server, but the game is largely going to run locally and I therefore acknowledge that it is impossible to outright prevent a user from cheating in a myriad of ways.

I'm just looking at the idea of a "dookie" achievement for editing the game data, and was wondering if this solution is in any way viable?

if (Reflect.set.toString() !== 'function set() { [native code] }') {
  throw new TypeError('Missing native Reflect.set');
}
if (Error.constructor.toString() !== 'function Function() { [native code] }') {
  throw new TypeError('Missing native Error constructor');
}
if (Proxy.constructor.toString() !== 'function Function() { [native code] }') {
  throw new TypeError('Missing native Proxy constructor');
}

function setTrap(target, _1, _2, receiver) {
  const res = Reflect.set(...arguments);
  const stack = new Error().stack.split('\n');
  if (stack.length >= 2 && stack[1].indexOf('<anonymous>') >= 0) {
    Reflect.set(target, 'cheated', true, receiver);
  }
  return res;
}

const myData = new Proxy({
  data1: valueA,
  data2: valueB,
  data3: valueC,
  // ...
}, {set: setTrap});

If the user edits myData in the browser console, their changes will remain, but myData will also get cheated: true after whatever change they make. Assuming setTrap is defined in a local scope, circumventing this would require editing the actual code loaded by the browser rather than simply editing a value in the console, and minified & packed JS would make that a more frustrating task than most would bother with, I think.

This would let someone who derives enjoyment from cheating do so without additional difficulties on their part, while letting me offer them a mild rebuke with a dookie achievement for cheating.

Of course, a determined cheater could prevent the game from sending the cheating marker to the server when saving the game. Even trying to alert the server from within the setTrap function could be easily defeated by simply cutting off the web connection while cheating, but this idea is more of a wagging finger than an attempt at preventing any cheating from occurring.

But my question is this: have I missed anything obvious with this idea? Is there a better way to write my conditional (I admit, I came up with it after just a few minutes messing around in Chrome, so it may not work the same in Firefox or something)?

\$\endgroup\$
4
\$\begingroup\$

Yes, your strategy for detecting manipulation of the runtime forgets that it is also possible to override the toString method of any function. After the cheater changed Reflect.set to their own implementation, they just need to enter the following in the javascript console of their web browsers to break your cheat detection system:

Reflect.set.toString = function() { return "function set() { [native code] }" }

Alternatively, they could set a break-point before const myData and replace setTrap with a different function. Which might ironically be really handy as an entry point for their own cheating functionality.

Sorry, but a browser-based anti cheat system is not going to work. The debugging tools of modern web browsers are just too powerful and the JavaScript language just allows too much runtime trickery.

\$\endgroup\$
  • \$\begingroup\$ Whoops! Good point. \$\endgroup\$ – Brian S May 18 at 8:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.