
Security concerns with minified JavaScript code - edward
https://lists.debian.org/debian-devel/2015/08/msg00427.html
======
mapgrep
Javascript minification seems like a classic case of premature optimization.
I'm not a front-end developer so maybe I'm missing something, but wouldn't
simple, transparent gzip compression via your webserver — one line in a config
file — get you something like ~90% of the benefits of minification, for free?

I'm sure there are some additional gains to be made from knowing the
syntactical structure of the language you're minifying, since no generic
compression scheme can know that. But that seems really marginal — and, as the
article in question shows, opens up potential security vulns.

~~~
dahart
It depends on why you're minifying, but there are good reasons to do so. I'd
bet, as in guess/speculate without evidence, that the #1 reason people do it
is for code obfuscation. Front end JavaScript delivers source code to your
users and competitors alike, that can be scary when you're working hard on
something you think people will like & copy.

Most here are talking about uglify which does very superficial minimization,
but Google's closure compiler is in the mix too, and that provides static type
checking, nested variable flattening, dead code removal, inlining
optimizations, and more. These things are substantial, not marginal,
especially the static type checking. It's closer to C++ compilation than it is
to gzip.

Gzip can be used (and usually is) either way, and generally still has some
benefit even on minified code. But no, gzip can't replace or provide the
majority benefit of a minification pass, they're two mostly independent things
done for mostly different reasons.

~~~
qCOVET
I don't know if you could hide it from competition - it can be unminified ex
with: [http://jsbeautifier.org/](http://jsbeautifier.org/) I have tried this
before and it worked.

~~~
leeoniya
it depends on how you minify.

if you use Closure Compiler in Advanced mode it will rename and rescope pretty
much everything except native methods and strings (making lots of globals
cause it assumes you're compiling all page's code at once), it will eliminate
unused code even from third party libs like jquery and do all sorts of stuff
that make the beautified code very expensive to understand, follow and reverse
and even then you may end up with only half a library that does only one
specific task and the rest of the code would be missing.

you have to do a bit of JSDoc annotations in some places to fix some breakages
and restructure the code a tiny bit to get it to rename absolutely everything
but the results are excellent.

~~~
kuschku
So, run [http://www.jsnice.org/](http://www.jsnice.org/) over it?

~~~
tomjen3
You can also just click the {} icon in Chromes debugger.

All you will get is a lot of function a with arguments z d and f calling eq on
z and passing it d, then returning the qln value of the result.

Slightly more readable, yes, but still like debugging assembler.

~~~
kuschku
It actually provides meaningful names, here is more about it:
[https://news.ycombinator.com/item?id=7836092](https://news.ycombinator.com/item?id=7836092)

------
cbr
Looks like Debian is mostly on top of it:

    
    
        > To me the problem suggests that it is important from a security and
        > accountability perspective to 1) include the human-readable source 
        > code of JavaScript in Debian packages, and 2) to compile the 
        > human-readable source code into a minified code (if required) during 
        > package builds, using a JS-minifier that is included in Debian.
        > Thoughts?
        
        This is anyway mandatory in Debian.
    

[https://lists.debian.org/debian-
devel/2015/08/msg00478.html](https://lists.debian.org/debian-
devel/2015/08/msg00478.html)

~~~
zamalek
> Looks like Debian is mostly on top of it

I wonder... I'm assuming their concern surrounds stuff that is installed on-
disk with the package manager. Is there any benefit _at all_ to JS
minification when the file:// protocol is in use? Marginal (if at all)
improvements to parse time? Anything else? Or is "everyone doing it?"

~~~
nteon
V8 uses a function's length in characters, _including comments_, to make
optimization decisions. By removing comments and shortening var identifiers
internal to a function, I can imagine a number of functions dropping under
that 600-char threshold. Would be great to measure.

~~~
the8472
that seems like a terrible way to measure things. why not go by the number of
some form of nodes after parsing? Parsing has to happen before optimization
anyway.

~~~
gmac
Agreed, this is awful. Presumably it means that _adding comments can slow your
code down_ , which I would otherwise have thought a completely laughable
suggestion.

~~~
laumars
The problem with JIT compiled languages is compilation time is as critical as
the performance of the compiled code. Using string length happens to be
quicker than producing ASTs. So while it is a dirty hack, it generally offers
greater performance more often than not.

------
caf
It's hard to see how shipping minified JavaScript in the source packages
passed muster with Debian in the first place. If it's not the human-readable
source that's in the preferred form for modifications, it's not really the
source at all.

------
skarap
I like how strict Fedora Packaging Policy is regarding this
([https://fedoraproject.org/wiki/Packaging:JavaScript](https://fedoraproject.org/wiki/Packaging:JavaScript)).

"If a JavaScript library typically is shipped as minified or compiled code, it
must be compiled or minified as part of the RPM build process. Shipping pre-
minified or pre-compiled code is unacceptable in Fedora."

~~~
vbernat
They say that but this is not always the case. See for example Roundcube. They
just shipped minified JS as shipped by upstream. In Debian, pre-minification
JS files are shipped and minified during the build process.

[http://pkgs.fedoraproject.org/cgit/roundcubemail.git/tree/ro...](http://pkgs.fedoraproject.org/cgit/roundcubemail.git/tree/roundcubemail.spec)

------
prolepunk
One of the messages in the thread links to this debian wiki article that lays
out all the arguments against minification.

[https://wiki.debian.org/onlyjob/no-
minification](https://wiki.debian.org/onlyjob/no-minification)

One argument that really drives it home -- GZIP compression without
minification is faster and better, and from my own experience takes exactly
two lines of nginx config to set up.

Is there really any benefit of using minification besides code obfuscation?

~~~
cwyers
Define "faster." GZIP creates a smaller file size, but the benefit of
minification is faster parsing by the Javascript engine -- GZIP does nothing
to help with this.

~~~
ape4
Maybe not always. Minifiers optimize for space. So they might turn "false"
into "!1". Not sure that executes faster.

~~~
BinaryIdiot
So yeah they optimize for space but they also optimize for execution. For
example the closure minifier literally puts everything on one line, without
semicolons (where possible) and uses commas which forces the JavaScript engine
to execute it as a single statement which can be faster.

That's just a minor point but it can help with speed. Is it enough of an
optimization that it's worth it though? I'm not sure; I haven't seen much in
the way of benchmarks in this area.

------
jimminy
All of the interesting content is in the link provided in the list[1], and
otherwise this is just a post about how debian should handle the security
issues presented there.

This might be a good candidate for having the link updated.

[1]: [https://zyan.scripts.mit.edu/blog/backdooring-
js/](https://zyan.scripts.mit.edu/blog/backdooring-js/)

~~~
boklm
This link has already been posted:
[https://news.ycombinator.com/item?id=10107833](https://news.ycombinator.com/item?id=10107833)

~~~
0942v8653
(As of right now, 202 points and 34 comments)

------
personjerry
The moral of the story, it seems, is to beware any tool that modifies or
writes your code for you, even something as seemingly harmless as a JS
minifier.

~~~
mtgx
Wouldn't that include compilers, too?

~~~
federico3
...and that's why the Debian community is driving reproducible builds:
[https://reproducible.debian.net/reproducible.html](https://reproducible.debian.net/reproducible.html)

~~~
pascal_cuoq
Reproducible builds do not solve the problem of a compiler bug being used to
introduce a backdoor during the translation from source code to binary.

~~~
kuschku
Yes, they do – you take a C compiler (written in ASM) that is just good enough
to compile TCC, now you prove that the source of TCC and GCC is right, you
compile TCC with your simple compiler, then compile GCC with TCC, and you have
a compiler which is guaranteed to be free of such backdoors.

Now, you compile your code with this compiler, and if your own build
reproducibly has the same results as this verified compiler, you're safe.

~~~
AgentME
That defends you from a different type of attack. That can't fix bugs in your
compiler or minifier. See [https://zyan.scripts.mit.edu/blog/backdooring-
js/](https://zyan.scripts.mit.edu/blog/backdooring-js/)

~~~
kuschku
Your attack only applies if my minifier is wrong.

My solution even protects me from attacks where the source of the minifier is
safe, and only the minified minifier contains the bug.

------
dahart
The whole point of the back-dooring js blog post was that you can create
"deniable" bugs that are hard if not impossible to see by inspecting the
unminified source, and can't prove intention. They already have great reasons
to require source, so it's ironic this would be the catalyst to start, no?

------
fixermark
Conceptually, this is a reasonable line to draw in the sand. Minified
Javascript should be thought of as equivalent to assembly language or Java
bytecode[1], and if the policy is to not ship compiled binaries without source
available, minified JS should fall into that category.

[1] [https://www.destroyallsoftware.com/talks/the-birth-and-
death...](https://www.destroyallsoftware.com/talks/the-birth-and-death-of-
javascript) is funny, but the honest truth is that it's likely the future of
web technologies, as Javascript is so deeply entrenched in the fiber of the
web standards and the practical implementation of the browsers.

------
wyldfire
Is the AST structure preserved when minifying? If only identifier names and
whitespace changed, then it seems like one could verify that the minified
matches the original.

~~~
prezjordan
Not always, most do tricks like `!0` for `true` and `a && b` instead of `if
(a) b`.

~~~
userbinator
IMHO that's going into optimiser territory, and perhaps the reason why these
minifier bugs exist - the minifier writers need to be _very_ sure of the
semantics of the language before attempting to rewrite code to have the same
behaviour, and JS/ECMAscript semantics are not trivial especially w.r.t the
(highly) dynamic type system as the example shows. Even renaming things is not
as trivial as it seems because the names can themselves be dynamic, e.g.
a.somefield vs a['s' \+ 'ome' \+ 'field'].

(I'm not a JS developer so I might've missed a few other things, but minifying
JS is definitely a much harder problem than doing it with something like C.)

~~~
EGreg
just one nitpick on your comment - minifiers do not replace property names,
only variable names in JS

~~~
comex
Closure Compiler does rewrite property names in advanced mode.

~~~
Arnavion
UglifyJS also rewrites property names if you tell it to. It's conservative so
it only replaces the names you explicitly tell it to rename. Recently it
gained the ability to let you give a regex, so if you have a convention of
always prefixing private rename-safe properties with an underscore, you can
now have those renamed.

------
eastbayjake
This thread on V8 inlining was buried at the bottom of the blog post linked in
this thread, but it was super valuable and is changing the way I write
functions in Node this morning:
[https://news.ycombinator.com/item?id=10108672](https://news.ycombinator.com/item?id=10108672)

------
nickpsecurity
Did anyone else get a kick out of seeing these two in a row on the homepage?

Closure compiler parses JavaScript code, removes dead code, _minimizes what 's
left_

Security concerns with _minified_ JavaScript code

One of those moments where I pause to wonder if I should even read further
haha.

------
kpcyrd
The quoted link discusses backdooring js by writing legit code that turns
malicious through minifier bugs.

The only way to be sure is not minifying the source.

~~~
gipsies
Or by using a trusted minifier on the source code yourself, as mentioned in
the link.

~~~
kpcyrd
How do you tell your trusted minifier has no exploitable bugs?

~~~
icebraining
Exploitable by what?

~~~
TheLoneWolfling
Anyone who has ever pushed a patch to the project that you're running through
the minifier.

~~~
icebraining
What would be the point of exploiting the minifier? The purpose is to get the
code to the Debian users, how does exploiting the minifier help with that? If
you can put an exploit is the program's source, you just want it to be
minified as-is so that it'll run on the end user machines.

~~~
TheLoneWolfling
Read the linked article: [https://zyan.scripts.mit.edu/blog/backdooring-
js/](https://zyan.scripts.mit.edu/blog/backdooring-js/)

It has the answer to your question. Namely, you can write bugs that are
exploitable _that aren 't present in the original source_, that _only_ appear
in the minified output. Which means that a) it is a whole lot harder for
someone to find (especially if it's something that is "obviously" correct),
and b) it's plausibly deniable.

------
jordigh
This is why LibreJS is an important project.

[https://www.gnu.org/software/librejs/](https://www.gnu.org/software/librejs/)

It won't help towards minification bugs or backdooring, but it's a step in the
right direction.

