首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Node.js中的简单密码管理器

Node.js中的简单密码管理器
EN

Code Review用户
提问于 2015-11-29 08:49:18
回答 1查看 3.3K关注 0票数 3

在我的学习过程中,我用Node.js编写了一个简单的命令行密码管理器,既然我已经完成了它,我将非常感谢您的评论。

评论可以基于任何主题。我正在寻找一份审查报告,内容包括:

  • 安全漏洞。
  • 业绩和效率。
  • 实现这一目标的更好方法。
  • 违反任何概念、最佳做法或公约的行为。

而且,这个程序中的假设是,用户在这个程序中输入的任何name字段条目都是unique

package.json

代码语言:javascript
复制
{
  "name": "password-manager",
  "version": "1.0.0",
  "description": "Manage your passwords locally",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node app.js"
  },
  "author": "Hassan Althaf",
  "license": "MIT",
  "dependencies": {
    "crypto-js": "^3.1.5",
    "node-persist": "0.0.6",
    "yargs": "^3.15.0"
  }
}

app.js

代码语言:javascript
复制
console.log('Starting password manager...');

var storage = require('node-persist');
storage.initSync();

var crypto = require('crypto-js');

var argv = require('yargs')
    .command('create', 'Create an entry to store some service credentials.', function (yargs) {
        yargs.options({
            name: {
                demand: true,
                alias: 'n',
                description: 'Service name.',
                type: 'string'
            },
            username: {
                demand: true,
                alias: 'u',
                description: 'The username or email for the account.',
                type: 'string'
            },
            password: {
                demand: true,
                alias: 'p',
                description: 'The password for the account.',
                type: 'string'
            },
            masterPassword: {
                demand: true,
                alias: 'm',
                description: 'The master password to access the system.',
                type: 'string'
            }
        }).help('help');
    })
    .command('get', 'Fetch credentials for a particular service.', function (yargs) {
        yargs.options({
            name: {
                demand: true,
                alias: 'n',
                description: 'Service name.',
                type: 'string'
            },
            masterPassword: {
                demand: true,
                alias: 'm',
                description: 'The master password to access the system.',
                type: 'string'
            }
        }).help('help');
    })
    .help('help')
    .argv;

function getAccounts (masterPassword) {
    var encryptedAccount = storage.getItemSync('accounts');
    var accounts = [];

    if (typeof encryptedAccount !== 'undefined') {
        var bytes = crypto.AES.decrypt(encryptedAccount, masterPassword);
        accounts = JSON.parse(bytes.toString(crypto.enc.Utf8));
    }

    return accounts;
}

function saveAccounts (accounts, masterPassword) {
    var encryptedAccounts = crypto.AES.encrypt(JSON.stringify(accounts), masterPassword);

    storage.setItemSync('accounts', encryptedAccounts.toString());

    return accounts;
}

function createAccount (account, masterPassword) {
    var accounts = getAccounts(masterPassword);

    accounts.push(account);

    saveAccounts(accounts, masterPassword);

    return account;
}

function getAccount (accountName, masterPassword) {
    var accounts = getAccounts(masterPassword);
    var foundAccount = null;

    for(var iterator = 0; iterator < accounts.length; iterator++) {
        if(accounts[iterator].name === accountName) {
            foundAccount = accounts[iterator];
        }
    }

    return foundAccount;
}

if(argv._[0] === 'create') {
    try {
        var createdAccount = createAccount({
            name: argv.name, 
            username: argv.username, 
            password: argv.password
        }, argv.masterPassword);

        console.log('Account created!');
        console.log(createdAccount);
    } catch (error) {
        console.log('Unable to create account!');
    }
}

if(argv._[0] === 'get') {
    try {
        var account = getAccount(argv.name, argv.masterPassword);

        if(account === null) {
            console.log('Account not found!');
        } else {
            console.log('Account found!');
            console.log(account);
        }
    } catch (error) {
        console.log('Unable to fetch account!');
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-11-29 15:36:49

你列出了4件你想要反馈的东西,但我只列出了第一件事情(安全性)。我的其他笔记是分散在其他3类。

这篇评论中没有代码,因为您的代码看起来很好:问题在其他地方。

安全

您必须在命令行中输入密码(S)(同时输入主密码,如果是create命令,输入服务密码)。因此,除非您清理您的命令历史记录,否则任何访问您的计算机的人都可以清楚地看到它们。通常,您需要做的就是按下shell中的向上箭头,直到找到前面的命令为止。

是的,你可以清除你的历史记录或者只是删除敏感的线条,但是你不应该记住这一点或者设置它是自动的。因此,基本上,您是安全地存储所有密码-但您也复制他们,在清除,到~/.bash_history或同等。

最好在脚本中以sudossh的安全方式提示密码(S)。我不知道如何最好地使用Node.js进行安全的密码输入,但是可以快速搜索找到了一些可能的解决方案

如果一个人真的疑神疑鬼,那么使用Node.js本身就是个问题,因为您对内存分配和(可能特别是)去分配之类的事情没有低级别的控制。但这有点超出了这里的范围(此外,在这种情况下能够利用某些东西的人已经拥有了你的计算机,所以无论如何,所有的赌注都取消了)。

其他东西

我会让accounts成为一个对象,而不是数组。这样,您就不必遍历每个帐户来找到正确的帐户,而只需说accounts[name]即可。

这还解决了另一个问题:不能有两个同名的服务。或者更确切地说,您可以使用相同的名称拥有多个服务,但是您总是会找到第一个服务,而只找到第一个服务。是的,你自己也说过你认为名字是独一无二的,但这是一个非常糟糕的假设,而且很容易避免。

同样,如果您使用一个以服务名称作为键的对象,您将立即获得一些好处:

  • 每个服务名称都有一组凭据,
  • 您可以立即获取正确的,而不需要循环。

第二点也使我们更容易:

  • 知道服务是否存在,
  • 删除服务;现在,您只能添加新服务,
  • 更新服务;现在,您只能添加另一个同名的服务(稍后您将找不到),或者创建一个新的名称(这会带来自身的问题,请参见下面)。

在用户界面方面,help命令什么也不做。如果您只是在没有参数/命令的情况下运行脚本,它只会说“启动”,然后退出。如果你给一些随机的东西作为参数/命令,它也只是退出。

显示帮助的唯一方法是(自相矛盾地)已经知道命令,并且故意遗漏一些必需的args -然后得到帮助文本。但这显然是反向的。

在特性方面,您需要一种列出(或至少搜索)服务的方法。目前,您必须记住以前存储的服务的确切名称。这就像必须记住密码一样糟糕!

例如,该站点的用户名/密码会存储为"codereview“、"CodeReview”、"codereview.stackexchange.com“、"codereview.SE”、"stackexchange",还是仅仅存储"cr“或其他什么东西?当然,您可以决定用于服务名称的模式(例如,它始终是URL),但随后又回到了更新问题:如果更改密码,则无法更新服务。因此,您必须打破模式来创建一个新的服务名称,这样您就可以在稍后再找到它。如果你还记得你叫它的话,现在你不用图案了.

而且,要想让它非常、真正有用,最好让它在运行一段时间后直接接受命令,而不需要和重新要求您的主密码。即使所有服务名称都是唯一的,在找到正确的名称之前,仍然需要尝试几个名称。目前,你必须每次都重新输入你的主密码。

密码管理器可能失败的两种方法是,它比其他方法更不安全,或者如果它太麻烦,以至于您根本不使用它。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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