Node.js+Express+Koa2开发接口学习笔记(六)

预防sql注入

sql注入

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

例如登录,访问http://localhost:8080/login.html

正常情况下是输入正确的用户名和密码来进行登录,这个登录是根据传入的username和password参数拼接sql语句来实现的。

// src/controller/user.js
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] || {};
  });
};
-- 拼接出来的语句
select username,realname from users where username='zhangsan' and password='123456'; 

因为username和password是传进去的,如果传进去的是(zhangsan'-- ),拼接语句就变成了

-- 拼接出来的语句
select username,realname from users where username='zhangsan'-- ' and password='123456'; 

在sql中 -- +空格是用来写注释语句的,可以看到上面的拼接语句密码处已经是无效的了。在登录页输入(zhangsan'-- )竟然可以登录成功,并且可以新建博客等操作。

如何预防?

在mysql.js文件中导出mysqlescape函数

const mysql = require("mysql");
const { MYSQL_CONF } = require("../conf/db");
...
module.exports = {
  exec,
  escape: mysql.escape,
};

然后在 src/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", sql);
  return exec(sql).then((rows) => {
    return rows[0] || {};
  });
};
module.exports = {
  login,
};

在登录页输入(zhangsan'-- )已经提示登录错误了,说明预防sql注入成功。通过escape函数后得到的拼接语句变成了

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

('--)escape去掉特殊化,只当作普通字符串,被整体包裹起来而非起到注释作用。

XSS攻击

  • 攻击方式:在页面展示内容中掺杂js代码,以获取网页信息
  • 预防措施:转换生成js的特殊字符
& -> &
< -> &lt;
> -> &gt;
" -> &quot;
' -> &#x27;
/ -> &#x2F;

例如在新建博客页面中,在标题处输入以下文本

<script>alert(document.cookie)</script>

它会在创建成功后返回管理页面执行脚本,弹出获取到的信息。

安装工具:npm i xss --save,使用这个工具可以转换生成js的特殊字符

在controller->blog.js

const xss = require("xss");

const newBlog = (blogData = {}) => {
  	const title = xss(blogData.title);
 	const content = xss(blogData.content);
	...
}

在新建博客页面中,在标题或内容处输入刚才的文本,可能会由于sql存储字符串长度限制报错,但是在控制台可以看到语句经过xss转义变成了不具有攻击能力的语句:

&lt;script&gt;alert(document.cookie)&lt;/script&gt;

用户加密

  • 万一数据库被用户攻破,最不应该泄漏的就是用户信息
  • 攻击方式:获取用户名和密码,再去尝试登录其他系统
  • 预防措施:将密码加密,即使拿到密码也不知道明文

在utils->crypt.js,通过node提供的crypto库编写加密函数

const crypto = require("crypto");

// 密钥
const SECRET_KEY = "WJiol_8776#";

// md5加密
function md5(content) {
  let md5 = crypto.createHash("md5");
  return md5.update(content).digest("hex");
}
// 加密函数
function genPassword(password) {
  const str = `password=${password}&key=${SECRET_KEY}`;
  return md5(str);
}
module.exports = {
  genPassword,
};

md5加密生出来的字符串是32位长度,需要去修改数据库中password的长度。

通过genPassword('123')可以看到加密后的密码为524ab85686df0e52ada43b11b53cce35,为了方便登录验证,将数据库中密码为123的其中一位用户密码改为加密后的密码。

在controller->login.js中将接收到的password参数进行加密

const { exec, escape } = require("../db/mysql");
const { genPassword } = require("../utils/crypt");
const login = ({ username, password }) => {
  username = escape(username);

  // 生成加密密码
  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,
};

分别验证被加密以及未加密的用户登录。

posted @ 2024-02-09 14:50  小风车吱呀转  阅读(38)  评论(0编辑  收藏  举报