在我的学习过程中,我用Node.js编写了一个简单的命令行密码管理器,既然我已经完成了它,我将非常感谢您的评论。
评论可以基于任何主题。我正在寻找一份审查报告,内容包括:
而且,这个程序中的假设是,用户在这个程序中输入的任何name字段条目都是unique。
package.json:
{
"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:
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!');
}
}发布于 2015-11-29 15:36:49
你列出了4件你想要反馈的东西,但我只列出了第一件事情(安全性)。我的其他笔记是分散在其他3类。
这篇评论中没有代码,因为您的代码看起来很好:问题在其他地方。
您必须在命令行中输入密码(S)(同时输入主密码,如果是create命令,输入服务密码)。因此,除非您清理您的命令历史记录,否则任何访问您的计算机的人都可以清楚地看到它们。通常,您需要做的就是按下shell中的向上箭头,直到找到前面的命令为止。
是的,你可以清除你的历史记录或者只是删除敏感的线条,但是你不应该记住这一点或者设置它是自动的。因此,基本上,您是安全地存储所有密码-但您也复制他们,在清除,到~/.bash_history或同等。
最好在脚本中以sudo或ssh的安全方式提示密码(S)。我不知道如何最好地使用Node.js进行安全的密码输入,但是可以快速搜索找到了一些可能的解决方案。
如果一个人真的疑神疑鬼,那么使用Node.js本身就是个问题,因为您对内存分配和(可能特别是)去分配之类的事情没有低级别的控制。但这有点超出了这里的范围(此外,在这种情况下能够利用某些东西的人已经拥有了你的计算机,所以无论如何,所有的赌注都取消了)。
我会让accounts成为一个对象,而不是数组。这样,您就不必遍历每个帐户来找到正确的帐户,而只需说accounts[name]即可。
这还解决了另一个问题:不能有两个同名的服务。或者更确切地说,您可以使用相同的名称拥有多个服务,但是您总是会找到第一个服务,而只找到第一个服务。是的,你自己也说过你认为名字是独一无二的,但这是一个非常糟糕的假设,而且很容易避免。
同样,如果您使用一个以服务名称作为键的对象,您将立即获得一些好处:
第二点也使我们更容易:
在用户界面方面,help命令什么也不做。如果您只是在没有参数/命令的情况下运行脚本,它只会说“启动”,然后退出。如果你给一些随机的东西作为参数/命令,它也只是退出。
显示帮助的唯一方法是(自相矛盾地)已经知道命令,并且故意遗漏一些必需的args -然后得到帮助文本。但这显然是反向的。
在特性方面,您需要一种列出(或至少搜索)服务的方法。目前,您必须记住以前存储的服务的确切名称。这就像必须记住密码一样糟糕!
例如,该站点的用户名/密码会存储为"codereview“、"CodeReview”、"codereview.stackexchange.com“、"codereview.SE”、"stackexchange",还是仅仅存储"cr“或其他什么东西?当然,您可以决定用于服务名称的模式(例如,它始终是URL),但随后又回到了更新问题:如果更改密码,则无法更新服务。因此,您必须打破模式来创建一个新的服务名称,这样您就可以在稍后再找到它。如果你还记得你叫它的话,现在你不用图案了.
而且,要想让它非常、真正有用,最好让它在运行一段时间后直接接受命令,而不需要和重新要求您的主密码。即使所有服务名称都是唯一的,在找到正确的名称之前,仍然需要尝试几个名称。目前,你必须每次都重新输入你的主密码。
密码管理器可能失败的两种方法是,它比其他方法更不安全,或者如果它太麻烦,以至于您根本不使用它。
https://codereview.stackexchange.com/questions/112212
复制相似问题