简体   繁体   English

在 nodejs 中创建安全令牌

[英]Create secure token in nodejs

I have a json with the date:我有一个带有日期的 json:

{
  password: 'mySecretPwd',
  validUntil: dayjs().add(1, 'hour').toDate(),
  userId: '29038492132'
}

Now I want to create something like a magic link (myUrl/).现在我想创建类似魔术链接 (myUrl/) 的东西。 When the person is clicking on the link it has to open a webpage and the user has to write their password into a textfield.当此人单击链接时,它必须打开一个网页,并且用户必须将密码写入文本字段。 After submit it sends a request with the user input and the token from the link.提交后,它会发送一个请求,其中包含用户输入和链接中的令牌。 The server then checks whether the input is equal to the user input in the token and validate the user or not.然后服务器检查输入是否等于令牌中的用户输入并验证用户。

Now my question is how to achieve that.现在我的问题是如何实现这一目标。 My code looks like that:我的代码看起来像这样:

const crypto = require('crypto')

const algorithm = 'aes-256-ctr'
const secretKey = process.env.JWT_TEST_RESULT_SECRET

export const encrypt = (text) => {
  const iv = crypto.randomBytes(16)

  const cipher = crypto.createCipheriv(algorithm, secretKey, iv)

  const encrypted = Buffer.concat([cipher.update(text), cipher.final()])

  return {
    iv: iv.toString('hex'),
    content: encrypted.toString('hex')
  }
}

Because I need the iv for decoding, I would create the link like that: myUrl/<content>_<iv>因为我需要 iv 进行解码,所以我会创建这样的链接: myUrl/<content>_<iv>

Is that a good and secure approach or not?这是一个好的和安全的方法吗?

I hope you have some input.我希望你有一些意见。 THANKS for that感谢那

I assume you want to implement 2fa into your project.我假设您想在您的项目中实施 2fa。

for 2fa authentication you would reset your user's password whenever they try to log in then send them a token as a response and save it in your project memory (in a variable or your state management).对于 2fa 身份验证,您将在用户尝试登录时重置用户密码,然后向他们发送令牌作为响应并将其保存在项目内存中(在变量或状态管理中)。

for achieving it this way (not storing the token) it's better if you mail them after they ask to login, (you can use SendGrid for that) a link they can click on and insert their password and of course along with their new password as well (I suggest you consider either only using a token with a static password or only sending them a password).为了以这种方式实现它(不存储令牌),最好在他们要求登录后邮寄他们,(您可以为此使用SendGrid )他们可以单击并插入密码的链接,当然还有他们的新密码好吧(我建议您考虑仅使用带有静态密码的令牌或仅向他们发送密码)。 like:像:

hi dear user, your new password is: NEW_PASSWORD, insert your password in the link below: YOUR_FRONT_END.com/insert-password/?token=NEW_TOKEN&username=USERNAME (I suggest you put an expiration time of 2 min for the token)亲爱的用户,您好,您的新密码是:NEW_PASSWORD,请在以下链接中插入您的密码:YOUR_FRONT_END.com/insert-password/?token=NEW_TOKEN&username=USERNAME(我建议您将令牌的有效期设置为 2 分钟)

in the front end we are looking for the token and the username in the query in nuxt.js for example you can access the items in the query by writing this.$route.query.YOUR_DESIRED_ITEM_IN_QUERY.在前端,我们正在nuxt.js中的查询中查找令牌和用户名,例如,您可以通过编写 this.$route.query.YOUR_DESIRED_ITEM_IN_QUERY 来访问查询中的项目。

if we got our desired item in the query we ask the user to insert their password, else we redirect them to the login page.如果我们在查询中得到我们想要的项目,我们要求用户输入他们的密码,否则我们将他们重定向到登录页面。

after they insert the password we send the username, password & token to the backend to validate them.在他们输入密码后,我们将用户名、密码和令牌发送到后端以验证它们。

for validating password & hashing it:用于验证密码并对其进行哈希处理:

I would consider using bycrypt .我会考虑使用bycrypt since it is easy to use and you want to expire your password every 1 hour (which I recommend for 2 min).因为它易于使用,并且您想每 1 小时使密码过期一次(我建议 2 分钟)。 there is no need for something more complex.不需要更复杂的东西。

but if you want to have a static password (and you want it to be as secure as it gets) for your users that won't change over time unless the users themselves update it, I suggest you don't store the password(even the hashed version of it) in the database.但是如果你想为你的用户设置一个静态密码(并且你希望它尽可能安全),除非用户自己更新它,否则它不会随着时间的推移而改变,我建议你不要存储密码(即使它的散列版本)在数据库中。

for achieving that you must hash unchangeable data from the user (like the id) with your password as the key and store that hashed data in the database, this way even if the hashed data you've stored gets decrypted the attacker won't have access to the password, instead they would get the id.为了实现这一点,您必须使用您的密码作为密钥对来自用户的不可更改的数据(如 ID)进行哈希处理,并将该哈希数据存储在数据库中,这样即使您存储的哈希数据被解密,攻击者也不会拥有访问密码,而不是他们会得到 id。

FIRST STEP: convert your password to base 32. (you are going to use your password as your key latter on),第一步:将您的密码转换为 32 进制。(您将在后面使用您的密码作为您的密钥),

function convertPassword (secretKey, password) {
    
        if(!password.length || password.length > 32){
            return false;
        }
    
        if(secretKey.length !== 32) {
            return false;
        }
    
        return`${secretKey.split('').splice(password.length).join('')}${password}`;
        
    }

STEP 2: encryption第 2 步:加密

import {randomBytes, createCipheriv} from "crypto";    
function encrypt (textToEncrypt, secretKey) {
    
        if(secretKey.length !== 32){
            return false
        }
    
        const cipher = createCipheriv('aes-256-ctr', secretKey, randomBytes);
    
        const encrypted = Buffer.concat([cipher.update(textToEncrypt), cipher.final()]);
    
        return  `${randomBytes.toString('hex')}:${encrypted.toString('hex')}`
    
    }

in this step you pass user id as the "textToEncrypt" & converted password as "secretKey".在此步骤中,您将用户 ID 作为“textToEncrypt”传递,并将转换后的密码作为“secretKey”传递。

and finally the LAST STEP: decryption最后一步:解密

import {createDecipheriv} from "crypto";
    function decrypt (encryptedText, secretKey, id) => {
        
        if(secretKey.length !== 32){
            return false
        }

        const splicedHash = encryptedText.split(':')
    
        const hashIv = splicedHash[0]
        const hashContent = splicedHash[1]
    
        const decipher = createDecipheriv('aes-256-ctr', secretKey, Buffer.from(hashIv, 'hex'));
    
        const decrypted = Buffer.concat([decipher.update(Buffer.from(hashContent, 'hex')), decipher.final()]);
    
        const result = decrypted.toString();
        
        return result === id;
    }

encryptedText is the text you've stored into your database, secretKey is the password that user has inserted and you've ran through your convert password function and id is the user id that you have used to encrypt. encryptedText 是您存储到数据库中的文本,secretKey 是用户输入的密码,您已经运行了转换密码功能,id 是您用于加密的用户 ID。 this function returns true if the password is valid and false if its not.如果密码有效,则此函数返回 true,否则返回 false。

PS: if your project doesn't require THAT much security i'd still suggest you use bycrypt PS:如果您的项目不需要那么多安全性,我仍然建议您使用 bycrypt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM