• sql注入:窃取数据库内容
  • XSS攻击:窃取前端的cookie内容
  • 密码加密:保障用户信息安全(重要!)

# 补充

  • server 端的攻击方式非常多,以防手段也非常多
  • 本文章之讲常见的、能通过 web server (nodejs) 层面预防的
  • 有些攻击需要硬件和服务来支持(需要 OP 支持),如 DDOS

# sql注入

  • 最原始、最简单的攻击,从有了web2.0就有了sql注入攻击
  • 攻击方式:输入一个sql片段,最终拼接成一段攻击代码
  • 预防措施:使用mysql的escape函数处理输入内容即可

# 演示

比如现在的登录验证,是通过字符串拼接后执行 SQL 语句来实现的:

const { exec } = require("../db/mysql");

const login = (username, password) => {
  const sql = `
    select username, realname from users where
    username = '${username}' and password = '${password}';
  `;
  return exec(sql).then((rows) => {
    return rows[0] || {};
  });
};

module.exports = {
  login,
};

SQL 语句例如:

select username, realname from users where username = 'zhangsan' and password = '123456';

# 免密码登录

但是如果输入的用户名是 zhangsan' -- ,便将后边的语句注释了,就算密码错误也可以用 zhangsan 的账号登录

select username, realname from users where username = 'zhangsan' -- ' and password = '123456';

# 删库

如果现在有人的用户名是 zhangsan';delect from users; -- ,后果更严重,直接数据库全删了。

select username, realname from users where username = 'zhangsan';delect from users; -- ' and password = '123456';

# 解决方法

使用转义字符。将敏感的符号转义可以避免这个问题。mysql 库提供了 mysql.escape 方法,用来转义内容:

// src/db/mysql.js
const mysql = require('mysql')
const { MYSQL_CONFIG } = require('../config/db')

// 创建链接对象
const con = mysql.createConnection(MYSQL_CONFIG)

// 开始链接
con.connect()

// 统一执行 sq l的函数
function exec(sql) {
	return new Promise((resolve, reject) => {
		con.query(sql, (err, result) => {
			if (err) {
				reject(err)
				return
			}
			resolve(result)
		})
	})
}

module.exports = {
	exec,
	escape: mysql.escape
}


// controller/user.js
const { exec, escape } = require('../db/mysql')

const login = (username, password) => {
	username = escape(username);
  	password = escape(password);
	const sql = `
		select username, realname from users where username=${username} and password=${password}
	`
	console.log(sql)
	return exec(sql).then((rows) => {
		return rows[0] || {}
	})
}
module.exports = {
	login,
}
// 前端页面
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>new</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>

</head>

<body>
    <h2>创建</h2>
    <input type="text" placeholder="请输入用户名" id="username" />
    <input type="text" placeholder="请输入密码" id="password" />
    <button onClick="test()">登录</button>
    <script>
        function test () {
            let data = {
                username: document.getElementById('username').value,
                password: document.getElementById('password').value
            }
            console.log(data);
            $.ajax({
                url: '/api/user/login',
                type: 'post',
                contentType: 'application/json',
                data: JSON.stringify(data),
                dataType: "json",
                success: function (res) {
                    if(res.errno === 0) {
                        location.href = '/'
                    }
                }
            })
        }
    </script>
</body>

</html>

转义后的SQL语句如下

select username, realname from users where username='zhangsan \' --' and password='123'

username 整体被单引号包起来了,里面的单引号会被转义,因此不会影响到查询。

需要拼接sql的语句 需要escape包装一遍--单引号去掉 重要

# XSS攻击

  • 前端同学最熟悉的方式,但server端更应该掌握
  • 攻击方式:在页面展示内容中掺杂了js代码,以获取网页信息
  • 预防措施:转换生成js的特殊字符

demo

# XSS攻击演示

XSS

创建成功后跳转到管理中心,因为 HTML 里直接嵌入了 script 脚本,因此会执行 alert(document.cookie)。因此转移会生成 script 脚本的符号是个有效的办法。

# 2.2 预防 XSS 攻击

yarn add xss // 安装

用法和上面的 escape 转义十分类似,只要将需要转义的字符串包起来即可。

// controller/blog.js
const newBlog = (blogData = {}) => {
  // blogData 是一个博客对象,包含 title、content、author 属性
  blogData = {
    ...blogData,
    createTime: Date.now(),
    id: 3, // 表示新建博客,插入到数据表里面的 id
  };
  const { title, content, author, createTime } = blogData;
  const sql = `
    insert into blogs (title, content, createTime, author) 
    values 
    ('${xss(title)}', '${xss(content)}', ${createTime}, '${author}');`;
  return exec(sql).then((insertData) => {
    // promise 返回插入的值对应的 id
    return {
      id: insertData.insertId,
    };
  });
};

效果 效果

转义后的 SQL 语句如下:

insert into blogs (title, content, createTime, author) 
values 

但是有些地方可能转义符号没有转义,例如文章详情页 这种内容需要由前端来处理而非后端。 这种内容需要由前端来处理而非后端。

# 密码加密

  • 万一数据库被用户攻破, 最不应该泄露的就是用户信息
  • 攻击方式:获取用户名和密码,再去尝试登录其他系统
  • 预防措施:将密码加密,即使拿到密码也不知道明文 加密流程
  1. 先引入node自带的crypto模块

    const crypto = require('crypto');
    
  2. 在utils文件里新建文件夹,写密码加密的相关逻辑,然后通过方法获取到密码加密后的内容,然后修改数据中原来的明文密码

    当然这一步是不合理的,但是没有做注册和修改密码功能没办法

    const crypto = require('crypto');
    
    // 密钥
    const SECRET_KEY = 'kfdsjl_742938#';
    
    // md5加密内容
    const md5 = content => {
    	// 输出变成16进制
    	return crypto.createHash('md5').update(content).digest('hex');
    }
    // 加密函数
    const genPassword = (password) => {
    	const str = `password=${password}&key=${SECRET_KEY}`;
    	return md5(str);
    };
    
    module.exports = {
    	genPassword,
    };
    
  3. 登录的时候密码加密并进行查询,因为现在数据库里存的是加密后的内容

    // controller/user.js
    const { exec, escape } = require("../db/mysql");
    const { genPassword } = require("../utils/cryp");
    
    const login = (username, password) => {
    username = escape(username);
    // 生成加密密码 注意getPassword 和escape的顺序 会牵扯sql是否有引号问题
    password = genPassword(password);
    password = escape(password);
    
    const sql = `
    	select username, realname from users where
    	username = ${username} and password = ${password};
    `;
    return exec(sql).then((rows) => {
    	return rows[0] || {};
    });
    };
    
    module.exports = {
    	login,
    };
    

# 总结

  • 开发了哪些功能模块,完整的流程
  • 用到了哪些知识点
  • server和前端的区别

# 功能模块

  • 处理http接口
  • 连接数据库
  • 实现登录
  • 日志
  • 安全
  • 上线(最后在一起讲)

流程图

# 核心知识点

  • http,nodejs处理http、处理路由,mysql
  • cookie, session,redis,nginx反向代理
  • sql注入,xss攻击,加密
  • 日志,stream,contrab, readline
  • 线上环境的知识点,之后统一讲

# server端和前端区别

  • 服务稳定性(pm2)
  • 内存CPU (优化扩展)
  • 日志记录
  • 安全(包括登录校验)
  • 集群和服务拆分(设计已支持)

# 下一步

  • 不适用框架开发,从0开始,关注底层API
  • 很琐碎、很复杂,没有标准可依,很容易代码写乱
  • 适合学习,但是不适用应用接下来开始express和koa2