保护您的 Firefox OS 应用代码

关于使用 HTML5、CSS3 和 JavaScript 开发 Web 应用,我们经常被问到的一个问题是如何隐藏代码。原因包括防止作弊、保护知识产权、隐藏加密算法等等。由于 JavaScript 是在客户端执行的,即使在 Firefox OS 上,用户也可以在拥有必要技能并花时间的情况下访问代码。但不要误解!Android、iOS 和大多数移动平台上的应用都可以通过努力和合适的工具进行反向工程。

我们将介绍一些措施,使模仿者更难揭示您的应用代码。在不同的程度上,这些措施将提高获取代码所需的努力、技能和工具。

压缩您的代码

这是大多数开发人员的起点。压缩您的代码通过删除浏览器运行您的应用不需要的内容来减小文件大小。它首先从剥离注释和不必要的空白开始。这是一个很好的第一步,因为注释通常会揭示大量关于代码如何工作的信息。

下一级是缩短局部变量和函数的名称,即所谓的混淆。由于这消除了这些符号的意义,因此它进一步降低了可读性。

更高级的压缩工具,如 UglifyJS,将实际解析您的 JavaScript 以分析和转换它。这不仅允许重命名局部符号,还允许将条件和循环重写为更短的表达式,合并变量声明(提升)并删除无法访问的代码。最终文件不仅更小,更难读,而且在许多情况下也更快。

您可以使用 Google Closure 优化器的高级模式将此进一步提高一个层次。该工具允许为所有上述转换设置。高级模式是最激进的,在使用时需要 仔细考虑,以确保输出与输入一样运行。它将重命名、内联甚至删除函数、属性和变量,因此您需要确保正确引用所有符号或对其进行注释。额外的努力会有所回报,因为结果是高度压缩的,甚至可以提高性能。例如,让我们看看这段代码会发生什么

document.onload = function() {
  var shareImage = document.querySelector("#share-image");

  function share() {
    var imgToShare = document.querySelector("#image-to-share");

    if (!imgToShare.naturalWidth) {
      alert("Image failed to load, can't be shared");
      return;
    }

    // Create dummy canvas
    var blobCanvas = document.createElement("canvas");
    blobCanvas.width = imgToShare.width;
    blobCanvas.height = imgToShare.height;

    // Get context and draw image
    var blobCanvasContext = blobCanvas.getContext("2d");
    blobCanvasContext.drawImage(imgToShare, 0, 0);

    // Export to blob and share through a Web Activitiy
    blobCanvas.toBlob(function (blob) {
      var sharingImage = new MozActivity({
        name: "share",
        data: {
          type: "image/*",
          number: 1,
          blobs: [blob]
        }
      });
    });
  };
  shareImage.onclick = share;
};

一旦我们将这段代码运行到 Google Closure 中,代码将看起来像

var b=document.querySelector("#share-image"),
c=document.querySelector("#image-to-share");b&&c&&
(b.onclick=function(){if(0

Of course, the further you are writing custom logic, the more the code will be obfuscated, as the tools cannot change names from reserved word, or core functions. There are many tools online, and you can choose the one that fit your need. Most web frameworks offer interchangeable libraries and build steps that bundle and compress your JavaScript sources. For smaller projects your IDE, like Sublime Text, can be extended with packages available.

Using Emscripten

Did you remember that we said JavaScript is interpreted code? Well, you can bend that rule. Mozilla's Emscripten tool compiles C and C++ to JavaScript. The output isn't JavaScript as you know it. It behaves more like native code and manages its own memory (stack and heap) in pre-allocated typed arrays. This reduces garbage collection and makes inspecting the memory harder. The output also benefits from asm.js, a highly optimizable subset of JS, that runs on all modern browsers. If you are moving your app from native to the web you can just convert your existing libraries. But even if that doesn't apply, moving your protected code into a C/C++ library that you compile to JavaScript is the best option to get blazing performance with the added benefits of obfuscation.

Using a web service

Compression and compilation isn't the safest solution as people still have access to your code: it's just a bit harder to understand the logic. On the other side, one technique that moves the code to a place you have more control over is to use a web service. Your secret logic would run as a server-side script that your application will call when needed. Someone might be able to inspect the in- and outgoing data but there is no way they will be able to view your logic without access to your server. The major downside is that this feature will be only available when the user is online.

Let's see a simple example on how you can achieve this with a Firefox OS app, and a Node.js script: let's say we would like to hide our super exceptional fantastic code to do the addition to two numbers. Here is a simple node.js script (the goal is not to teach node.js, but show you a working basic example):

"use strict";
var express = require('express');
var app = express();

// Set up express to parse body
app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart());

// Only one route for web service
app.post('/', function(req, res) {
    // Convert parameters from request body
  var firstNumber = parseInt(req.body.firstNumber, 10);
  var secondNumber = parseInt(req.body.secondNumber, 10);

  if (isNaN(firstNumber) || isNaN(secondNumber)) {
      // Bad request, fail with 400 status
      return req.send(400);
  }

  var result = firstNumber + secondNumber;
  res.send({'result': result});
});

app.listen(8000);

Now in your app, you'll call this script by sending the values you want to add, and the script will return you the result. Of course, users can see the call you make, and the results you'll get, but they won't be able to see the magic behind the hidden function.

"use strict";
var firstNumber = 4;
var secondNumber = 5;

var data = new FormData();
data.append("firstNumber", firstNumber);
data.append("secondNumber", secondNumber);

var xhr = new XMLHttpRequest({mozSystem: true});
xhr.open("POST", "http://fharper-nodejs.nodejitsu.com", true);

xhr.onload = function() {
    // Parse the response and do something with it
    var response = JSON.parse(xhr.response);
    console.log(response.result);
};

xhr.onerror = function() {
    console.log("Magic failed");
};

xhr.send(data);

This code is just showing the web service pattern and not best practices. As any server, it still needs security measures to prevent exploits. Make sure you mask the request and response data by using a secure HTTPS connection. Control client access to your service by checking the Referrer and Origin HTTP headers but don't trust what you see as they can be faked. You can limit replaying request with malicious parameters by using adding a secret time-limited or single-use token. As you can see, having your code in a web service is keeping it off the client but opens different attack vectors you need to consider.

Be proactive, open your code

Of course, the best approach is to open your code: make your application open source, choose the right license, and show to the rest of the world that you were the first to create this piece of art. Choose the technology you want: you will make it harder to copy your code, but there is never a way to hide your idea. You need to be proactive: you need to be one of the best in your domain. Show your expertise, innovate, add features faster than the competitor do, be relevant for your market, and believe in what you do. How many knock offs of Angry Birds can you find on the web or on any marketplace/store? I don't have the exact number, but I can definitely say with a lot of confidence, that it's many! Rovio had an amazing idea, and they were the first to make it a reality: they are always innovating with brand new levels, new games with the characters we like, and more. You, your children, myself, we want to play to the original one, not the copy. Replace Angry Birds with any app or games you like, and you'll understand what I mean.

You have access to an amazing platform that open the web like never before: take advantage of it, build that app you want, and make your idea a reality!

About Harald Kirschner (digitarald)

Harald "digitarald" Kirschner is a Product Manager for Firefox's Developer Experience and Tools – striving to empower creators to code, design & maintain a web that is open and accessible to all. During his 8 years at Mozilla, he has grown his skill set amidst performance, web APIs, mobile, installable web apps, data visualization, and developer outreach projects.

More articles by Harald Kirschner (digitarald)…

About Frédéric Harper

As a Senior Technical Evangelist at Mozilla, Fred shares his passion about the Open Web, and help developers be successful with Firefox OS. Experienced speaker, t-shirts wearer, long-time blogger, passionate hugger, and HTML5 lover, Fred lives in Montréal, and speak Frenglish. Always conscious about the importance of unicorns, and gnomes, you can read about these topics, and other thoughts at outofcomfortzone.net.

More articles by Frédéric Harper…


22 条评论

  1. Willy Aguirre

    我只是在寻找使用 Web 服务 =)

    2013 年 12 月 5 日 下午 09:51

    1. Frédéric Harper

      很乐意提供帮助,如果您需要更多信息,请告诉我们。

      2013 年 12 月 5 日 下午 10:08

  2. SunboX

    我正在使用...

    对于 JavaScript: npmjs.org/package/closurecompiler
    对于 CSS: npmjs.org/package/yuicompressor
    对于 HTML: npmjs.org/package/html-minifier

    2013 年 12 月 5 日 下午 10:04

    1. Frédéric Harper

      很棒的包,感谢您分享信息。

      2013 年 12 月 5 日 下午 10:09

  3. Lisa Brewster

    请注意,如果您的应用是特权应用,则 Marketplace 应用审核团队仍然需要查看您未混淆的代码。提交后,您可以将源代码发送到 app-reviewers@mozilla.com,这样可以节省时间,而无需等待我们要求提供源代码。这是一个由 Mozilla 员工和社区成员组成的志愿者团队,他们自愿花费时间审核应用。

    如果您担心即使与这个小团队共享您的代码,也请告诉我们您的情况,我们将与您一起寻找解决方案。

    2013 年 12 月 5 日 下午 13:57

    1. Frédéric Harper

      没错,感谢您为这篇文章补充内容,Lisa。

      2013 年 12 月 5 日 下午 14:00

    2. SunboX

      感谢 Lisa!我偶然发现了这一点,然后想“天哪,你不能早点告诉我吗?”;o)

      2013 年 12 月 6 日 上午 00:38

    3. Sheldon

      一些合作者审阅者是其他应用的开发人员,因此,如果您以后需要将代码发送给其他开发人员,那么保护您的代码就比较复杂。

      我不明白为什么我们必须发送源代码,而在其他市场则没有必要。

      2013 年 12 月 8 日 上午 05:19

      1. Andrew Williamson

        正如 Lisa 所说,如果您担心与可能参与其他应用的社区审阅者共享可读代码,请告诉我们,我们会讨论解决方案。

        我们阅读源代码是为了检查 API 是否安全使用。目前这是手动审核,因此提交代码是最有效的方法(反混淆通常不是一个好的输出)

        2013 年 12 月 9 日 下午 18:29

        1. Luke

          在基于 OSS 的手机上运行专有的*Web* 应用似乎有点奇怪。我的意思是,当最终出现 FirefoxOS-Greasemonkey 应用时,它是否能够对这些应用进行样式设置和调整,如果它们被标记为专有呢?我们是否会收到关于哪些应用是非免费的警告?

          2013 年 12 月 9 日 下午 19:08

          1. Frédéric Harper

            所有应用都是 HTML、CSS 和 JavaScript,因此您现在可以通过使用 Web 开发者工具调试来调整这些应用(即使是 OS 应用)。

            至于付费应用,您可以在市场应用中直接看到它,就像此屏幕截图中一样 https://hacks.mozilla.ac.cn/wp-content/uploads/2013/12/paidapp.png

            2013 年 12 月 10 日 上午 08:04

  4. Brett Zamir

    这对 Web 开发人员来说也是很好的信息。

    2013 年 12 月 5 日 下午 20:09

    1. Frédéric Harper

      我们的主要关注点是 Firefox OS,但由于 Firefox OS 应用实际上是 Web 应用,因此您可以将本文中的所有内容应用于其他任何内容。

      2013 年 12 月 6 日 下午 13:45

  5. Luke

    不错的文章 - 除了为了最大限度地加载时间而进行缩小之外,还应该提到良好的编码标准可以产生很大的影响。

    例如,没有缩小器可以安全地组合 jQuery 链,而像这样的东西可能会对 js 长度和执行时间产生负面影响

    $(‘#something, .anotherthing’).addClass(‘whatever’)
    $(‘#something, .anotherthing’).attr(‘something’,’something’)
    $(‘#something, .anotherthing’).attr(‘another’,’attr’)

    此外,许多人不那么喜欢缩小器,因为它的代码用户不容易理解,可能会被认为是可疑的。(例如,大多数攻击代码都是缩小的) - 请参阅 https://gnu.ac.cn/philosophy/javascript-trap.html

    如果编译/缩小的应用是 FirefoxOS 商店的一部分,是否必须向审阅者提供原始代码?

    2013 年 12 月 6 日 下午 20:52

    1. Frédéric Harper

      好的观点,正如 Lisa 在评论部分所说:如果您的应用使用特权 API,您需要向审阅者提供未混淆的代码,以便他们能够完成他们的工作(已发布的代码将是混淆后的代码)。

      2013 年 12 月 7 日 上午 05:42

  6. niutech

    您可以通过使用 bananascript.com 或 jscrambler.com 来进一步混淆您的 JS 代码。但最好的解决方案是与他人共享您的代码!

    2013 年 12 月 7 日 上午 03:57

    1. Frédéric Harper

      感谢您分享这些其他工具。

      2013 年 12 月 7 日 上午 05:44

  7. Steve

    最近对我有很大帮助。发布它是个好主意 ;) 谢谢

    2013 年 12 月 7 日 下午 22:30

    1. Frédéric Harper

      很高兴它对您有帮助,Steve!

      2013 年 12 月 9 日 上午 09:24

  8. Sourav

    像这个 http://jsbeautifier.org/ 这样的在线 JavaScript 美化器不能用来将变量重命名回来吗?.. 虽然它可能无法完全解码代码,但可以提供一些帮助来理解以后的逻辑。

    2013 年 12 月 15 日 上午 07:35

    1. Frédéric Harper

      正如我们在文章中所写,这是尝试保护代码的第一步:它将阻止一些开发人员,但我们也列出了更高级的技术。不可能将变量重命名回来,因为没有工具可以找到混淆的变量名称背后代码的逻辑。它所能做的最好的事情是美化代码(解压缩、添加空格、对齐代码…),但您仍然需要使用像 a、b 和 c 这样的变量来理解逻辑。

      2013 年 12 月 16 日 上午 06:24

  9. Sourav

    嗯,是的,这是解码代码的人员的要求。感谢您,@Frederic

    2013 年 12 月 18 日 上午 08:16

本文的评论已关闭。