美文网首页freeCodeCamp
管中窥豹谈谈简单的密文存储

管中窥豹谈谈简单的密文存储

作者: 莫珂 | 来源:发表于2017-11-08 13:17 被阅读102次

    本文首发于:我的个人博客 墨客

    本文源码地址: https://github.com/edwardwang0302/encrypt_demo
    如需转载请注明出处


    这篇文章会讲什么?

    写这篇文章的目的主要是想讲讲常见的密文存储方式,从明文存储,到Md5加密,再到“加盐”(salting)和多次md5加密,涉及到的只是加密方面非常小的一部分。

    涉及到的环境和工具

    如果你是把本文当做一个扩展来看,完全没有必要安装和配置以下的环境,安装这些只是为了方便演示

    1. Node.js环境 Npm包管理 Express框架 Nodemon来热加载 body-parser插件解析post请求的body
    2. MongoDB数据库 mongoose操作数据库
    3. PostMan工具

    正式开始

    在开始之前我们假设你的电脑已经安装了Node.js环境,Npm包管理以及MongoDB

    初始化工程

    mkdir encrypt
    cd encrypt
    npm init
    

    接下来一路回车,就是提示一些npm工程初始化的信息,如下图


    Screen Shot 2017-11-08 at 10.13.31.png

    安装Express,Nodemon,Mongoose

    npm i -g nodemon
    npm i -S express mongoose body-parser
    

    稍微等待之后,我们目录下面多了node_modules目录,里面是一些依赖

    开始写后台代码

    mkdir server        //存放server代码目录
    cd server       
    touch server.js     //server代码
    touch model.js      //存放数据库相关
    touch user.js       //存放和user相关的后台代码
    

    打开model.js文件,复制下面代码:

    const mongoose = require('mongoose')
    // 连接mongo 并使用encrypt这个集合
    const DB_URL = 'mongodb://localhost:27017/encrypt'
    mongoose.connect(DB_URL)
    
    // 定义存储用户数据的模型
    const models = {
        user:{
            'user':{type:String, require:true},
            'pwd':{type:String, require:true},
        },
        chat:{
    
        }
    }
    // 根据模型创建集合
    for(let m in models) {
        mongoose.model(m, new mongoose.Schema(models[m]))
    }
    
    module.exports = {
        getModel:function(name) {
            return mongoose.model(name)
        }
    }
    

    打开user.js文件,复制下面代码

    const express = require('express')
    const Router = express.Router()
    const models = require('./model')
    const User = models.getModel('user')
    
    Router.get('/all', function(req, res) {
        // 查找所有的用户信息
        User.find({}, function(err, doc) {
            return res.json(doc)
        })
    })
    Router.post('/regist', function(req, res) {
        const { user, pwd } = req.body
        User.findOne({user}, function(err, doc) {
            if(doc) {
                return res.json({code:1, msg: '用户名存在'})
            }
            User.create({user, pwd}, function(e, d) {
                if(e) {
                    return res.json({code:1, msg: '注册失败'})
                }
                return res.json({code:1, msg: '注册成功'})
            })
        })
    })
    
    module.exports = Router
    

    打开server.js文件,复制下面代码:

    const express = require('express')
    const bodyParser = require('body-parser')
    const userRouter = require('./user')
    
    const app = express()
    
    // 用于解析post请求的body
    app.use(bodyParser.json())
    // 把所有user操作都路由到/user这个url下面
    app.use('/user', userRouter)
    
    app.listen(9000, function() {
        console.log('Hi, guys! I am listening prot 9000')
    })
    

    前后端联调

    这里其实不是真正意义上的前后端联调,这为了简单使用PostMan工具发送一个请求,大家可以随意用自己喜欢的工具

    1. 启动MongoDB
      如果你已经安装好了MongoDB,直接使用mongod命令就可以启动
    mongod
    
    1. 启动后台服务器
    nodemon server/server.js
    

    看到下图说明启动成功


    Screen Shot 2017-11-08 at 11.00.04.png
    1. 使用PostMan发送注册请求


      Screen Shot 2017-11-08 at 13.08.58.png
    2. 查看已注册的用户
      可以看到这里我们的用户信息已经保存了,不过是明文保存的,这点当然不能接受,下面我们将怎么变为密文


      Screen Shot 2017-11-08 at 11.01.35.png

    从明文到md5加密

    加密中最简单的方式是使用md5这种单向的不可逆的方式,我们这里借助utility这个工具来加密

    1. 停掉刚刚的服务,安装utility
    npm i -S utility
    
    1. 修改user.js
    ...省略一些内容
    // 引用utility
    const utils = require('utility')
    
    ...省略一些内容
    
    Router.post('/regist', function(req, res) {
        const { user, pwd } = req.body
        User.findOne({user}, function(err, doc) {
            if(doc) {
                return res.json({code:1, msg: '用户名存在'})
            }
            // 这里使用md5加密
            User.create({user, pwd: utils.md5(pwd)}, function(e, d) {
                if(e) {
                    return res.json({code:1, msg: '注册失败'})
                }
                return res.json({code:0, msg: '注册成功'})
            })
        })
    })
    
    module.exports = Router
    
    1. 启动服务,重新注册一个用户u2 密码123

    再次查看请求 http://localhost:9000/user/all

    可以看到这已经使用md5加密过了,这种方式依然存在着一些问题,我们下面展开讨论

    从md5加密到“加盐”

    到目前为止我们已经使用了最简单的加密方式md5,可是有些网站使用“彩虹表”的方式可以逆向这种加密,举个例子:
    打开 http://www.cmd5.com 直接输入我们的密文: 202cb962ac59075b964b07152d234b70 点击查询

    Screen Shot 2017-11-08 at 12.50.50.png

    天哪理论上不可逆的md5居然被破译了,怎么降低这种概率呢(注意这里说的是降低),那么就要使用我们之前提到的“加盐”(salting)的方式。加盐其实就是我们自己加密的过程中加入一串复杂的字符串。

    我们修改user.js文件,在module.exports前面加入下面函数定义

    // 加密加盐
    function salting(pwd) {
        const salt = 'i_wanna_be_bestx8yza6!@#IUHJH~~'
        return utils.md5(pwd+salt)
    }
    

    修改加密部分

    User.create({user, pwd: utils.md5(pwd)}, function(e, d)
    // 将上面的这句换成
    User.create({user, pwd: salting(pwd)}, function(e, d)
    

    重启服务,再次注册一个u3 密码为123,注册成功后我们可以看到密码,再去刚刚的网站发现已经不能解析了

    多次md5增加复杂度

    修改上面提到的salting函数,多加一次md5加密

    function salting(pwd) {
        const salt = 'i_wanna_be_bestx8yza6!@#IUHJH~~'
        return utils.md5(utils.md5(pwd+salt))
    }
    

    总结

    至此,我们大致完成了一般简单的系统加密过程的演进,对于一些有更高要求的加密系统来说,上述的方式依然是不安全的,只是简单的增加了破译的时间成本和空间成本;加密方式目前有很多,当然也已经不属于我们“管中窥豹”的简单定义了,大家有兴趣可以再去深入研究,就先写到这里吧。

    相关文章

      网友评论

        本文标题:管中窥豹谈谈简单的密文存储

        本文链接:https://www.haomeiwen.com/subject/buwemxtx.html