Potential security concern: calling functions from the browser console

0 favourites
  • 5 posts
From the Asset Store
Medeival & Viking War Horns Sounds / 22 Unique Calls / 2:40 minutes of audio
  • When playing a game made in Construct 3 running on the web, it is possible to call any function inside of the event sheet directly from the browser console.

    Simply type this in the console:

    c3_callFunction(functionName , arr);

    where functionName is a string that matches the name of the function in the event sheet, and arr is an array of parameters for that function.

    This may raise some concerns, as anyone can basically do whatever they want to our games with much little effort.

    c3_callFunction is used to call a method of eventSheetManager named _InvokeFunctionFromJS

    e.EventSheetManager = class extends e.DefendedBase {
     constructor(c) {
     super();
     this._runtime = c;
     this._allSheets = [];
     this._sheetsByName = new Map();
     this._allGroups = [];
     this._groupsByName = new Map();
     this._blocksBySid = new Map();
     this._cndsBySid = new Map();
     this._actsBySid = new Map();
     this._allUniqueSolModifiers = new Map();
     this._eventVarsBySid = new Map();
     this._nextLocalVarIndex = 0;
     this._allGlobalVars = [];
     this._allLocalVars = [];
     this._localVarInitialValues = [];
     this._functionBlocksByName = new Map();
     this._eventStack = e.New(e.EventStack, this);
     this._localVarStack = e.New(e.LocalVarStack, this);
     this._loopStack = e.New(e.LoopStack, this);
     this._triggersToPostInit = [];
     this._queuedTriggers = [];
     this._queuedDebugTriggers = [];
     this._blockFlushingDepth = this._executingTriggerDepth = this._runningEventsDepth = 0;
     this._scheduledWaits = [];
     this._asyncActionPromises = [];
     self.c3_callFunction = (d, f) => this._InvokeFunctionFromJS(d, f);
     }
    }
    

    where _InvokeFunctionFromJS is defined as

    _InvokeFunctionFromJS(c, d) {
     Array.isArray(d) || (d = []);
     c = this.GetFunctionBlockByName(c.toLowerCase());
     if (!c) 
     return null;
     if (!c.IsEnabled()) 
     return c.GetDefaultReturnValue();
     var f = c.GetFunctionParameters();
     if (d.length < f.length) {
     d = d.slice(0);
     do 
     d.push(f[d.length].GetInitialValue());
     while (d.length < f.length);
     }
     f = c.GetEventBlock();
     return f.RunAsExpressionFunctionCall(
     f.GetSolModifiersIncludingParents(), 
     c.GetReturnType(), 
     c.GetDefaultReturnValue(), 
     ...d
     );
    }
    

    Now, to call c3_callFunction successfully you would need to know the names of the functions in the event sheets. These names can easily be accessed from this object:

    window["c3_runtimeInterface"]._localRuntime._eventSheetManager._functionBlocksByName;
    

    Inspecting each element, you can also get info on the parameters needed.

    There are also other similar things, such as the the ability to change layout from the console:

    IRuntime.prototype.getAllLayouts();
    const target = 0; //layout index
    IRuntime.prototype.goToLayout(target); 
    

    All these things are actually very useful when developing, since you can call functions directly from the console and design debug-specific functions that are supposed to be called from the console itself, with custom parameters. However, I don't understand why this functionality is kept after export. I tried to find a workaround to disable this sort of things and I end up with something like this in my events

    c3_callFunction = null;
    IRuntime = null;
    C3 = null;
    C3_ExpressionFuncs = null;
    C3_GetObjectRefTable = null; 
    C3_JsPropNameTable = null;
    

    I tested this and I dind't run into problems, but I don't know if doing this messes up things. Also, note that it is still possible to call functions from the console by doing this:

    window["c3_runtimeInterface"]._localRuntime._eventSheetManager._InvokeFunctionFromJS(functionName , params);
    

    However, setting window["c3_runtimeInterface"] to null stops the game from working correctly.

    Is there any way to prevent users from messing with the game using the console? If not, is it at least possible to make it harder?

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • If you're willing to use dev tools, then in principle users can probably do pretty much anything to your project. You can try to hide things, but then you can use the debugger to break inside engine code and run more commands. You can even use dev tools or browser extensions to rewrite the JavaScript code and remove protections. You can even go lower-level and use advanced tools to identify values in RAM like for the score or lives and overwrite them.

    In general normal players just don't do that, so I don't think it's ever been a major concern. Trying to remove various interfaces could also break your project, so it's risky to do that. If you're concerned about cheating, yes, people can cheat in single-player games, and have always been able to do so pretty much regardless of the technology, unless you run on a totally secured and locked down console platform. If you're concerned about cheating in multiplayer games, in a well-designed system with an authoritative server, peers cannot cheat regardless of what the client does. That's about how it is and how it's always been for the industry.

  • Preventing cheating is essentially impossible, but I find it strange that these commands are globally accessible. Is there a specific reason for this?

    I’m developing a single-player game for a client, and we are 100% sure that many people will try to exploit it with malicious intent after release. I’m particularly worried about code injection. However, since I’m not familiar with how the engine operates, I don’t know what an attacker could achieve with minimal effort.

    Another concern is the possibility of players bypassing paywalls. The game includes a "no ads" option that can be purchased. Internally, the purchase triggers a function that permanently disables ads. Wouldn’t allowing external access to such a function pose a security risk?

  • On a very random note but related to this, a reason NWJS for desktop builds has a downside:

    Haven't tested this, but, say you export as nwjs with the nwjs version that removes F12 console - cool... But you could just download the nwjs console version, move your steam game files and package.nw into it, run, and boom, console.

    I presume not as easy, if not impossible, in webview2!

  • Preventing cheating is essentially impossible, but I find it strange that these commands are globally accessible. Is there a specific reason for this?

    Basically there's no difference between writing JavaScript code in your project, and running commands in dev tools. So there's no good way to block one off but not another.

    Another concern is the possibility of players bypassing paywalls.

    Welcome to the DRM arms race. All you can do is invest increasing amounts of time to make it increasingly difficult for anyone to bypass protections. It is not generally possible to make it completely bulletproof for the reasons I mentioned. The best protection is an account system with server-side validation.

    FWIW, blocking off various JavaScript APIs is only minimal protection. If your "no ads" option permanently disables ads by saving something in storage, then someone can run a command in dev tools to write the same thing directly to storage - then there's no need to go via your project's logic and no amount of blocking off code features will help. In principle on the client side nothing is trusted and everything can be manipulated. The only way to put things beyond reach is to move it to the server side, and even that has ways around it - clients can intercept and change responses from the server too!

    My advice: as long as it's fairly difficult to do, 99% of people won't do it. I'd count accessing dev tools and running some specific command as fairly difficult for most people.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)