Note: This specific question has not been answered or discussed before, I have looked at all obfuscator-relevant questions on this forum.
I’m looking for an obfuscator that will do the following:
auto-rename all variables and function names starting from the shortest names possible and gradually increasing the name length by covering all letter and number combinations.
will auto exclude all function and object names that belong to the system (DOM, WebGL, WebGPU, etc).
the logic-flow obfuscation will be optional, to an optional degree.
That should result in one-click obfuscator + the options selected.
Does this species exist?
That is the question!
BTW, assuming that all three criteria above are satisfied, how easy will it be to de-obfuscate the code by using de-obfuscator utilities or relevant methods?
I truly doubt that such obfuscator exists. If it exits, it will not be perfect and will be full of compromises. Consider this case:
function carcolepsy( gl )
{
gl.enable(gl.DEPTH_TEST);
}
Should enable and DEPTH_TEST be renamed, provided that you have no runtime information whether gl is a WebGL rendering context or just some user-defined object?
No, even if everything is known, it is still impossible. Consider the carcolepsy function is called by this code, and it is known that gl is a WebGL context, but fakeGL is not:
carcolepsy( gl );
carcolepsy( fakeGL );
In this case enable is sometimes a WebGL function, but sometimes it is not. Obfuscation by mere replacement of names in JS is impossible at the level that you ask for.
Google Closure Compiler does something like this, but was (as @PavelBoytchev said) full of compromises. It needed to be told a great deal about which properties must be preserved, and if your dependencies are not themselves built with Closure Compiler (most aren’t!) you’ll have a lot of extra work to do. See The Saga of the Closure Compiler, and Why TypeScript Won for a bit of background on that.
I would have a look at Terser, for a more practical and easy-to-use option: https://terser.org/.
That said, both are intended to minimize file size, not to provide robust security or IP protection. I think that’s the best you can realistically hope for, when running code on a user-owned device. Even WebAssembly can be reverse engineered given enough time and a determined attacker.
I would disagree with the original assumption. This has been discussed time and time again and conclusion was that you (someone) probably doesn’t have code thats really worth securing like this.
However, i did encounter something that ran webgl, but spector was somehow disabled. Never dug in deeper to see what it was about. Probably can be beat, but it definitely removed the one click convenience out of it.
But not on the same code. By examining the code (not judging from a single function) you can easily tell whether gl is about WebGL or something completely irrelevant.
Well, I the user shouldn’t have access to your code running as a guest on my device, and your guest code shouldn’t have access to my device - beyond what would be allowed in an isolated and protected space of presentation, interactivity and download/upload, which is all we both need.
That’s how things should have been, and the criteria that web protocols and browser architectures should have been based on.
Instead, since the internet was invented, every website can mess with the user’s device - even destroy it, and the users can hack the website, and steal thousands of man-hours of work, or other valuable data. That’s incredibly dumb in my book.
Anyway, thanks for the info, but there are certainly methods to at least make it difficult, so I’ll have to dig deeper on the subject and try several of the options available…
I mean exactly the same code. In the example, that I gave, it is the same code that once is used with WebGL and once is used with non-WebGL. It doesn’t matter whether you know which time which case is. The issue is that within a single execution of the running program both cases occur.
Here is again the example. Should enable and DEPTH_TEST be renamed? Whatever the answer is, either yes or no, it will be a wrong answer.
If you want to hide the code from the user, you have to physically separate the code from the user, by moving all the code server-side. Then send only the result to the user. PHP is doing this.
Anyway, good luck with the search for a perfect JS obfuscator.
If you find something, please, post it here.
Why would one do that? I wouldn’t, not even to run WebGL without hardware acceleration - definitely not.
That’s an extremely rare case, the exception that proves the rule.
I put the user experience first, and it’s a highly interactive website, so I don’t want to introduce delays and hiccups in user experience and ruin it - not to mention the increased server cost which in my case has to stay low, so I can’t do that.
minifiers do to some extend, for the purposes of saving bandwidth, it is not common to run a production app that hasn’t been minified. but obfuscation is not reliable, and they cause performance deopt, your code will runs hundreds of times slower.
all known obfuscators have de-obfuscators. you can even ai assist to re-establish sensible variable names. i’ve demonstrated this here once in a similar discussion where it not only could establish the original source from a bunch of nonsense, but also with meaningful variables: After obfuscation there is a performance drop for Mirrors - #3 by drcmda it probably won’t be perfect but to single out the code that a hacker is interested in likewise wouldn’t be that hard.
not only that you can easily read out gpu buffers, get all your meshes and materials, shaders and so on. i would not count on this kind of security.
When I tried minifiers a couple of years ago, they all left long simple variable names intact and I ended up doing the whole renaming process manually with search and replace! They were completely useless to do even the most obvious thing, to scramble the meaningful names of the variables.
About obfuscation of the logic, I don’t trust it, nor I want to lose all optimization and have a performance hit, which is why I mentioned that it should be optional, and to a varying degree, just to make the already complex code non-understandable.
Unless you scramble them which will render copying the meshes useless - and de-scramble them within the shader as have been proposed on this forum and it might not even have a noticeable performance drop.
hm, yes, it seems that way. i’m surprised it doesn’t remove all names. variables are gone, but library functions are left untouched. i think it is too unsafe.
turned it to JS and pased it through 3 random online minification apps, one of which mentions that it uses uglifyJS 3 and as you can see, variable names remained intact:
let realDaysPerIdealDay = 4;
const WORK_DAYS_PER_WEEK = 5;
let sum = 0, i;
for (i = 0; j < NUMBER_OF_TASKS; j++) {
let realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
let realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
let realDaysPerIdealDay = 4;const WORK_DAYS_PER_WEEK = 5;let sum = 0, i;for (i = 0; j < NUMBER_OF_TASKS; j++) { let realTaskDays = taskEstimate[j] * realDaysPerIdealDay; let realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK); sum += realTaskWeeks;} https://freecodetools.org/minifier/
let realDaysPerIdealDay=4;const WORK_DAYS_PER_WEEK=5;let sum=0,i;for(i=0;j<NUMBER_OF_TASKS;j++){let realTaskDays=taskEstimate[j]*realDaysPerIdealDay;let realTaskWeeks=(realdays/WORK_DAYS_PER_WEEK);sum+=realTaskWeeks} https://www.minifier.org/
let realDaysPerIdealDay=4;const WORK_DAYS_PER_WEEK=5;let i,sum=0;for(i=0;j<NUMBER_OF_TASKS;j++){taskEstimate[j];sum+=realdays/5} https://skalman.github.io/UglifyJS-online/
i think you’re using old tooling. you don’t drop files into online minifiers for at least 10 years or longer. it probably represented uglify form ages ago.
in a vite app it does
let realDaysPerIdealDay = 4;
const WORK_DAYS_PER_WEEK = 5;
const NUMBER_OF_TASKS = 10;
const taskEstimate = []
const realdays = 2
let sum = 0, i;
for (i = 0; i < NUMBER_OF_TASKS; i++) {
let realTaskDays = taskEstimate[i] * realDaysPerIdealDay;
let realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
console.log(sum);
const u = 5, f = 10, d = 2
let c = 0, s
for (s = 0; s < f; s++) {
let i = d / u
c += i
}
console.log(c)
even eliminated the dead code (realTaskDays). i did nothing else, didn’t install anything, just yarn build. ps without the console it would have deleted the whole code because it had no effect.
This is a perfectly legal use of JavaScript functionality. Once you go the route of “nobody-writes-such-code-because-according-to-me-it-is-pointless-so-the-obfuscator-is-not-required-to-support-it”, then you are already making a compromise.
By the way, I just looked at the last link that you posted (UglifyJS 3: Online JavaScript minifier). Your claim that variable names remained intact is wrong. There is [Options] button. When I changed the options, this code:
let realDaysPerIdealDay = 4;
const WORK_DAYS_PER_WEEK = 5;
const NUMBER_OF_TASKS = 20;
var taskEstimate = [];
var realdays = 7;
let sum = 0, i;
for (i = 0; j < NUMBER_OF_TASKS; j++) {
let realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
let realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
becomes this:
var t,e;let l=4;const o=5,r=20;t=[],e=7;let a,c=0;for(a=0;j<20;j++){t[j];c+=e/5}
I’m actually using esbuild, but that project while it was quite complex (6-months of full-time work) with lots of 2D graphics and audio processing, didn’t require Three.js nor modules, just vanilla Javascript and HTML, so I didn’t use esbuild.
I’m relatively new in using bundlers, I haven’t finished a project with esbuild yet, so I forgot that it is minimizing the code too, so thanks for reminding me that!
Esbuild for that same input, gave me the following:
So the burden is on the shoulders of the user to study the brief and rather vague documentation and choose/guess the right options in order to get the right results - not the kind of software completeness I’m looking for.
Besides, they didn’t enable those options for a reason…
Which is why my judgement of their default, most reliable options is still valid.
But you’re right, I didn’t notice the option button, I stand corrected about that.
That’s what I intend to do if I don’t find a less D&D solution!
But I’ve made too many myself: my own font series, my own rich document format, my own word processor, my own database, my, my…
Now my own… minimizer & obfuscator?
Minifiers should never touch “Public API” code (exported identifiers), since that may very well break functionality of (peer) dependencies of said libraries. Typically, only local identifiers are renamed, because these variables, functions and classes are only used locally within a module. This is why the names of ThreeJS methods remain untouched. Also, object keys are typically left untouched for the same reason.
The reason for this is that a minifier cannot safely assume the way you bundle your app. For example, there may be peer dependencies involved outside your bundle that rely on exported members from a library that you are using.
You can of course configure most bundlers to create one big bundle of your application which also have plugins for minifiers. The combination of rollup + terser is a typical one that is used quite often. In the end, this all depends on your project and build tools and configuration.