网安入门之MySQL后端基础
数据库 (Database)
数据库是指长期存储在计算机中的,有组织、可共享的数据集合。它通过表、列、行等结构来组织数据,目的是使数据可以高效存储、检索和管理。数据库通常包括多个表,每个表存储与特定主题或对象相关的数据
数据库管理系统 (DBMS)
数据库管理系统(DBMS)是用于创建、管理、操作、维护数据库的软件。它提供了用户与数据库交互的接口,支持数据的增、删、改、查等操作,并确保数据的完整性、安全性和一致性。常见的DBMS有:
- MySQL:开源的关系型数据库管理系统,广泛用于Web开发中
- MSSQL:微软推出的关系型数据库管理系统,通常用于企业级应用
- ORACLE:强大的关系型数据库系统,广泛应用于大型企业
- Redis:一个高性能的键值数据库,通常用于缓存和实时数据处理
- Access:微软推出的小型数据库管理系统,适合小型应用
Excel 与 DBMS
虽然Excel本身不是一个数据库管理系统,但它可以用作简易数据库来存储和组织数据。Excel通过表格的形式组织数据,可以进行基本的增、删、改、查操作,适合小规模数据管理。不过,Excel的功能有限,不具备DBMS的高效性、并发控制、事务处理等特性。因此,Excel不适合处理大量数据或复杂的数据库操作
数据库的操作
数据库通常支持以下基本操作: - 增(INSERT):向数据库表中插入新数据
o 通过 INSERT 语句可以添加新记录到表中 - 删(DELETE):从数据库表中删除数据
o 使用 DELETE 语句来删除一行或多行数据 - 改(UPDATE):更新数据库表中的已有数据
o 使用 UPDATE 语句来修改表中的记录 - 查(SELECT):查询数据库中的数据
o 使用 SELECT 语句来检索数据,可以进行复杂的过滤、排序、连接等操作 - 新建数据库(CREATE DATABASE):创建一个新的数据库
o 使用 CREATE DATABASE 语句可以新建一个数据库 - 新建数据表(CREATE TABLE):在数据库中创建一个新的表
o CREATE TABLE 语句定义表的结构,包括列名、数据类型、主键等 - 新建数据列(ALTER TABLE):向已有的数据表中添加、删除或修改列
o 使用 ALTER TABLE 可以添加、删除或修改表的字段,允许对表进行扩展或调整
数据库表的组成
数据库表是由行(记录)和列(字段)组成的二维结构。每个表有: - 列(Field/Column):每列存储一种类型的数据,如 ID、Name、Age 等
- 行(Record/Row):每行表示一条具体的数据记录,每行数据包含所有列的值
数据库操作示例 - 插入数据(INSERT):
//创建数据表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
age INT
);
INSERT INTO users (name, age) VALUES ('Alice', 30);
可见已经插入:
- 删除数据(DELETE):
DELETE FROM users WHERE name = 'Alice'
因为已经删除了,再次查询就没有了
- 更新数据(UPDATE):
UPDATE users SET age = 31 WHERE name = 'Alice'
- 查询数据(SELECT):
SELECT * FROM users WHERE age > 25
数据库事务
DBMS通常支持事务的管理。一个事务是一组操作的集合,它要么完全执行,要么完全不执行。事务保证了数据库的ACID特性(原子性、一致性、隔离性、持久性)
- 原子性(Atomicity):事务中的所有操作要么全部执行,要么全部不执行
- 一致性(Consistency):事务的执行使得数据库从一个一致性状态转变为另一个一致性状态
- 隔离性(Isolation):事务的执行不受其他事务的干扰
持久性(Durability):事务一旦提交,它的结果就永久保存在数据库中
总结
SELECT * FROMuserinfo
WHERE 1
查询 想要查询到的结果(数据列的名称) 从哪儿来(从哪个数据表操作) 资源归属(数据表的名称) where 限制条件(key=value)
INSERT INTO 表名(字段名称1, 字段名称2) VALUES (字段值1,字段值2)可以不用对记录中每一个字段都赋值,但是字段名称和字段值数量要一致
UPDATE 表名 SET 字段 = '值' WHERE 条件
DELETE FROM 表名 WHERE 条件delete没有输入字段,是因为我们是将整条记录一起删除的
AND
AND 是 SQL 中的逻辑运算符,用于在 WHERE 子句中组合多个条件。只有当所有条件都为 TRUE 时,整个表达式才返回 TRUE。
用法:当多个条件必须同时满足时,可以使用 AND 连接。例如,查询年龄大于 20 且性别为男性的所有用户:
SELECT * FROM users WHERE age > 20 AND gender = 'Male';OR
OR 也是 SQL 中的逻辑运算符,用于在 WHERE 子句中组合多个条件。只要有一个条件为 TRUE,整个表达式就返回 TRUE。
用法:当多个条件中至少有一个条件满足时,可以使用 OR 连接。例如,查询年龄大于 20 或性别为男性的所有用户:
SELECT * FROM users WHERE age > 20 OR gender = 'Male';ORDER BY(后期用来SQL注入攻击探测列数)
ORDER BY 用于对查询结果进行排序。你可以指定按一个或多个列进行排序,并选择升序(ASC)或降序(DESC)排列。
用法:默认是升序排列(ASC),如果需要降序排列,可以明确指定 DESC。例如,按 age 升序排列用户:
SELECT * FROM users ORDER BY age ASC;
如果要按多个字段排序,可以在 ORDER BY 后列出多个字段,按顺序排列。例如,先按 age 升序排列,再按 name 字母顺序排列:
SELECT * FROM users ORDER BY age ASC, name ASC;
- UNION(后期用来联合查询攻击)
UNION 用于将两个或多个 SELECT 查询的结果合并成一个结果集。注意,UNION 会去除重复的记录。如果需要包含所有记录(包括重复记录),可以使用 UNION ALL。
用法:UNION:将两个查询的结果合并,并去掉重复的行。UNION ALL:将两个查询的结果合并,包括重复的行。例如,查询 users 表和 employees 表的所有 name 字段,合并结果:
SELECT name FROM users UNION SELECT name FROM employees;
如果不去掉重复记录,可以使用 UNION ALL:
SELECT name FROM users UNION ALL SELECT name FROM employees;
总结
- AND:用来连接多个条件,所有条件都满足时,返回 TRUE。
- OR:用来连接多个条件,只要有一个条件满足时,返回 TRUE。
- ORDER BY:用来排序查询结果,可以指定排序的列及排序顺序。
- UNION:用来合并多个 SELECT 查询的结果,默认去掉重复行,可以使用 UNION ALL 保留重复行
- LIKE
LIKE 是 SQL 中用于模式匹配的运算符,常用于 WHERE 子句中,用来匹配列中的某些字符模式。通常与通配符(如 % 和 )一起使用。
用法:
%:表示零个或多个字符。 :表示一个字符。
示例:匹配以特定字符开头的值,查询名字以 "A" 开头的所有用户:
SELECT * FROM users WHERE name LIKE 'A%';
这条查询会返回所有名字以 "A" 开头的用户,例如 "Alice"、"Alex"。
- 匹配包含特定字符的值查询名字中包含 "li" 的所有用户:
SELECT * FROM users WHERE name LIKE '%li%';
这条查询会返回名字中包含 "li" 的所有用户,例如 "Alice"、"Bill"。
- 匹配特定字符数的值查询名字的第二个字符是 "l" 的用户:
SELECT * FROM users WHERE name LIKE '_l%';
这条查询会返回名字第二个字符是 "l" 的用户,例如 "Alice"。
- GROUP BY
GROUP BY 用于将查询结果按某些列进行分组。它通常与聚合函数(如 COUNT、SUM、AVG、MAX、MIN 等)一起使用,用来对每个分组执行聚合操作。
用法: GROUP BY 将结果集按指定的列进行分组,然后可以对每个分组执行聚合操作。
示例:按年龄分组并统计每个年龄段的用户数查询每个年龄段的用户数量:
SELECT age, COUNT(*) AS num_users FROM users GROUP BY age;
这条查询会按 age 列对用户进行分组,并返回每个年龄的用户数量。
- 按部门分组并求每个部门的最大工资查询每个部门的最高工资:
SELECT department, MAX(salary) AS max_salary FROM employees GROUP BY department;
这条查询会返回每个部门中薪资最高的员工工资。
- 按多个列分组按年龄和性别分组,并计算每个分组的用户数量:
SELECT age, gender, COUNT(*) AS num_users FROM users GROUP BY age, gender;
这条查询会返回每个年龄和性别组合下的用户数量。
- IN
IN 是 SQL 中的一种运算符,用于检查某个值是否在指定的一组值中。通常用于 WHERE 子句中,可以简化多个 OR 条件的表达式。
用法: IN 用于匹配指定列的值是否在给定的集合中。
示例:查询年龄为 20、25 或 30 的用户使用 IN 来匹配年龄为 20、25 或 30 的用户:
SELECT * FROM users WHERE age IN (20, 25, 30);
这条查询会返回年龄是 20、25 或 30 的用户。
- 查询名字为 "Alice"、"Bob" 或 "Charlie" 的用户使用 IN 来匹配名字为 "Alice"、"Bob" 或 "Charlie" 的用户:
SELECT * FROM users WHERE name IN ('Alice', 'Bob', 'Charlie');
这条查询会返回名字为 "Alice"、"Bob" 或 "Charlie" 的用户。
- 与子查询一起使用使用子查询返回的结果作为 IN 的参数。例如,查询所有在部门 A 或部门 B 的员工:
SELECT * FROM employees WHERE department IN (SELECT department FROM departments WHERE name IN ('A', 'B'));
这条查询会返回所有在部门 A 或部门 B 的员工。
利用 LIKE 进行 SQL 注入:
LIKE 通常在数据库查询中作为过滤条件使用。如果输入没有正确验证或过滤,攻击者可能会通过构造恶意输入,导致 SQL 注入。
例如,如果网站允许用户通过查询名字来查找用户,但没有正确处理输入,攻击者可能会尝试通过 LIKE 运算符注入 SQL 代码。
示例注入攻击: 假设查询语句为:
SELECT * FROM users WHERE name LIKE '%[用户输入]%';
攻击者可以输入如下内容:
' OR '1'='1
这会导致 SQL 查询变成:
SELECT * FROM users WHERE name LIKE '% OR '1'='1%';
这样,OR '1'='1' 总是为真,导致返回所有记录,从而绕过认证或过滤。
利用 GROUP BY 进行 SQL 注入:
攻击者可能利用 GROUP BY 子句注入恶意 SQL 代码。假设应用程序没有正确处理用户输入,攻击者可以插入恶意的 GROUP BY 子句,尝试影响查询结果或利用数据库的结构信息。
示例注入攻击: 假设查询语句是:
SELECT department, COUNT(*) FROM employees GROUP BY department;
攻击者输入恶意代码:
' GROUP BY CONCAT(username, password) --
这可能导致查询变成:
SELECT department, COUNT(*) FROM employees GROUP BY CONCAT(username, password) -- ;
CONCAT(username, password) 会拼接 username 和 password 列的值,并可能返回这些敏感信息。
利用 IN 进行 SQL 注入:
攻击者可能在 IN 子句中注入恶意值,导致 SQL 查询失效或获取不必要的数据。注入攻击通常利用 IN 语句的数组特性来篡改查询。
示例注入攻击: 假设查询语句是:
SELECT * FROM users WHERE name IN ('[用户输入]');
攻击者输入恶意内容:
' OR '1'='1' --
这会导致查询变成:
SELECT * FROM users WHERE name IN ('', ' OR '1'='1' --');
由于 1 = 1 始终为真,查询可能返回所有记录,绕过身份验证或其他限制。
在 PHP 中,可以使用 mysqli 扩展来与 MySQL 数据库进行交互。mysqli(MySQL Improved)提供了一个更为现代化和灵活的方式来操作 MySQL 数据库,支持面向过程和面向对象的编程方式。
- 连接 MySQL 数据库
在执行任何操作之前,首先需要建立与 MySQL 数据库的连接。使用 mysqli_connect 或 new mysqli 方法。
面向过程:
$conn = mysqli_connect("localhost", "username", "password", "database_name");
if (!$conn) {
die("连接失败: " . mysqli_connect_error());
}
面向对象:
$conn = new mysqli("localhost", "username", "password", "database_name");
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
- 增、删、改(INSERT、UPDATE、DELETE)
在执行增、删、改操作时,使用 mysqli_query 函数。mysqli_query 返回一个布尔值(TRUE 或 FALSE),表示查询是否成功执行。
插入数据(INSERT):
$sql = "INSERT INTO users (name, age) VALUES ('Alice', 30)";
if (mysqli_query($conn, $sql)) {
echo "新记录插入成功";
} else {
echo "错误: " . $sql . "
" . mysqli_error($conn);
}
更新数据(UPDATE):
$sql = "UPDATE users SET age = 31 WHERE name = 'Alice'";
if (mysqli_query($conn, $sql)) {
echo "记录更新成功";
} else {
echo "错误: " . $sql . "
" . mysqli_error($conn);
}
删除数据(DELETE):
$sql = "DELETE FROM users WHERE name = 'Alice'";
if (mysqli_query($conn, $sql)) {
echo "记录删除成功";
} else {
echo "错误: " . $sql . "
" . mysqli_error($conn);
}
面向对象的语法:
$sql = "INSERT INTO users (name, age) VALUES ('Bob', 25)";
if ($conn->query($sql) === TRUE) {
echo "新记录插入成功";
} else {
echo "错误: " . $sql . "
" . $conn->error;
}
- 查询操作(SELECT)
当执行查询操作(SELECT)时,mysqli_query 返回一个结果集。如果查询成功且有结果,它返回一个 mysqli_result 对象。你需要使用 mysqli_fetch_assoc 或 mysqli_fetch_row 来解析查询结果。
查询数据(SELECT):
$sql = "SELECT id, name, age FROM users";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
// 输出每行数据
while($row = mysqli_fetch_assoc($result)) {
echo "id: " . $row["id"] . " - Name: " . $row["name"] . " - Age: " . $row["age"] . "
";
}
} else {
echo "没有结果";
}
面向对象的语法:
$sql = "SELECT id, name, age FROM users";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo "id: " . $row["id"] . " - Name: " . $row["name"] . " - Age: " . $row["age"] . "
";
}
} else {
echo "没有结果";
}
- mysqli_fetch_assoc 和 mysqli_fetch_row 解析查询结果
在查询数据时,你可以使用两种常用的函数来解析结果集:
mysqli_fetch_assoc:返回一个关联数组,其中数组的键是列名(字段名)。这种方法通常用于需要字段名的情况。
mysqli_fetch_row:返回一个索引数组,其中数组的键是列的数字索引。适用于你只关心结果的顺序,而不关心字段名称的情况。
使用 mysqli_fetch_assoc:
$row = mysqli_fetch_assoc($result);
echo "id: " . $row["id"] . " - Name: " . $row["name"] . " - Age: " . $row["age"] . "
";
使用 mysqli_fetch_row:
$row = mysqli_fetch_row($result);
echo "id: " . $row[0] . " - Name: " . $row[1] . " - Age: " . $row[2] . "
";
mysqli_fetch_assoc 返回的结果是一个关联数组,可以通过字段名直接访问数据。而 mysqli_fetch_row 返回的是一个索引数组,字段值是通过列索引来访问的(从 0 开始)。
- 关闭连接
操作完成后,别忘了关闭数据库连接。
面向过程:
mysqli_close($conn);
面向对象:
$conn->close();
总结:
增、删、改:使用 mysqli_query 执行操作,返回一个布尔值来判断是否成功。
查询:使用 mysqli_query 执行 SELECT 查询,返回一个结果集,使用 mysqli_fetch_assoc 或 mysqli_fetchrow 解析结果。
面向对象和过程化:可以选择面向过程(mysqli*)或面向对象($conn->method)的方式来操作 MySQL。
注意: 在使用 mysqli_query 执行用户输入的 SQL 语句时,务必防止 SQL 注入攻击,推荐使用 预处理语句(Prepared Statements) 来确保安全。