【问题标题】:Rest API - Is it safe to get password in JSON responseRest API - 在 JSON 响应中获取密码是否安全
【发布时间】:2016-03-14 01:19:32
【问题描述】:

我正在使用 Node.js 和 JWT 编写一个 Rest API。

我有下面的路由来验证用户。

我想问一下,User.findOne 方法返回的 user 返回正确的密码,因此我可以检查它是否正确。 但是这样做安全吗?我做了一个console.log,它显示了密码(尽管是加密的),但仍然感觉不安全,因为有人肯定会看到?

router.post('/authenticate', function(req, res) {

  // find the user
  User.findOne({
    name: req.body.name
  }, function(err, user) {

    if (err) throw err;

    if (!user) {
      res.json({ success: false, message: 'Authentication failed. User not found.' });
    } else if (user) {

      // check if password matches
      if (user.password != req.body.password) {
        res.json({ success: false, message: 'Authentication failed. Wrong password.' });
      } else {

        // if user is found and password is right
        // create a token
        var token = jwt.sign(user, app.get('superSecret'), {
          expiresInMinutes: 1440 // expires in 24 hours
        });

        // return the information including token as JSON
        res.json({
          success: true,
          message: 'Enjoy your token!',
          token: token
        });
      }

    }

  });
});

【问题讨论】:

  • 没有。此外,将密码存储在您的服务器上并不安全。您应该使用安全的散列机制在服务器上进行身份验证,以便永远不会存储原始密码。这里有一些关于这个主题的好信息:security.stackexchange.com/questions/19525/… 在这里:crackstation.net/hashing-security.htm
  • 我们在当前项目中正在做类似的事情,到目前为止我们没有遇到任何问题。对我们来说,生成的 JWT 令牌保留在浏览器缓存中。要补充的另一件事是,我们的应用程序是组织内部的。
  • 问题不在于您以某种方式传递密码,问题在于您需要在某处未加密才能通过它。这通常已经是个坏主意了。
  • @userMod2:请查看我的最新编辑。我还添加了一些您最可能感兴趣的技术方面。如果我的回答令人满意,请务必投票并接受。

标签: json node.js rest


【解决方案1】:

没有。

此外,密码不应加密保存在数据库中,而应散列。经典环境将密码保存为例如 md5(更常见)或 bcrypt(更安全)哈希。

这样可以确保即使您的数据库被盗,也不会有人知道您用户的密码;没有办法“解密”哈希(在一亿年之内)。

当用户登录时,您将输入密码的哈希值与分配给用户的哈希值进行比较。你可以使用像bcrypt-nodejs这样的优秀模块

编辑

从技术方面来说,这并不危险。当您启动服务器时,JavaScript 会编译您的代码并在 V8 引擎中执行结果。只要保存了节点和 MySQL 之间的连接,就无法访​​问数据库返回的内容。 人们可能会转储服务器内存并希望找到正确的位。但是,如果有人获得了必要的权限,那么无论哪种方式,你都注定要失败。

我为您实现了一个示例,它未经测试,但应该向您展示它的含义。

router.post('/register', function(req, res) {
    bcrypt.hash(req.body.password, null, null, function(err, hash) {
        if (!err) {
            var newUser = new User({
                name: req.body.name,
                password: hash
            });

            newUser.save(); // ????
        }
    });
});
router.post('/authenticate', function(req, res) {
    User.findOne({
        name: req.body.name
    }, function(err, user) {
        var password = 'GP%Z!zvbk/9>Ss-R';
        var passwordHash = '$2a$10$W.zZPCaNOuR152I4qENKH.8h7I6BPcfCYBJqHPNXbVaBz0XWVxnBm'; // bcrypt of string ')RZK&M(QX"k188cw'

        if (user) {
            password = req.body.password;
            passwordHash = user.password;
        }

        bcrypt.compare(password, passwordHash, function(err, success) {
            if (success) {
                var token = jwt.sign(user, app.get('superSecret'), {
                    expiresInMinutes: 1440
                });

                res.json({
                    success: true,
                    message: 'Enjoy your token!',
                    token: token
                });
            }
            else {
                res.status(401).json({
                    success: false,
                    message: 'Authentication failed.'
                });
            }
        });
    });
});

注意:默认情况下,bcrypt 对每个哈希操作使用随机盐。这意味着,每当您对给定的输入进行哈希处理时,它每次都会产生不同的哈希值。然后将盐存储为哈希的一部分,然后可以对其进行验证。详情请查看Wikipedia

【讨论】:

  • 谢谢 - 所以散列位是有道理的。但是流程会是什么。用户输入他们的用户名+密码。然后我是否发送用户名+散列密码并在发送真假之前在服务器上检查它是否匹配?如果是这样,如何通过 https 发送散列密码。
  • 两个选项:要么在客户端散列密码并直接比较它,要么只有当你有 HTTPS 发送它并在服务器上散列它。如果有人截获流量并获取哈希,此人还可以重放哈希以获取访问权限。所以最好使用 HTTPS,不要关心其他的 ;)
  • 要记住的另一件事是,对用户不存在或密码是否错误做出不同的响应并不是一个好主意。如果这样做,在找到正确的用户名之前枚举用户是很简单的。如果您以相同的方式响应 (401 Unauthorized),攻击者将不知道哪个是错误的。出于这个原因,您可能希望在未找到用户的情况下与占位符密码进行比较,因此服务请求所花费的时间也是相同的。
  • 关于您的建议,我再次编辑了代码并添加了您提到的安全要求。还有什么顾虑吗? =)
  • 是的 - 在您的指导下,所有设置都差不多。而且我对关键概念有了很好的整体认识,以寻找围绕身份验证安全性的关键概念。再次感谢您的帮助。
猜你喜欢
  • 2015-10-26
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 2021-10-27
相关资源
最近更新 更多