首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HTML5存储引擎的透明加密(S)

HTML5存储引擎的透明加密(S)
EN

Code Review用户
提问于 2014-11-14 14:45:06
回答 1查看 382关注 0票数 3

我正在寻找有关JS代码的反馈和可能的建议,该代码使用HTML5 localStoragesessionStorage或折旧cookie选项对用户指定的数据执行透明的对称加密/解密。

该项目可以在这里上完整地查看。

代码语言:javascript
复制
/**
 * secStore.js - Encryption enabled browser storage
 *
 * https://www.github.com/jas-/secStore.js
 *
 * Author: Jason Gerfen <jason.gerfen@gmail.com>
 * License: GPL (see LICENSE)
 */
(function(window, undefined) {

  'use strict';

  /**
   * @function secStore
   * @abstract Namespace for saving/retrieving encrypted HTML5 storage engine
   * data
   */
  var secStore = secStore || function() {

    /**
     * @var {Object} defaults
     * @abstract Default set of options for plug-in
     *
     * @param {Boolean} encrypt Optionally encrypt stored data
     * @param {Object} data Data to be setd (JSON objects)
     * @param {String} passphrase Passphrase to use (optional)
     * @param {String} storage Storage mechanism (local, session or cookies)
     */
    var defaults = {
      encrypt: false,
      data: {},
      key: 'secStore.js',
      passphrase: '',
      storage: 'local'
    };

    /**
     * @method setup
     * @scope private
     * @abstract Initial setup routines
     */
    var setup = setup || {

      /**
       * @function set
       * @scope private
       * @abstract Initialization
       *
       * @param {Object} opts Plug-in option object
       */
      init: function(opts) {
        opts.passphrase = (opts.encrypt && opts.passphrase) ?
          opts.passphrase : (opts.encrypt && !opts.passphrase) ?
          crypto.key(opts) : false;
      }
    };

    /**
     * @method storage
     * @scope private
     * @abstract Interface to handle storage options
     */
    var storage = storage || {

      /**
       * @function quota
       * @scope private
       * @abstract Tests specified storage option for current amount of space available.
       *  - Cookies: 4K
       *  - localStorage: 5MB
       *  - sessionStorage: 5MB
       *  - default: 5MB
       *
       * @param {String} t Type of storage specified
       *
       * @returns {Boolean}
       */
      quota: function(storage) {
        var max = /local|session/.test(storage) ? 1024 * 1025 * 5 :
          1024 * 4,
          cur = libs.total(storage),
          total = max - cur;

        if (total <= 0) {
          return false;
        }

        return true;
      },

      /**
       * @function set
       * @scope private
       * @abstract Interface for saving to available storage mechanisms
       *
       * @param {Object} opts Default options
       * @param {Function} cb Callback function
       *
       * @returns {Boolean}
       */
      set: function(opts, cb) {
        var ret = false;

        if (!storage.quota(opts.storage))
          cb('Browser storage quota has been exceeded.');

        opts.data = (opts.encrypt) ?
          sjcl.encrypt(opts.passphrase, storage.fromJSON(opts.data)) :
          storage.fromJSON(opts.data);

        switch (opts.storage) {
          case 'cookie':
            ret = this.cookie.set(opts);
            break;
          case 'local':
            ret = this.local.set(opts);
            break;
          case 'session':
            ret = this.session.set(opts);
            break;
          default:
            ret = this.local.set(opts);
            break;
        }
        if (!ret) {
          cb('Error occured saving data');
        } else {
          cb(null, 'Successfully set data');
        }
      },

      /**
       * @function get
       * @scope private
       * @abstract Interface for retrieving from available storage mechanisms
       *
       * @param {Object} opts Default options
       * @param {Function} cb Callback function
       *
       * @returns {Object}
       */
      get: function(opts, cb) {
        var ret = {};

        switch (opts.storage) {
          case 'cookie':
            ret = this.cookie.get(opts);
            break;
          case 'local':
            ret = this.local.get(opts);
            break;
          case 'session':
            ret = this.session.get(opts);
            break;
          default:
            ret = this.local.get(opts);
            break;
        }

        ret = sjcl.decrypt(opts.passphrase, ret);
        ret = storage.toJSON(ret);

        if (/object/.test(ret)) {
          cb(null, ret);
        } else {
          cb('Error occured retrieving storage data');
        }
      },

      /**
       * @function fromJSON
       * @scope private
       * @abstract Convert to JSON object to string
       *
       * @param {Object|Array|String} obj Object, Array or String to convert to JSON object
       *
       * @returns {String}
       */
      fromJSON: function(obj) {
        return (/object/.test(typeof(obj))) ? JSON.stringify(obj) : obj;
      },

      /**
       * @function toJSON
       * @scope private
       * @abstract Creates JSON object from formatted string
       *
       * @param {String} obj Object to convert to JSON object
       *
       * @returns {Object}
       */
      toJSON: function(obj) {
        return (/string/.test(typeof(obj))) ? JSON.parse(obj) : obj;
      },

      /**
       * @method cookie
       * @scope private
       * @abstract Method for handling setting & retrieving of cookie objects
       */
      cookie: {

        /**
         * @function set
         * @scope private
         * @abstract Handle setting of cookie objects
         *
         * @param {String} key Key to use for cookies
         * @param {String|Object} value String or object to place in cookie
         *
         * @returns {Boolean}
         */
        set: function(key, value) {
          var d = new Date();
          d.setTime(d.getTime() + (30 * 24 * 60 * 60 * 1000));
          document.cookie = key + '=' + value + ';expires=' + d.toGMTString() +
            ';path=/;domain=' + this.domain();
          return true;
        },

        /**
         * @function get
         * @scope private
         * @abstract Handle retrieval of cookie objects
         *
         * @param {String} key cookie key
         *
         * @returns {String|False}
         */
        get: function(key) {
          var i, x, y, z = document.cookie.split(";");
          for (i = 0; i < z.length; i++) {
            x = z[i].substr(0, z[i].indexOf('='));
            y = z[i].substr(z[i].indexOf('=') + 1);
            x = x.replace(/^\s+|\s+$/g, '');
            if (x == key) {
              return unescape(y);
            }
          }
          return false;
        },

        /**
         * @function domain
         * @scope private
         * @abstract Provides current domain of client for cookie realm
         *
         * @returns {String}
         */
        domain: function() {
          return location.hostname;
        }
      },

      /**
       * @method local
       * @scope private
       * @abstract Method for handling setting & retrieving of localStorage objects
       */
      local: {

        /**
         * @function set
         * @scope private
         * @abstract Handle setting & retrieving of localStorage objects
         *
         * @param {Object} opts Application defaults
         *
         * @returns {Boolean}
         */
        set: function(opts) {
          try {
            window.localStorage.setItem(opts.key, opts.data);
            return true;
          } catch (e) {
            return false;
          }
        },

        /**
         * @function get
         * @scope private
         * @abstract Handle retrieval of localStorage objects
         *
         * @param {Object} o Application defaults
         *
         * @returns {Object|String|Boolean}
         */
        get: function(opts) {
          return window.localStorage.getItem(opts.key);
        }
      },

      /**
       * @method session
       * @scope private
       * @abstract Method for handling setting & retrieving of sessionStorage objects
       */
      session: {

        /**
         * @function set
         * @scope private
         * @abstract Set session storage objects
         *
         * @param {Object} o Application defaults
         *
         * @returns {Boolean}
         */
        set: function(opts) {
          try {
            window.sessionStorage.setItem(opts.key, opts.data);
            return true;
          } catch (e) {
            return false;
          }
        },

        /**
         * @function get
         * @scope private
         * @abstract Retrieves sessionStorage objects
         *
         * @param {Object} opts Application defaults
         *
         * @returns {Object|String|Boolean}
         */
        get: function(opts) {
          return window.sessionStorage.getItem(opts.key);
        }
      }
    };

    /**
     * @method crypto
     * @scope private
     * @abstract Interface to handle encryption option
     */
    var crypto = crypto || {

      /**
       * @function key
       * @scope private
       * @abstract Prepares key for encryption/decryption routines
       *
       * @returns {String}
       */
      key: function() {
        var pass = crypto.uid(),
          salt = crypto.salt(pass);

        return sjcl.codec.hex.fromBits(sjcl.misc.pbkdf2(pass, salt,
          10000, 256));
      },

      /**
       * @function uid
       * @scope private
       * @abstract Generates a machine identifier
       *
       * @returns {String}
       */
      uid: function() {
        var ret = window.navigator.appName +
          window.navigator.appCodeName +
          window.navigator.product +
          window.navigator.productSub +
          window.navigator.appVersion +
          window.navigator.buildID +
          window.navigator.userAgent +
          window.navigator.language +
          window.navigator.platform +
          window.navigator.oscpu;
        return ret.replace(/\s/, '');
      },

      /**
       * @function salt
       * @scope private
       * @abstract Creates salt from string & iv
       *
       * @param {String} str Machine identification used as salt
       *
       * @returns {String}
       */
      salt: function(str) {
        var rec, ret, hash = [],
          slt = crypto.iv(str);

        hash[0] = sjcl.hash.sha256.hash(str), rec = [], rec = hash[0],
          ret;

        for (var i = 1; i < 3; i++) {
          hash[i] = sjcl.hash.sha256.hash(hash[i - 1].concat(slt));
          ret = rec.concat(hash[i]);
        }
        return JSON.stringify(sjcl.codec.hex.fromBits(ret));
      },

      /**
       * @function iv
       * @scope private
       * @abstract Creates IV based on UID
       *
       * @param {String} str Supplied string
       *
       * @returns {String}
       */
      iv: function(str) {
        return encodeURI(str.replace(/-/gi, '').substring(16, Math.ceil(
          16 * str.length) % str.length));
      }
    };

    /**
     * @method libs
     * @scope private
     * @abstract Miscellaneous helper libraries
     */
    var libs = libs || {

      /**
       * @function total
       * @scope private
       * @abstract Returns size of specified storage
       *
       * @param {String} engine Storage mechanism
       *
       * @returns {Insteger}
       */
      total: function(storage) {
        var current = '',
          engine = window.storage + 'Storage';

        for (var key in engine) {
          if (engine.hasOwnProperty(key)) {
            current += engine[key];
          }
        }

        return current ? 3 + ((current.length * 16) / (8 * 1024)) : 0;
      },

      /**
       * @function size
       * @scope private
       * @abstract Perform calculation on objects
       *
       * @param {Object|Array} obj The object/array to calculate
       *
       * @returns {Integer}
       */
      size: function(obj) {
        var n = 0;

        if (/object/.test(typeof(obj))) {
          for (var i in obj) {
            if (obj.hasOwnProperty(obj[i])) n++;
          }
        } else if (/array/.test(typeof(obj))) {
          n = obj.length;
        }
        return n;
      },

      /**
       * @function merge
       * @scope private
       * @abstract Perform preliminary option/default object merge
       *
       * @param {Object} defaults Application defaults
       * @param {Object} obj User supplied object
       *
       * @returns {Object}
       */
      merge: function(defaults, obj) {
        defaults = defaults || {};

        for (var item in defaults) {
          if (defaults.hasOwnProperty(item)) {
            obj[item] = (/object/.test(typeof(defaults[item]))) ?
              this.merge(obj[item], defaults[item]) : defaults[item];
          }
          obj[item] = defaults[item];
        }

        return obj;
      }
    };

    /**
     * @function get
     * @scope public
     * @abstract Retrieves storage engine data
     *
     * @param {Object} obj User supplied options
     * @param {Function} cb User supplied callback function
     */
    secStore.prototype.get = function(obj, cb) {
      cb = cb || obj;

      var opts = libs.merge(obj, defaults);

      setup.init(opts);

      storage.get(opts, cb);
    };

    /**
     * @function set
     * @scope public
     * @abstract Saves data to specified storage engine
     *
     * @param {Object} obj User supplied options
     * @param {Function} cb User supplied callback function
     */
    secStore.prototype.set = function(obj, cb) {
      cb = cb || obj;

      var opts = libs.merge(obj, defaults);

      setup.init(opts);

      storage.set(opts, cb);
    };

  };

  /* secStore.js, do work */
  window.secStore = secStore;

})(window);
EN

回答 1

Code Review用户

回答已采纳

发布于 2014-11-14 21:42:26

从一次又一次:

  • 耶,GPL!我喜欢GPL,请注意,通过在这里发布您的代码,任何人现在都可以将其用作非-GPL。
  • 这是我所见过的最可读的嵌套三元函数:函数(Opts){ opts.passphrase = (opts.encrypt && opts.passphrase)?opts.passphrase:(opts.encrypt & !opts.passphrase)?crypto.key(opts):false;}您可以考虑这个init: function(opts) { opts.passphrase = opts.encrypt?(opts.passphrase \x\x{e76f}\x{e76f}(Opts):false;}
  • 考虑到其他代码的巧妙性,我惊讶地发现:如果(总<= 0) {返回假;}返回真;考虑返回!(总<= 0);//或。回归总数> 0;
  • 如果您的开关等于如下所示的确切函数名: switch (opts.storage) { case 'cookie':ret = this.cookie.set(opts);坏掉;case 'local':ret = this.local.set(opts);case;case‘会话’:ret = this.session.set(opts);ret =this.session.set(Opts);默认值: ret = this.local.set(opts);}您可以简单地动态访问函数ret=这个opt.storage?opt.storage.set(Opts):this.local.set(opts);
  • var i, x, y, z = document.cookie.split(";"); <- x,y,z是不幸的变量名,我相信它们在学校里经常被取笑。
  • 要命名一个机器标识符uid并不理想,通常uid是为唯一记录id保留的
  • 我没有检查任何密码,但至少您依赖第三方库,即90%的工作。
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/69850

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档