云中数据_一种用于保护云中密码的加密方法

云中数据

在本文中,后端开发人员将学习为什么使用加密很重要,以及如何有效地使用加密来保护云上的用户信息(尤其是密码),从而即使在几十年之内也无法破解数据泄漏。 安全性是云中一个始终重要的主题,对于全栈开发至关重要,并且对所有产品和服务都至关重要。

让我们首先解决在考虑开发中的安全性时要做(或不做)这些简单的事情:

  • 始终选择使用别人已经检查过并检查过的别人的哈希/加密库。
  • 不要将密码打印到日志中!
  • 使用某种形式的密钥管理服务。
  • 不要在代码存储库中提交秘密(API密钥,密码)。

在本文中,我将引导您完成一个示例应用程序,以重点介绍加密关键数据的方法。 为了存储本文中介绍的密码,我们将使用SQLite数据库 ,因为它几乎可以在任何系统上使用。 几乎在所有地方都使用相同的原理和思想,并且数据库系统应该没有什么关系(尽管取决于所选择的数据库,可以有更好的散列和保护用户信息的方法)。 我还想展示如果丢失数据库文件会发生什么,但仍保持用户哈希完整且不可破解。

使用bcrypt

bcrypt是当今用于密码哈希的最广泛使用的功能之一。 它适用于大多数编程语言,并且通常有针对特定框架和数据库的超特定模块。 让我们看一下这个回购示例 。 该代码通常与Node.js一起使用,并且非常简单明了(它允许将salting和hashing函数称为syncasync )。 它还使您不必担心实现细节或如何完成操作,而可以专注于防止意外的密码泄漏。

什么是哈希,加盐和加密?

尽管散列和加密看似没有什么不同,并且可以互换使用,但是它们实际上却大不相同,并且具有不同的用例。 哈希函数需要一些输入,并且具有到输出的单向映射。 虽然存在各种各样的哈希技术和算法,但我建议使用bcrypt作为密码。 您可以在此处阅读有关加密哈希函数的更多信息,但是通常不必了解这些函数的基本细节。 在散列期间使用盐分,并作为提供给散列函数的附加信息,因此,如果发现一个散列(偶然或蛮力),则无法检查可能具有相似输入的其他散列。 例如, user_1的密码与user_2的密码相同。 如果在散列函数中使用了盐析功能,则这些用户将不会找到密码。 要了解有关此功能的更多信息,请参见此处的各种信息和示例

另外,加密是一些输入到输出的一对一映射。 一个重要的密钥区别是,如果您具有加密密钥,则它是可逆的。

您可以在以后使用散列来检查输入到另一个输入的内容,但是您不想直接存储输入(密码,引脚号等)。 发送邮件时(双方都有密钥进行编码/解码),或者要存储一些私人信息(例如家庭住址或信用卡)时,可以使用加密,但是以后需要一种方法来检索此信息。 。

前端

因为本文的重点不在前端,所以我们不会使用任何会增加复杂性或其他框架的东西。 相反,我们将使用两种形式在同一页面上进行登录/注册。 除了使用超级简单的引导程序之外,我们不会对这些形式进行任何操作,因为这不是本文的重点。

<form action="/signin" method="post">
  

      <div class="row">
        <div class="col">
         
          <input name="email" type="email" class="form-control" placeholder="email"/>
        </div>
        <div class="col">


          <input name="password" type="password" class="form-control" placeholder="password"/>
        </div>
        <div class="col">
          <button class="btn btn-dark">sign in</button>
        </div>
     

  </form>
  

  <form action="/register" method="post">
 
      <div class="row">
        <div class="col">
         
          <input name="email" type="email" class="form-control" placeholder="email"/>
        </div>
        <div class="col">
          
          <input name="password" type="password" class="form-control" placeholder="password"/>
        </div>
        <div class="col">
          <button class="btn btn-dark">register</button>
        </div>
      </div>
   
  </form>

我们还将输入信息从表单发布到后端,并且不处理检查/创建/设置会话,因为这也不符合本文的范围,并且可能会非常广泛,具体取决于目标或目的您的应用程序是。

创建后端

接下来,针对本文的目的,我们将使用Express框架和SQLite在Node.js中运行后端,以使最基本的系统成为可能。

const path = require('path')
const bcrypt = require('bcrypt')
const bodyParser = require('body-parser')
const sqlite = require('sqlite')

const express = require('express')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

const dbPromise = sqlite.open('./database.sqlite', { Promise })
const saltRounds = 10

我们在这里所做的全部工作是为数据库创建一个承诺,生成一个盐,并创建应用程序和简单的中间件以获取用户名/密码,以及加载一些我们要使用的库。

路线

就服务器将执行的操作而言,我们将有一条登录路径和一条供用户注册的路径。 他们是分开的,以了解系统中正在发生的事情,但是什么也没做(嗯,与会话/ cookies / etc相关的任何事情)。 一旦密码匹配,我们(而是简单地)说明如何对密码进行哈希处理然后进行检查。 登录路由几乎与注册路由相同,尽管我们正在检查HTML表单上的电子邮件,但在这两个路由上均未进行任何数据验证。

app.get('/', async (req,res) => {
 res.sendFile(path.join(__dirname, '/main.html'))
})

app.post('/register', async (req, res) => {
  const db = await dbPromise

  // check if user already exists
  const checkUser = await db.get('SELECT * FROM Users WHERE email = ?', req.body.email)
  if (checkUser) {
    return res.send('user already exists')
  }

  const hashedPassword = await bcrypt.hash(req.body.password, saltRounds)
  const resp = await db.run(`INSERT INTO Users VALUES(?,?)`, req.body.email, hashedPassword)
  res.send('registered')
})

注册路由检查用户是否存在于数据库中,以及我们是否使用哈希密码将其插入数据库中。 请记住,我们没有采取任何措施来减轻SQL注入或各种其他形式的黑客入侵/滥用。 如果用户不存在,我们将使用bcrypt哈希函数对密码进行哈希处理,并为我们添加盐值,因为我们提供了盐值计算的轮数。 这种散列使我们能够以某种方式存储用户的密码,以便将来在用户输入密码时可以对其进行检查。我们个人不具备查询密码的能力。 另外,我们不应该将密码打印到用户的日志中,并且我们可能希望创建使用数据库模型来检查密码并将用户的密码保存到哈希中的功能

虽然登录路径几乎相同(并且我们可以很容易地对其进行重构以使其更加DRY ,但是我们将在此处进行理解),但以下内容略有不同:

const passwordMatch = await bcrypt.compare(req.body.password, user.password)

所有这些操作就是使用Bcrypt比较哈希密码和用户在前端为我们输入的密码,并返回true或false。 由于盐已合并到哈希中,因此我们无需显式使用它进行比较。 以下是要运行的完整server.js

虽然登录路径几乎相同(并且我们可以很容易地对其进行重构以使其更干燥,但是我们将在此处进行理解),但以下内容略有不同:

const passwordMatch = await bcrypt.compare(req.body.password, user.password)

上面所做的所有操作就是使用Bcrypt比较哈希密码和用户在前端为我们键入并返回true或false的密码。 由于盐已合并到哈希中,因此我们无需显式使用它进行比较。 下面的代码清单是要运行的完整server.js

const bcrypt = require('bcrypt')
const bodyParser = require('body-parser')


const express = require('express')
const app = express()

app.post('/register', async (req, res) => {
  const db = await dbPromise



  const hashedPassword = await bcrypt.hash(req.body.password, saltRounds)
  const resp = await db.run(`INSERT INTO Users VALUES(?,?)`, req.body.email, hashedPassword)
  res.send('registered')
})


app.post('/signin', async (req, res) => {
  const db = await dbPromise
  const user = await db.get('SELECT * FROM Users WHERE email = ?', req.body.email)

  if (!user) {
    return res.send('user doesnt exist')
  }


  const passwordMatch = await bcrypt.compare(req.body.password, user.password)
  if (passwordMatch) {

    return res.send('signed in')
  }
  res.send('password does not match')
})


app.listen(PORT, async () => {

  console.log(`app listening at http://localhost:${PORT}`)
})

现在安装依赖项:

yarn add bcrypt express body-parser sqlite

运行服务器Node server.js并打开http://localhost:8080 。 然后尝试登录并创建用户,然后再次登录。

通过网络发送未加密的密码!

尽管本文只是向您展示如何存储和哈希密码,并且您并未保留用户的纯文本密码,但是由于我们未使用HTTPS,因此我们仍在浏览器和后端之间发送纯文本。 如果该示例在生产中,则黑客可以轻松地看到在服务器和客户端之间发送的这些密码(登录和注册)(如果它们在此通信过程中)。 预防中间人黑客的方式有很多种,但是为了简单起见,我们将在Express中进行处理,并生成自签名SSL证书作为此方法的示例。 。 请记住,这些签名的方式与从LetsEncrypt或其他各种SSL / TLS证书提供者获得证书的方式不同。

首先,我们需要通过软件包管理器或从其官方网站安装OpenSSL。 在macOS上,如果已经安装了自制软件 ,则可以简单地编写:

brew-install Openssl

接下来,您将要运行命令以生成密钥和证书:

openSSL req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 30

该命令将要求您输入一些信息,但是最后您将拥有key.pemcert.pem 。 通过这些,您可以将以下内容添加到server.js的顶部(请注意,我们现在正在使用来自Node.js的https标准库):

const fs = require('fs')
const https = require('https')

const options = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
}

在代码的底部,我们以前有:

const PORT = 8080
app.listen(PORT, async () => {
  const db = await dbPromise
  await db.run("CREATE TABLE IF NOT EXISTS Users (email TEXT, password TEXT)")
  console.log(`app listening at http://localhost:${PORT}`)
})

我们将上面的先前代码更改为:

const PORT = 8081
https.createServer(options, app)
  .listen(PORT, async () => {
    const db = await dbPromise
    await db.run("CREATE TABLE IF NOT EXISTS Users (email TEXT, password TEXT)")
    console.log(`app listening at https://localhost:${PORT}`)
  })

此时,我们将仅使用HTTPS,并将加密的密码发送到我们的服务器,并且在保存到数据库时将对密码进行哈希处理。

最坏的情况:数据库泄漏

假设我们的服务器被黑客入侵,或者发生了其他一些利用,并且我们SQLite(或任何数据库)被泄漏。 尽管这种情况很糟糕,但我们至少可以确信用户密码本身应被安全使用,并且可以最大程度地减少要求用户在其他地方更改其密码的机会。 例如,下面的图1显示,哈希表没有看到用户graham@test.xyz的密码secret ,而是对试图使用它的黑客毫无用处。

fig01.png

结论:云安全的其他替代方案

虽然创建和存储用户数据的实例可能是最直接的方法,但是有些替代方法可以让您维护有关用户身份的信息(通过OAuth之类的方法,该方法允许用户登录外围服务,然后返回身份验证信息,或通过电子邮件登录)。 替代路线意味着您不必存储有关用户的秘密信息,这可能是潜在的责任。 尽管可能有很多方法来混淆和保护您存储在某个数据库中的用户信息,但是在用于最佳安全性实践的任何服务器框架文档和数据库中,几乎总是存在指导。

例如,访问Express.js的最佳实践,因为它列出了本文未涉及的众多主题。 此处未涵盖的一个示例是使用Helmet来防止一些众所周知的HTTP标头漏洞 。 对于诸如SQL注入缓解和速率限制之类的事情,也有一些建议,以防止蛮力猜测。 尽管没有任何一种简单的方法可以缓解所有这些问题,但在线查看文档或建议绝非难事。


翻译自: https://www.ibm.com/developerworks/security/library/se-protecting-passwords-cloud/index.html

云中数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值