Basically what the title says. I'm making a game and most of it is in js. My concern is that the player stats are all just variables in its object and they are easy to change in the browser console(e.g. player.hitpoints = 1000;).
Is there any way to hide certain objects/variables from being editable in the browser console?
JavaScript is clients side scripting language, that means that it is interpreter on the client's computer and he can do or change whatever he wants. You can uglify and minimize your code for obfuscation the code, but it won't prevent the user from changing it.
I just google "obfuscation javascript code online" and I found this link with a good example of before and after the obfuscation process - here
The other answers have already explained obfuscation and the inherent limitations of client side JavaScript. They are correct and relevant, but don't directly address the question.
To make it impossible¹ to access a variable through the browser console, you need to make it into a local variable inside a function. For example, instead of:
var player = {hitpoints: 100};
// game logic here
You would do:
(function() {
var player = {hitpoints: 100};
// game logic here
})();
This creates an anonymous function, and then immediately calls it, the so-called IIFE. Now player is no longer global (i.e. a property on the window object), but exists only within this function.
¹ It can still be done through the debugger, but it's a lot harder than just copying and pasting some commands.
You can use JWT to make immutable data that don't change very often. For your case, I would recommend storing the state on the server.
You can minify/uglify your code to make it trickier for someone to modify it, but the most important part would be to validate everything server-side. If their hitpoints equal 10 one second and 10000 the next, obviously something would be fishy and the server could prevent itself from broadcasting that out to the other players -- essentially isolating the ne'er-do-well to their own PC.
Identifiers declared with let and const do have block scope. So if your variables are not declared with var then use a block statement.
Example:
code:{
//your code...
}
code: {
let hitpoint = 12;
let kill = function() {
hitpoint -= 12;
}
//function kill(){} is accessible from outside
//let kill = function(){} is not accessible from outside
console.log("From inside:", hitpoint)
}
try {
console.log("From outside:", hitpoint)
} catch (err) {
console.log("From outside:", "It gives an error. So you can't access it from the console or anywhere outside the code")
}
Related
I support a very old PHP web framework that uses server-side rendering. I decided to implement Vue for the rendering of some modules, so I compiled a hello world app and realized deployment wouldn't be so simple.
The framework works as a giant SPA, with each module being rendered using the html output of a body() function. The output is replaced in the client's DOM without reloading the page itself.
<script> tags are banned for security reasons and will be sanitized from the resulting html. The only way to deliver JS to the client is by using an eval_js() function.
The problem is rather simple. I need to safely load JS code several times in the same DOM. I cannot load it as-is after app compilation, because from the second time onwards the code is executed (every time a user visits a module, or performs an action) the code will attempt to re-define global variables and kill the whole client.
The solution is also rather simple, just rewrite the JS code such that every global definition is transformed into a window property. This way, even if the same piece of code gets executed several times in the same DOM, it will simply replace window properties rather than attempting to re-define variables.
In example, the following input:
function Yr(t){
const b = t.prototype.hasOwnProperty;
this._init(b);
}
var hOe = sg(uOe, fOe, dOe, !1, null, "e687eb20", null, null);
const vOe = {
name: "AmmFilters",
components: {
AmmOptionSelect: pOe
}
};
new Yr({...}).$mount("#app");
Would be rewritten into:
window.Yr = function(t){
const b = t.prototype.hasOwnProperty;
this._init(b);
}
window.hOe = sg(window.uOe, window.fOe, window.dOe, !1, null, "e687eb20", null, null);
window.vOe = {
name: "AmmFilters",
components: {
AmmOptionSelect: window.pOe
}
}
new window.Yr({...}).$mount("#app");
I initially considered to write my own parser, but then realized that ES6+ syntax is no child's play. The code I will attempt to rewrite is optimized & obfuscated which means it will have all sort of complex syntax and I must be careful not to turn scoped definitions into window properties.
Any ideas on a tool that already performs this task? The resulting JS code should have no difference from the original, as global scoped variables end up in the window object anyway.
I believe it would be a fairly useful tool for various use cases, so thought about asking before attempting to reinvent the wheel.
I have a web-app (GAS) where once the user has logged in (through Firebase/Google Auth), i am able catch his uID. Since i need this uID on server-sided functions later, once i catch it i run a function via google.script.run that will update an already declared global variable so that to store it on the other side. This variable is declared outside any function as empty like this var fbuserIDkey = ""; when the app is loaded . So this variable, once the user has logged in, should have been updated with the uID.
My problem is that later i need this variable in a function triggered by a click on a button, but the function server side seems to use an old version of the variable if that is possible, like it has not been updated.
Here are the bits of code involved:
declaring variable:
var fbuserIDkey = "";
passing the value when user is logged and updating server-side variable :
onAuthStateChanged{
...
const userIDkey = user.uid;
...
google.script.run.logUser(userIDkey);
} //console.log(userIDkey); = correct value
the function that updates global variable server-side:
function logUser(userIDkey){
fbuserIDkey = userIDkey;
Logger.log("ID is " + fbuserIDkey);//returns correct value on server-side too
}
So the variable has been updated.
But later, when the button is clicked on client-side and it triggers the function that should use the variable on server-side, it is still empty. Using Logger.log(fbuserIDkey) returns ""
It looks like the variable has been updated but the function that actually needs it has the old version of it (""). How is that possible? I precise that the click on the button always happens far after everything is loaded. I guess there might be some trick due to server/client communication but i don't understand where exactly and how to correct it.
EDIT
So as suggested i add more context, trying to paste as much code as possible.
fbuserIDkey is declared on the file (.gs) that contains the doGet() function if it adds any relevant detail.
var fbuserIDkey = "";
function doGet(e) {
...}
below is the function triggered by button that uses the variable:
function TransferPicks(picks){
Logger.log("ID is" + fbuserIDkey);//returns ""
...}
the eventlister is:
document.getElementById("savepicks").addEventListener("click", StartSaving);
StartSaving is a function that does several stuff (not related) and in the end triggers TransferPicks
Just answering the question since it is now solved if that can help anybody.
The trick as advised by the comments was indeed to get (again) the user.uID right at the moment I (re)need it, instead of trying to store it once and use it later. So, when document.getElementById("savepicks").addEventListener("click", StartSaving); happens, i get user info with onAuthStateChanged((user) => {if (user) {...}} and create one object with all the data I need from the client side. Then I pass it through google.script.run to the server side function and it works just fine!
Thanks to all who commented, I was just forcing it the wrong way.
Is it possible to change the content of document.scripts[0].firstChild with a textual replacement? And in such a way that the changes affect the code.
e.g.
function hamjam() {
console.log("__hamjam__");
}
document.scripts[0].firstChild.textContent = document.scripts[0].firstChild.textContent.replaceAll("__hamjam__", "XXX")
Calling hamjam now should log XXX instead of __hamjam__ (which it does not).
Important, what I try to do is highly experimental and more a proof of concept then something I try to do in production. The goal can be achieved in a really ugly matter, and if it only works with a certain browser is also fine.
And in such a way that the changes affect the code.
No. You can change the text that's in the script tag, but that text is obsolete by the time you do it. It's been parsed into actual program code in memory. Changing it has no effect on that program code in memory. (In fact, you could remove the script tag entirely and it would have no effect — unlike removing CSS resources, which does.)
Here's an example:
const script = document.getElementById("target-script");
script.textContent = script.textContent.replace("Hi there", "I've updated the message!");
console.log("The new script content is:");
console.log(script.textContent);
console.log("But click the button and see what happens.");
<input type="button" id="the-button" value="Click me">
<script id="target-script">
document.getElementById("the-button").addEventListener("click", () => {
const message = "Hi there";
console.log(message);
});
</script>
You've said your example is just a small example of a larger thing, but FWIW, you could replace that function. In very limited situations, that might do what you want (and so probably isn't applicable to your larger pattern).
Here's an example:
console.log("Calling it before the change:");
hamjam();
console.log("Changing it");
let source = Function.prototype.toString.call(hamjam);
source = source.replace(/__hamjam__/g, "XXX");
hamjam = (0, eval("(" + source + ")"));
console.log("Calling it after the change:");
hamjam();
<script id="target-script">
function hamjam() {
console.log("__hamjam__");
}
</script>
But that only works in very limited situations.
Function.prototype.toString has to return a valid representation of the function (which it should do in the normal case, but...)
The function has to be assignable in the scope where you're making the change
It has to not close over things that it relies on (because your new version won't)
...and probably others.
Side note: That (0, eval("...")) may look odd if you haven't seen it before. As you may know, eval has this...special...ability that it works in local scope. But most of the time (in my experience, YMMV), you don't want the code you're evaluating to have access to local scope. The (0, eval("...")) pattern (0 can be anything) breaks the link with local scope, doing the eval at global scope instead. It's called "indirect eval".
We have a use case where we need to block Drupal's core ajax error handling from alerting users (we're handling the error reporting on our own). Previously another developer had commented out a line in the core ajax.js file, to prevent Drupal from spawning the alert box, but I'd like to handle it without touching core.
From the core, drupal.js:
/**
* Displays a JavaScript error from an Ajax response when appropriate to do so.
*/
Drupal.displayAjaxError = function (message) {
// Skip displaying the message if the user deliberately aborted (for example,
// by reloading the page or navigating to a different page) while the Ajax
// request was still ongoing. See, for example, the discussion at
// http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-
// clicks-refresh.
if (!Drupal.beforeUnloadCalled) {
alert(message);
}
};
My current fix, is to override the Drupal.displayAjaxError function and change the Drupal.beforeUnloadCalled property that determines whether or not to alert the error:
var ajax_error_backup = Drupal.displayAjaxError;
Drupal.displayAjaxError = function (message) {
Drupal.beforeUnloadCalled = true;
ajax_error_backup(message);
};
My question, is whether or not this is an appropriate fix? I know that I could also override the function and just leave it empty - costing fewer lines, and not invoking another call to the original function (and saving the object I've created by backing up the original in ajax_error_backup).
Am I adding complexity to keep things tidy, or should I just override with:
Drupal.displayAjaxError = function (message) {
//empty
};
To clarify - the desire is to never have this ajax alert occur, so there's not functional difference between my desire to keep things neat/tidy, and just overriding the function with a blank one - there isn't a case where want this alert to succeed.
Thanks in advance for helping this old dog think through something with fresh eyes.
In this case, there isn't one option that seems to be clearly better than the other. It should be handled on a case by case basis, and in this case, either of the methods really is adequate.
I personally opted for using the slightly more expensive method of overriding the function and calling it back, because I felt that it might be somewhat more future-proof:
var ajax_error_backup = Drupal.displayAjaxError;
Drupal.displayAjaxError = function (message) {
Drupal.beforeUnloadCalled = true;
ajax_error_backup(message);
};
If Drupal were to extend the function on their end in the future, there might be another condition that we wouldn't want to override.
Overriding with the empty function would be the cheapest, but would also potentially be a bit heavy handed.
It seems that either approach is valid, and is probably best handled case-by-case.
In a web app that allows users to play with javascript I require them to have a function main() in their "program". There's a "run" button, and an "edit" button. When you press "run" text from a <textarea> is used to create a script block and insert it into the DOM. Then main() is called.
I catch window.onerror to display errors to the user. This generally works OK. If there is no main(), an appropriate error message is shown.
When you press "edit", the script block is set to blank (script.text = '';),and removed from the DOM.
Testing, I noticed that if I had "program" consisting of just:
function main() { printLn('main here'); }
it worked as expected, but when I changed that to:
function moon() { printLn('moon here'); }
instead of getting a message saying main() not defined, it still worked as before, despite the fact that the script block had the "moon" text. This continued to happen if I gave each created script block a distinctive ID, and if I changed the script block type to text/plain before removing it.
The problem occurs in current Firefox, Chrome, and Opera. You can see what happens here
The functions are still defined, even if you remove the script that defined it.
This is in stark contrast to CSS, where removing or modifying a stylesheet will remove or update the styles on the page accordingly.
There's a good reason for that, and that is that CSS is designed to be easily re-evaluated when changes are made. JavaScript on the other hand is far too complex for the browser to be able to understand what "removing a script" actually does.
So, provided you have run one function with main(), it will continue to exist even if you then delete it.
My suggestion would be to create a closure to run your script. You can do this with something like...
var input = "........"; // user's input
var runner = "if( typeof main === 'undefined') {"+
"showErrorMessage('No main() defined');"+ // or whatever your error function is
"} else { main(); }";
var func = new Function("(function() {"+input+runner+"})()");
func();
It's always worth noting that the global scope can still be accessed, such as if the user forgets to var their local variables, or if they outright access window.something. So long as it's only being run on the user's own machine, this isn't much of an issue, but if people can share their codes with others then you will need to be much more careful.