网络安全篇(数据表单的创建 SQL命令拾遗 数据的SQL注入的防护)

SQL注入五孔不入,尽管是老技术了,但是依然是重点防护的手段,更多的需要我们数据库开发者细心!!
数据表的演练

1 创建数据表

create database jing_dong charset=utf8; -- 使用 jing_dong数据库 use jing_dong; create table goods(     id int unsigned primary key auto_increment not null,     name varchar(150) not null,     cate_name varchar(40) not null,     brand_name varchar(40) not null,     price decimal(10,3) not null default 0,     is_show bit not null default 1,     is_saleoff bit not null default 0 ); -- 展示数据库 show tables; -- 插入数据 INSERT INTO `goods` VALUES (1, '联想(Lenovo)天逸510S 个人商务台式机电脑整机(i3-10100 8G 512G', '笔记本', '华硕', 2324.000, b'0', b'0'); INSERT INTO `goods` VALUES (2, '戴尔dell成就3690 商用办公台式机电脑主机(11代i5-11400 16G', '笔记本', '联想', 4234.000, b'0', b'0'); INSERT INTO `goods` VALUES (3, '联想(Lenovo)天逸510S 个人商务台式机电脑整机(i5-10400 16G 1T+256G SSD', '游戏本', '索尼', 2342.000, b'0', b'0'); INSERT INTO `goods` VALUES (4, '武极天机 i5 10400F/GTX1050Ti/游戏台式办公电脑主', '超极本', '苹果', 4655.000, b'0', b'0'); INSERT INTO `goods` VALUES (5, '荣耀笔记本 MagicBook X 14 2021 14英寸全面屏轻薄笔记本电脑 (i3 10110U 8GB', '笔记本', '苹果', 1233.000, b'0', b'0'); INSERT INTO `goods` VALUES (6, '戴尔Dell成就3000 办公商用家用台式机电脑英特尔十代处理器学习财务', '平板电脑', '戴尔', 3434.000, b'0', b'0'); INSERT INTO `goods` VALUES (7, '戴尔笔记本电脑dell灵越15-3501 15.6英寸高性能轻薄商务笔记本电脑(11代英特', '台式机', '华硕', 2344.000, b'0', b'0'); INSERT INTO `goods` VALUES (8, '联想(Lenovo)扬天M4000q英特尔酷睿i3 商用台式机电脑整机(i3-10100', '平板电脑', '联想', 4543.000, b'0', b'0'); INSERT INTO `goods` VALUES (9, '联想ThinkPad E15 2021款 酷睿版 英特尔酷睿i5/i7 轻薄笔记本电脑 人', '台式机', '苹果', 3455.000, b'0', b'0'); INSERT INTO `goods` VALUES (10, '硕扬 i5 10400F/八核/GTX1050TI 4G/32G内存游戏台式吃鸡', '台式机', '索尼', 2455.000, b'0', b'0'); INSERT INTO `goods` VALUES (11, '惠普(HP)小欧高清一体机电脑21.5英寸(J4025 4G 256SSD UMA 无线', '笔记本', '华硕', 7899.000, b'0', b'0');  -- 查询为超极本的商品 select *from goods where cate_name="超极本";  -- 但是我只想看商品品名称和价格 select name,price from goods where cate_name="超极本";  -- 但是我不认识英文怎么办,设置一个中文的对话框 select name as 名字,price as 价格 from goods where cate_name="超极本";  -- 显示商品种类 select name,cate_name from goods;  -- 我想看所有的电脑的分类,全部类别用distinct进行实现 select distinct cate_name from goods;  -- 我想看所有的电脑的分类,用分组来进行实现 select cate_name from goods group by cate_name;  -- 我想看电脑的分类并且要加上名字,用分组来进行实现 select cate_name,group_concat(name) from goods group by cate_name;  -- 求所有电脑产品的平均价格 select avg(price) from goods;  -- 求所有电脑产品的平均价格,并保留两位小数 select round(avg (price),2) from goods;  -- 显示每种类别商品的平均价格 select cate_name ,avg(price) from goods group by cate_name;  -- 查询每种类型的商品中最贵的,最便宜的,平均价,数量 select cate_name, max(price) ,min(price) ,avg(price),count(*) from goods group by cate_name;  -- 查询所有价格大于平均价格的商品,并且按价格降序排序 select id,name,price from goods where price> (select round (avg(price),2) as avg_price from goods) order by price desc;  -- 查询每种商品中最贵的电脑信息 select * from goods  inner join(     select 	cate_name, 	max(price) as max_price,     min(price) as min_price, 	avg(price) as avg_price, 	count(*) from goods group by cate_name      )     as goods_new_info on goods.cate_name=goods_new_info.cate_name and goods.price=goods_new_info.max_price; -- 思路,把第一次上面的查询语句看作一个表,用下面的检索条件进行适配,名字相等 价格相等,那么就直接输出出来,相当于两张表合并查询 -- 或者也可以有这种写法(表级的查询) select *from 	(select cate_name,max(price) as max_price from goods group by cate_name	) 	as g_new left join goods as g on g_new.cate_name =g.cate_name and g_new.max_price =g.price; 

最后一个的输出结果为:

+----+----------------------------------------------------------------------+-----------+------------+----------+---------+------------+-----------+-----------+-----------+--------------+----------+ | id | name                                                                 | cate_name | brand_name | price    | is_show | is_saleoff | cate_name | max_price | min_price | avg_price    | count(*) | +----+----------------------------------------------------------------------+-----------+------------+----------+---------+------------+-----------+-----------+-----------+--------------+----------+ |  3 | 联想(Lenovo)天逸510S 个人商务台式机电脑整机(i5-10400 16G 1T+256G SSD | 游戏本    | 索尼       | 2342.000 |         |            | 游戏本    |  2342.000 |  2342.000 | 2342.0000000 |        1 | |  4 | 武极天机 i5 10400F/GTX1050Ti/游戏台式办公电脑主                      | 超极本    | 苹果       | 4655.000 |         |            | 超极本    |  4655.000 |  4655.000 | 4655.0000000 |        1 | |  8 | 联想(Lenovo)扬天M4000q英特尔酷睿i3 商用台式机电脑整机(i3-10100       | 平板电脑  | 联想       | 4543.000 |         |            | 平板电脑  |  4543.000 |  3434.000 | 3988.5000000 |        2 | |  9 | 联想ThinkPad E15 2021款 酷睿版 英特尔酷睿i5/i7 轻薄笔记本电脑 人     | 台式机    | 苹果       | 3455.000 |         |            | 台式机    |  3455.000 |  2344.000 | 2751.3333333 |        3 | | 11 | 惠普(HP)小欧高清一体机电脑21.5英寸(J4025 4G 256SSD UMA 无线         | 笔记本    | 华硕       | 7899.000 |         |            | 笔记本    |  7899.000 |  1233.000 | 3922.5000000 |        4 | +----+----------------------------------------------------------------------+-----------+------------+----------+---------+------------+-----------+-----------+-----------+--------------+----------+ 

查询各个电脑种类的最大价格

select cate_name ,max(price) from goods group by cate_name; 

数据库高级演练 拆分为多个表

-- 创建商品分类 create table if not exists goods_cates(     id int unsigned primary key auto_increment,     name varchar(48) not null );  -- 查看之前的goods表单中的所有分类 select cate_name from goods group by cate_name;  --将分组结果写入goods_cates 数据表中  insert into goods_cate(name) select cate_name from goods group by cate_name;  -- 同步表数据 通过goods_cates 数据表来更新goods表  更新goods 中的cate_name用第二张表来进行更新  update goods as g inner join goods_cates as c on g.cate_name=c.name set g.cate_name =c.id;  -- 我们发现主键的goods与goods_cates类型并不是一样的,这里我们就需要改变一下类型 alter table goods change cate_name cate_id int unsigned not null;  -- 插入关联,将cate_id 追加约束外键 alter table goods add foreign key(cate_id)  references goods_cates(id);  -- 如何取消外键 alter table goods drop foreign key 外键名称; 

pymysql 连接的过程

开始 ==> 创建connection > 获取cursor (游标对象)> 执行查询 执行命令 获取数据 处理数据 ==> 关闭cursor ==> 关闭 connection ==> 结束

import pymysql #创建connect链接数据库 connect = pymysql.connect(host="localhost",port=3306,user='root',password='200228',database='jing_dong',charset='utf8') #获得游标对象 cursor cs1= connect.cursor() #执行SQL语句中的select 并返回受影响的行数,查找一条数据 count =cs1.execute('select id,name from goods where id>=4') print(count) for i in range(count):     #获取查询结果     result = cs1.fetchone()     #打印查询的结果     print(result) #关闭cursor对象 cs1.close() #关闭连接 connect.close() 

当我们的游标可以取到商品后,我们也可以取一下数据 = cs1.fetchone() for temp in line_content: print(temp) #如果要是用fetchmany来进行查看的时候,就是元组套元组来进行显示 #这个的意思就是取五条数据 lines = cs1.fetchmany(5) print(lines)

工程案例:

开发一款可以方便于京东商品查询的脚本,需使用面向对象开发;实现可查看数据库内所有商品 所有商品的分类 所有商品的品牌分类

from pymysql import connect class JD(object):     def __init__(self):         # 初始化创建connection连接         self.conn = connect(host="localhost", port=3306, user='root', password='200228', database='jing_dong',                             charset='utf8')         # 创建游标对象         self.cs1 = self.conn.cursor()      def __del__(self):         self.cs1.close()         self.conn.close()      def execute_sql(self, sql):         """         :param sql: 传入sql数据,无需每次连接后断开         """         self.cs1.execute(sql)         for temp in self.cs1.fetchall():             print(temp)      def show_all_intem(self):         """显示所有的商品"""         # 创建connection连接         sql = "select *from goods;"         self.execute_sql(sql)      def show_cates(self):         sql = "SELECT name FROM goods_cates"         self.execute_sql(sql)      def show_brand(self):         sql = "SELECT name FROM goods_brands;"         self.execute_sql(sql)      @staticmethod     def print_menu():         """         使用实例方法,全局调用         :return: 返回的值传入下一个即将调用的函数中         """         print("----京东-----")         print("1:所有的商品")         print("2:所有的商品分类")         print("3:所有的商品品牌分类")         num = input("请输入功能对应的序号:")         # 这里切记,一定要return返回值         return num      def run(self):         while True:             num = self.print_menu()             if num == "1":                 # 查询所有商品                 self.show_all_intem()             elif num == "2":                 # 查询分类                 self.show_cates()             elif num == "3":                 # 查询品牌                 self.show_brand()             else:                 print("输入有误,请重新输入....")   def main():     # 1. 创建一个京东商城的对象     jd = JD()     # 2. 调用这个对象的run 方法,让其运行     jd.run()   if __name__ == '__main__':     main() 

pymysql增删改查:实质上都是一样的,唯一不一样的就是最后一个需要提交 通过使用 commit 方法

插入数据:

from pymysql import connect  conn = connect(host="localhost", port=3306, user='root', password='200228', database='jing_dong',                charset='utf8') # 创建游标对象 cs1 = conn.cursor() sql = "insert into goods_cates(name) VALUES ('硬盘_new')" cs1.execute(sql) #无论是更新还是删除,必须要commit提交一下数据库 conn.commit() cs1.close() conn.close() 

实现数据的插入

from pymysql import connect   class JD(object):     def __init__(self):         # 初始化创建connection连接         self.conn = connect(host="localhost", port=3306, user='root', password='200228', database='jing_dong',                             charset='utf8')         # 创建游标对象         self.cs1 = self.conn.cursor()      def __del__(self):         self.cs1.close()         self.conn.close()      def execute_sql(self, sql):         """         :param sql: 传入sql数据,无需每次连接后断开         """         self.cs1.execute(sql)         for temp in self.cs1.fetchall():             print(temp)      def show_all_intem(self):         """显示所有的商品"""         # 创建connection连接         sql = "select *from goods;"         self.execute_sql(sql)      def show_cates(self):         sql = "SELECT name FROM goods_cates"         self.execute_sql(sql)      def show_brand(self):         sql = "SELECT name FROM goods_brand;"         self.execute_sql(sql)      def add_menu(self):         item_name =input("请输入新商品分类名称")         sql ="""insert into goods_brand(name) VALUES("%s") """ %item_name         self.execute_sql(sql)         #更新创建数据表单,实现数据的更新         self.conn.commit()      @staticmethod     def print_menu():         """         使用实例方法,全局调用         :return: 返回的值传入下一个即将调用的函数中         """         print("----京东-----")         print("1:所有的商品")         print("2:所有的商品分类")         print("3:所有的商品品牌分类")         print("4:插入商品品牌")         num = input("请输入功能对应的序号:")         # 这里切记,一定要return返回值         return num      def run(self):         while True:             num = self.print_menu()             if num == "1":                 # 查询所有商品                 self.show_all_intem()             elif num == "2":                 # 查询分类                 self.show_cates()             elif num == "3":                 # 查询品牌                 self.show_brand()             elif num == "4":                 # 插入数据                 self.add_menu()             else:                 print("输入有误,请重新输入....")   def main():     # 1. 创建一个京东商城的对象     jd = JD()     # 2. 调用这个对象的run 方法,让其运行     jd.run()   if __name__ == '__main__':     main() 

SQL注入:

什么是SQL注入,在数据库没有超级管理员的权限时,我们通过一些奇怪的命令也可以获取到数据表单

-- 输入5之后 可以通过这条命令获取到全部的信息 -- select * from goods where name =''or 1=1 or''; 'or 1=1 or' 

输出

----->select * from goods where name =''or 1=1 or'';ql<------ (1, '联想(Lenovo)天逸510S 个人商务台式机电脑整机(i3-10100 8G 512G', 4, '华硕', Decimal('2324.000'), b'x00', b'x00') (2, '戴尔dell成就3690 商用办公台式机电脑主机(11代i5-11400 16G', 4, '联想', Decimal('4234.000'), b'x00', b'x00') (3, '联想(Lenovo)天逸510S 个人商务台式机电脑整机(i5-10400 16G 1T+256G SSD', 3, '索尼', Decimal('2342.000'), b'x00', b'x00') (4, '武极天机 i5 10400F/GTX1050Ti/游戏台式办公电脑主', 5, '苹果', Decimal('4655.000'), b'x00', b'x00') (5, '荣耀笔记本 MagicBook X 14 2021 14英寸全面屏轻薄笔记本电脑 (i3 10110U 8GB', 4, '苹果', Decimal('1233.000'), b'x00', b'x00') (6, '戴尔Dell成就3000 办公商用家用台式机电脑英特尔十代处理器学习财务', 2, '戴尔', Decimal('3434.000'), b'x00', b'x00') (7, '戴尔笔记本电脑dell灵越15-3501 15.6英寸高性能轻薄商务笔记本电脑(11代英特', 1, '华硕', Decimal('2344.000'), b'x00', b'x00') (8, '联想(Lenovo)扬天M4000q英特尔酷睿i3 商用台式机电脑整机(i3-10100', 2, '联想', Decimal('4543.000'), b'x00', b'x00') (9, '联想ThinkPad E15 2021款 酷睿版 英特尔酷睿i5/i7 轻薄笔记本电脑 人', 1, '苹果', Decimal('3455.000'), b'x00', b'x00') (10, '硕扬 i5 10400F/八核/GTX1050TI 4G/32G内存游戏台式吃鸡', 1, '索尼', Decimal('2455.000'), b'x00', b'x00') (11, '惠普(HP)小欧高清一体机电脑21.5英寸(J4025 4G 256SSD UMA 无线', 4, '华硕', Decimal('7899.000'), b'x00', b'x00')  

我们如何做到用更安全的方式防护SQL注入呢

#安全的方式 #构建参数列表 params =[find_name] #执行select语句,并返回收影响的行数,查询所有数据 count =cs1.execute('select * from goods where name=%s',params) #注意 如果有多个参数,需要进行参数化,那么params=[数值1,数值2....] ,此时sql语句中有多个%s即可 #打印受影响的行数 print(count) 

防止SQL注入代码:

from pymysql import connect   class JD(object):     def __init__(self):         # 初始化创建connection连接         self.conn = connect(host="localhost", port=3306, user='root', password='200228', database='jing_dong',                             charset='utf8')         # 创建游标对象         self.cs1 = self.conn.cursor()      def __del__(self):         self.cs1.close()         self.conn.close()      def execute_sql(self, sql):         """         :param sql: 传入sql数据,无需每次连接后断开         """         self.cs1.execute(sql)         for temp in self.cs1.fetchall():             print(temp)      def show_all_intem(self):         """显示所有的商品"""         # 创建connection连接         sql = "select *from goods;"         self.execute_sql(sql)      def show_cates(self):         sql = "SELECT name FROM goods_cates"         self.execute_sql(sql)      def show_brand(self):         sql = "SELECT name FROM goods_brand;"         self.execute_sql(sql)      def add_menu(self):         item_name =input("请输入新商品分类名称")         sql ="""insert into goods_brand(name) VALUES("%s") """ %item_name         self.execute_sql(sql)         #更新创建数据表单,实现数据的更新         self.conn.commit()     def get_info_by_name(self):         find_name = input("请输入要查询的商品名字:")         sql = "select * from goods where name= %s"         self.cs1.execute(sql,[find_name])         print(self.cs1.fetchall())     @staticmethod     def print_menu():         """         使用实例方法,全局调用         :return: 返回的值传入下一个即将调用的函数中         """         print("----京东-----")         print("1:所有的商品")         print("2:所有的商品分类")         print("3:所有的商品品牌分类")         print("4:插入商品品牌")         print("5:根据名字查询一个商品")         num = input("请输入功能对应的序号:")         # 这里切记,一定要return返回值         return num      def run(self):         while True:             num = self.print_menu()             if num == "1":                 # 查询所有商品                 self.show_all_intem()             elif num == "2":                 # 查询分类                 self.show_cates()             elif num == "3":                 # 查询品牌                 self.show_brand()             elif num == "4":                 # 插入数据                 self.add_menu()             elif num == "5":                 #根据名字查询商品                 self.get_info_by_name()             else:                 print("输入有误,请重新输入....")   def main():     # 1. 创建一个京东商城的对象     jd = JD()     # 2. 调用这个对象的run 方法,让其运行     jd.run()   if __name__ == '__main__':     main() 

写在最后:
SQL注入的防治之一,只有唯一的方法,千万不要自己去拼接,而是我们需要用pymysql自带的函数来进行进一步拼接,这边的话我们要更多的使用 execute 来进行安全防护

我们设想一下,查询可以通过 'or 1=1'来进行查看的,我们仅仅是通过查看,走更危险的一步 如果要是 删除呢(手动滑稽) drop table message 哈哈哈这就有意思了,未来可能面临三餐管饭了,乌云网被封了,如果要是能看到更多安全渗透可以学到更多的安全防护知识。