我的目标是安全/开发/AI方向,这里整理一些老被问的问题。

计算机网络

传输层

协议
应用层 HTTP、FTP、SMTP、NFS、Telnet、DNS
传输层 TCP、UDP
网络层 IP、ICMP、ARP、RARP
数据链路层 PPP、Ethernet

TCP、UDP相关

TCP的可靠传输

  • 校验和:发送方在构造TCP报文段时,会计算包括TCP头部、数据部分以及一个伪头部(包含源IP地址、目的IP地址、协议号和TCP长度)在内的校验和,并将结果填入TCP头部的校验和字段。接收方收到报文后,使用相同的算法重新计算校验和,若结果不为全零,则说明数据在传输过程中可能发生了损坏,接收方将丢弃该报文段并可能触发重传。
  • 序列号/确认应答(seq、ACK):TCP通过序列号(Sequence Number,seq)和确认应答号(Acknowledgment Number,ack)实现可靠、有序的数据传输。序列号标识发送方当前报文段中第一个字节在整个数据流中的位置,确保接收方能按正确顺序重组数据;确认应答号则表示接收方期望下一个接收的字节序号,即“已成功接收ack-1之前的所有数据”。例如,若接收方返回ack=1001,说明它已正确收到序号1000及之前的所有数据,并期待发送方接下来发送序号为1001的字节。
  • 超时重传:发送方每发送一个TCP报文段后,会启动一个定时器(重传定时器),等待接收方返回对应的确认应答(ACK)。如果在定时器超时前未收到ACK,发送方就认为该报文段可能在网络中丢失或延迟过久,于是会重新发送该报文段。超时时间(RTO, Retransmission Timeout)通常基于网络往返时间(RTT)动态调整,以适应网络状况的变化。
  • 流量控制:TCP的流量控制通过滑动窗口机制实现,目的是防止发送方发送数据过快,导致接收方缓冲区溢出而丢弃数据。接收方在每个ACK报文中携带一个“接收窗口”(rwnd)字段,告知发送方其当前还能接收多少字节的数据。发送方据此动态调整自己的发送窗口大小,确保未被确认的数据量不超过接收方的处理能力。滑动窗口允许发送方在未收到确认的情况下连续发送多个报文段,提高了传输效率;同时,随着ACK的返回和接收窗口的变化,窗口不断“滑动”,实现数据的有序、高效且不溢出的传输。
  • 快速重传:快速重传(Fast Retransmit)是TCP为提高重传效率而引入的一种机制,用于在不等待超时的情况下快速恢复丢失的报文段。当发送方连续收到三个或更多个针对同一数据序号的重复确认(Duplicate ACKs)时,就认为该确认所期望的下一个报文段很可能已经丢失。此时,发送方会立即重传该缺失的报文段,而不必等待重传定时器超时。快速重传显著减少了因丢包导致的延迟,提升了网络吞吐量和响应速度,通常与快速恢复(Fast Recovery)算法配合使用,以避免不必要的拥塞控制降速。
  • 拥塞控制:网络可能刚开始很拥塞,如果在网络传输过程中开始就发送大量数据的话,会发生丢包和超时重传,所以需要慢启动算法、拥塞避免算法、快速重传和快速恢复。

TCP三次握手

  1. 第一次握手:客户端请求建立连接,将首部的SYN标识位置为1,初始化序列号seq=x,发送给服务器,并进入SYN_SENT状态,等待服务器确认。
  2. 服务器接收到SYN后,将首部的SYN和ACK标识位置为1,回复确认ack的值为客户端发送的序列号x+1,同时自己也要初始化一个seq=y,ACK和SYN一起发送客户端,进入SYN_RECV状态。
  3. 客户端收到服务器的SYN和ACK包后,向服务器发送确认包ack,值为服务器的序列号y+1,并且自己的seq为x+1,此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态。

为什么要三次握手?没有会怎么样?

只有三次握手才能证明服务端和客户端的收发能力都是正常的。

第一次握手:客户端发数据服务端接收,服务端可以知道客户端发消息的能力是正常的,自己接收消息的能力是正常的。

第二次握手:服务端发消息客户端收,客户端可以知道自己发送接收消息的能力和服务端发送接收消息的能力是正常的。

第三次握手:客户端再发送消息服务端接收,服务端可以知道自己发送消息的能力是正常的,客户端接收消息的能力是正常的。

由此经过三次握手之后双方就可以都知道自己的发送和接收消息的能力是正常的。


如果没有,会导致:

  • 无法确认双方的收发能力:仅一次或两次握手无法确保双方都能正常发送和接收数据。例如,若只有两次握手,服务器无法确认客户端是否收到了自己的 SYN-ACK,可能导致服务器以为连接已建立而持续发送数据,但客户端并未准备好
  • 历史重复连接请求引发错误:假设客户端发送的旧 SYN 报文因网络延迟突然到达服务器,若仅两次握手,服务器会误以为是新连接请求并立即建立连接,浪费资源。而三次握手中,客户端在收到旧 SYN-ACK 后会发现该连接并非自己当前发起的(序列号不匹配),从而发送 RST 或忽略,避免无效连接

为什么不是两次握手?

如果使用的是两次握手建立连接,可能客户端发送的第一个请求连接并且没有丢失,只是因为在网络中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。之前滞留的那一次请求连接,因为网络通畅了,到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

四次挥手

  1. 主动关闭方(如客户端)发送 FIN:表示“我已无数据要发送”,进入 FIN-WAIT-1 状态。
  2. 被动关闭方(如服务器)回复 ACK:确认收到 FIN,进入 CLOSE-WAIT 状态。此时客户端到服务器的连接方向已关闭,但服务器仍可继续发送数据给客户端。
  3. 服务器数据发送完毕后,发送自己的 FIN:表示“我也无数据要发了”,进入 LAST-ACK 状态。
  4. 客户端回复 ACK:确认收到服务器的 FIN,进入 TIME-WAIT 状态,并等待一段时间(通常为 2×MSL,Maximum Segment Lifetime)后才彻底关闭连接。服务器收到 ACK 后立即关闭。

TCP跟UDP的区别

  • TCP:面向连接(Connection-Oriented)
    通信前需通过“三次握手”建立连接,通信结束后通过“四次挥手”断开连接。
  • UDP:无连接(Connectionless)
    发送数据前无需建立连接,直接发送数据包。
  • TCP:可靠传输
    提供确认机制(ACK)、重传机制、错误校验,确保数据完整、有序到达。
  • UDP:不可靠传输
    不保证数据是否到达、是否重复或乱序,可能丢包。
  • TCP:开销大、速度相对较慢
    因需维护连接状态、确认、重传等机制,头部开销大(20字节起)。
  • UDP:开销小、传输快
    头部仅8字节,无连接管理,适合实时性要求高的场景。
  • TCP:保证数据按发送顺序到达
    使用序列号和确认机制对数据包排序。
  • UDP:不保证顺序
    数据包独立传输,可能乱序到达。
  • TCP:具备流量控制(滑动窗口)和拥塞控制机制
    避免网络过载,动态调整发送速率。
  • UDP:无流量控制和拥塞控制
    应用层需自行处理(如视频会议软件)。

常用应用层协议底层情况

协议 端口 传输层协议
HTTP 80 TCP
HTTPS 443 TCP
FTP 21控制/20数据 TCP
SMTP 25/587/465 TCP
POP3 110/995(加密) TCP
IMAP(互联网消息访问协议) 143/993(加密) TCP
TELNET 23 TCP
SSH 22 TCP
DNS 53 UDP大响应时用TCP
DHCP 67(服务器)/68(客户端) UDP
TFTP 69 UDP
SNMP 161/162 UDP
NTP 动态 UDP

网络层

DNS查询过程?

  • 客户机向其本地域名服务器发出DNS请求报文
  • 本地域名服务器收到请求后,查询本地缓存,假设没有该记录,则以DNS客户的身份向根域名服务器发出解析请求
  • 根域名服务器收到请求后,判断该域名所属域,将对应的顶级域名服务器的IP地址返回给本地域名服务器
  • 本地域名服务器向顶级域名服务器发出解析请求报文
  • 顶级域名服务器收到请求后,将所对应的授权域名服务器的IP地址返回给本地域名服务器
  • 本地域名服务器向授权域名服务器发起解析请求报文
  • 授权域名服务器收到请求后,将查询结果返回给本地域名服务器
  • 本地域名服务器将查询结果保存到本地缓存,同时返回给客户机

HTTP与HTTPS

HTTP和HTTPS区别?

  • HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP)数据传输过程是加密的,安全性较好。
  • 使用 HTTPS 协议需要到 CA 申请证书。
  • HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,而 HTTPS除了 TCP 的三个包,还要加上SSL握手的消耗。
  • 用的端口也不一样,前者是 80,后者是 443。
  • HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。

操作系统

进程/线程

  • 进程:进程是操作系统进行资源分配和调度的基本单位,是一个独立运行的程序实体。每个进程拥有独立的内存空间、文件描述符、寄存器状态等资源。进程之间的资源是相互隔离的,因此进程间通信需要通过操作系统提供的特定机制(如管道、消息队列、共享内存等)进行。由于进程拥有独立的资源,所以进程间的切换和调度开销较大。
  • 线程:线程是操作系统调度执行的最小单位,是进程内的一个执行流。一个进程可以拥有多个线程,这些线程共享进程的资源(如内存空间、文件描述符等)。由于线程共享相同的资源,线程间通信相对简单,可以直接通过共享变量、锁等方式进行。线程相较于进程,上下文切换和调度开销较小。但多个线程并发执行时,需要处理好同步和互斥问题,以避免数据不一致或竞争条件。
  • 协程:协程是一种用户态的轻量级线程,它的调度和切换完全由程序控制,不依赖于操作系统的调度。协程之间共享线程的资源,因此协程间通信也可以通过共享变量、锁等方式进行。协程的优势在于能够轻松地实现高并发,因为协程切换和调度的开销非常小。协程适用于I/O密集型任务,通过异步I/O可以有效地提高程序的性能。

用户线程和内核线程

  • 用户线程:用户线程是完全在用户空间中实现和管理的线程。它们的创建、同步和调度都由用户级别的线程库(如POSIX线程库,即Pthreads)处理,而不需要内核直接参与。由于用户线程的操作不涉及系统调用,它们的创建和切换开销相对较小。用户线程的一个主要限制是,它们不能充分利用多核处理器的并行能力。因为操作系统调度的基本单位是内核线程,当一个用户线程阻塞时(如I/O操作),整个进程都会被阻塞,即使其他用户线程仍处于就绪状态。这可能导致多处理器系统中的性能下降。
  • 内核线程:内核线程是由操作系统内核直接支持和管理的线程。内核负责创建、调度和销毁内核线程,每个内核线程都拥有独立的内核栈和线程上下文。由于内核线程是操作系统调度的基本单位,它们可以充分利用多处理器系统的并行能力。内核线程的缺点是,它们的创建、切换和同步操作涉及系统调用,导致较大的开销。此外,内核线程需要更多的内核资源(如内核栈),这可能在大量线程的情况下导致资源耗尽。

父子进程

在操作系统中,进程可以创建其他进程。创建新进程的进程称为父进程,新创建的进程称为子进程。这种关系形成了一个进程树结构,其中根进程(如 Unix 和类 Unix 系统中的 init 进程,进程ID为1)是所有其他进程的祖先。在 Unix 和类 Unix 系统中,可以通过 fork() 系统调用创建子进程。fork() 调用会复制当前进程的地址空间和环境,并创建一个新的进程。子进程从 fork() 调用处继续执行,并继承父进程的大部分属性(如文件描述符、环境变量等)。父子进程可以通过 getpid()(获取当前进程ID)和 getppid()(获取父进程ID)系统调用来识别彼此。

进程调度算法

  • 时间片轮转(RR):将CPU划分为一个一个时间片,每个任务独占CPU的一个时间片。时间片选的越小,那么任务响应的时间就越快,但是这意味着调度的次数会增加,调度的开销就大。弊端是任务的平均周转时间比较高
  • 优先级调度:在RR的时间片轮转基础上,满足用户的响应时间指标后考虑系统性能的。首先为了给用户提供更好的体验,交互式任务的优先级一定高于批处理任务。
  • 先到先得FCFS:任务到达时间早的优先级高。这个策略最大的特点就是简单直观,开发者只用维护一个队列即可。这个策略属于非抢占式调度,在任务执行完前是不会让出CPU的。短任务不友好,IO密集型也不友好
  • 短任务优先SJF:考虑执行时间最短的任务优先级高

进程通信方式

  • 管道:管道是一种半双工的通信机制,主要用于具有亲缘关系的进程(如父子进程)之间传递数据。它在内核中维护一个缓冲区,一端用于写入,另一端用于读取,数据遵循先进先出(FIFO)原则。匿名管道仅限于本地进程间通信,而命名管道(FIFO)可通过文件系统路径被无关进程访问。
  • 消息队列:消息队列允许进程通过发送和接收格式化的消息进行通信,消息按类型或优先级排队,接收方可选择性地读取特定类型的消息。与管道不同,消息队列支持异步通信、消息持久化(直到被读取),且不要求进程同时运行,适用于复杂或松耦合的进程协作场景。
  • 信号量:信号量本质上是一个计数器,用于协调多个进程或线程对共享资源的并发访问,实现同步与互斥。它通过原子操作(如 P/V 操作)控制资源的可用数量,常用于防止竞态条件,例如确保同一时刻只有一个进程进入临界区。
  • 共享内存:共享内存允许多个进程直接访问同一块物理内存区域,是最快的 IPC 方式,因为数据无需在内核与用户空间之间复制。但它本身不提供同步机制,通常需配合信号量或互斥锁使用,以避免多个进程同时修改数据导致不一致。

中断与异常

外部中断

外部中断是由计算机系统外部事件触发的,通常与硬件设备相关。外部中断的目的是通知处理器某个外部设备需要处理器的注意,例如设备需要传输数据、设备发生错误等

异常

异常是由计算机系统内部事件触发的,通常与正在执行的程序或指令有关。异常的目的是通知处理器某个指令无法正常执行。

并发/并行

  • 并发:指一个时间段内同时处理多个任务的能力。它强调的是任务之间的独立性,以及它们是如何在一个处理器或单个核心上交替执行的。
  • 并行:指在同一时刻执行多个任务的能力。
    并发关注在一个时间段内处理多个任务,而并行关注在同一时刻执行多个任务

内存管理

碎片

  • 内部碎片:指已分配给进程的内存块中,未被实际使用的那部分空间。这部分内存虽然属于进程,但进程无法使用,造成浪费。
  • 外部碎片:指内存中存在大量不连续的小块空闲内存,总和足够大,但无法满足一个较大内存请求的现象

如何避免内部碎片?

使用更小的页面大小(但会增加页表开销)。
在分页系统中,内部碎片不可避免,但通常较小(平均浪费约半页)。
对于动态分配,可采用伙伴系统(Buddy System) 或slab 分配器等更精细的内存管理策略。

如何避免外部碎片?

内存紧缩(Compaction):
将所有已分配的内存块“移动”到一端,合并空闲块形成大块连续空间。但开销大,且需硬件支持(如重定位寄存器)。
采用分页(Paging)机制:
分页不要求物理内存连续,逻辑地址通过页表映射到非连续的物理页,从根本上消除外部碎片(但引入内部碎片)。
采用分段+分页(段页式):兼顾逻辑模块化与物理非连续分配。
使用更智能的分配算法:
如“最坏适应”(Worst Fit)可能减少小碎片,但效果有限。

内存管理算法:

  • 首次适应(First Fit):从内存低地址开始查找,找到第一个足够大的空闲分区就分配
  • 最佳适应(Best Fit):遍历所有空闲分区,选择满足需求且大小最小的那个
  • 最坏适应(Worst Fit):总是选择最大的空闲分区进行分配,大块分出去后剩下的部分仍可能较大,便于后续分配
  • 邻近适应 / 循环首次适应:从上次分配结束的位置开始查找,找到第一个足够大的空闲区
  • 伙伴:伙伴系统将内存划分为大小为 2 的幂次(如 1 页、2 页、4 页、8 页……) 的块。当请求一块内存时,系统会找到最小的、大小 ≥ 请求大小的 2 的幂次块进行分配。
    如果该大小的块不可用,就将一个更大的“伙伴”块递归拆分,直到得到所需大小;
    当释放内存时,系统会检查其“伙伴”是否也空闲,若是,则合并成更大的块,以减少碎片。

数据结构

排序算法

  • 插入排序:每一趟将一个待排序记录按其关键字的大小插入到已排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。排序算法稳定。时间复杂度 O(n²),空间复杂度 O(1)。
  • 希尔排序:把记录按下标的一定增量分组,对每组进行直接插入排序,每次排序后减小增量,当增量减至 1 时排序完毕。排序算法不稳定。时间复杂度 O(nlogn),空间复杂度 O(1)。
  • 直接选择排序:每次在未排序序列中找到最小元素,和未排序序列的第一个元素交换位置,再在剩余未排序序列中重复该操作直到所有元素排序完毕。排序算法不稳定。时间复杂度 O(n²),空间复杂度 O(1)。
  • 堆排序:将待排序数组看作一个树状数组,建立一个二叉树堆。通过对这种数据结构进行每个元素的插入,完成排序工作。排序算法不稳定,时间复杂度 O(nlogn),空间复杂度 O(1)。
  • 冒泡排序:比较相邻的元素,如果第一个比第二个大就进行交换,对每一对相邻元素做同样的工作。排序算法稳定,时间复杂度 O(n²),空间复杂度 O(1)。
  • 快速排序:随机选择一个基准元素,通过一趟排序将要排序的数据分割成独立的两部分,一部分全部小于等于基准元素,一部分全部大于等于基准元素,再按此方法递归对这两部分数据进行快速排序。排序算法不稳定,时间复杂度 O(nlogn),空间复杂度 O(logn)。
  • 归并排序:将待排序序列分成两部分,然后对两部分分别递归排序,最后进行合并。排序算法稳定,时间复杂度都为 O(nlogn),空间复杂度为 O(n)。

Python

赋值/深浅拷贝

  • 赋值:就是简单的对象引用,在以下情况中,ab一样,b只是a的别名,它们指向同一片内存。a is b返回True
    1
    2
    a = [1,2,"hello",['python', 'C++']]
    b = a
  • 浅拷贝:浅拷贝会创建新对象,其内容非原对象本身的引用,而是原对象内第一层对象的引用。浅拷贝有三种形式:切片操作(b = a[:])、工厂函数(b = list(a))、copy 模块中的 copy 函数(b = copy.copy(a))。在这种情况下,列表 a 和 b 是不同的对象,修改列表 b 理论上不会影响到列表 a。

但是要注意的是,浅拷贝之所以称之为浅拷贝,是它仅仅只拷贝了一层,在列表 a 中有一个嵌套的 list,如果我们修改了它,情况就不一样了。

1
2
3
4
5
变量是到内存空间的一个指针,也就是拥有指向对象连接的空间;

对象是一块内存,表示它们所代表的值;

引用就是自动形成的从变量到对象的指针。
  • 深拷贝:深拷贝和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。

init__和__new

当我们使用「类名()」创建对象的时候,Python 解释器会帮我们做两件事情:第一件是为对象在内存分配空间,第二件是为对象进行初始化。「分配空间」是__new__ 方法,初始化是__init__方法。

管理内存

  • 引用计数:Python内部维护了一个引用计数器,用来确保对象被需要时保持在内存中,不再需要时释放。
  • 垃圾回收:对于循环引用的情况,引用计数器就无能为力了。Python的垃圾回收器会定期运行,检测并清除循环引用的对象。

列表和元组

  • 列表(List):动态数组,可变类型,可以增加、删除或搜索列表中的元素。
  • 元组(Tuple):不可变序列,一旦创建就不能修改。因为不可变性,元组可以作为字典的键,而列表则不行。

装饰器

装饰器(Decorator) 是一种用于修改或增强函数(或类)行为的高级语法工具,它允许你在不修改原函数代码的前提下,为其动态地添加额外功能(如日志、权限检查、缓存、计时等)
示例:
如果有个函数,你想在每次调用 greet() 时自动记录日志,但又不想修改 greet 的内部代码。这时就可以用装饰器。

1
2
def greet():
print("Hello!")
1
2
3
4
5
6
7
# 定义装饰器函数
def log_call(func):
def wrapper():
print(f"Calling function: {func.__name__}")
func() # 调用原函数
print("Function call ended.")
return wrapper
1
2
3
4
5
6
7
# 应用装饰器

@log_call
def greet():
print("Hello!")

greet()

输出:

1
2
3
Calling function: greet
Hello!
Function call ended.

*args和**kwargs是什么

  • *args:允许函数接受任意数量的位置参数。会将传入的额外位置参数打包成一个元组(tuple)
  • **kwargs:允许函数接受任意数量的关键字参数。将传入的额外关键字参数打包成一个字典(dict)

生成器

生成器是一种特殊类型的迭代器,使用yield语句一次返回一个值。生成器函数在每次生成一个值后,会暂停其状态,等待下一次调用。

匿名函数(lambda)

Lambda函数是一种匿名函数,使用lambda关键字定义。它们通常用于需要函数对象的地方,但又不想在全局命名空间中定义一个完整的函数。

安全

【2021】OWASP TOP5

  1. 失效的访问控制应用程序未正确实施权限检查,导致用户可以访问本不应有权访问的数据或功能。(例如普通用户通过修改 URL 中的 ID(如 /user/123 → /user/124)查看他人资料)
  2. 加密失效敏感数据(密码、身份证、银行卡号)未加密存储或传输;使用弱加密算法(如 MD5、SHA1、DES);HTTPS 配置错误(如支持 SSLv3、弱密码套件);
  3. 注入将不可信数据作为命令或查询的一部分执行,而未做有效隔离。
  • SQL注入:当应用程序将用户输入直接拼接到 SQL 查询语句中,而未进行适当过滤或参数化处理时,攻击者可通过构造恶意输入操控数据库查询,从而读取、修改或删除敏感数据。
  • XSS注入:应用程序将未经验证或转义的用户输入直接输出到网页中,导致攻击者可注入恶意脚本(如 JavaScript),在其他用户浏览器中执行,窃取 Cookie、会话令牌或进行钓鱼。
  1. 不安全设计:缺乏安全设计流程(如未做威胁建模);业务逻辑本身存在缺陷(如“忘记密码”流程可被暴力枚举)
  2. SSRF服务端请求伪造:应用程序未验证用户提供的 URL,直接用服务端发起 HTTP 请求。攻击者可诱使服务器访问内部系统、本地文件和内网服务等

其他WEB相关漏洞

  1. 不安全的反序列化:应用程序反序列化来自不可信源的数据(如 Cookie、请求参数),若未验证数据完整性,攻击者可构造恶意序列化对象,触发远程代码执行、权限提升或拒绝服务。
  2. CSRF跨站请求伪造:攻击者诱使已登录用户在不知情的情况下向目标网站发送恶意请求(如转账、修改密码),利用用户当前有效的会话凭证执行非本意操作。

密码相关

(非)对称密码

对称密码和非对称密码是密码学中的两类基本加密机制。对称密码使用同一个密钥进行加密和解密(如 AES、DES),加解密速度快,适合大量数据加密,但密钥分发和管理困难;非对称密码则使用一对密钥——公钥用于加密,私钥用于解密(如 RSA、ECC),解决了密钥分发问题,支持数字签名和身份认证,但计算开销大、速度较慢。实际应用中常将两者结合:用非对称密码安全交换对称密钥,再用对称密码高效加密数据(如 TLS 协议)。

什么是哈希?

哈希(Hash)是单向函数,将任意长度输入映射为固定长度摘要(如SHA-256),不可逆、无密钥。
哈希用于验证完整性(如文件校验)、存储密码;加密用于保护数据机密性。

什么是加盐?

Salt 是随机生成的唯一字符串,与密码拼接后再哈希。
作用:防止彩虹表攻击、确保相同密码生成不同哈希值。
Salt 不需要保密,可与哈希值一起存储,但必须每个用户独立生成。

什么是HMAC(Hash-based Message Authentication Code)

是一种带密钥的消息认证码,使用哈希函数(如 SHA-256)和密钥生成摘要。它同时提供数据完整性和身份认证,防止消息被篡改或伪造(如 API 签名、JWT 签名)

应用方向:

  • HTTPS(TLS):服务端提供证书(含公钥);
    客户端验证证书合法性;
    双方通过 ECDHE 等密钥交换算法协商临时对称密钥(实现前向保密);
    后续通信使用该对称密钥(如 AES-GCM)加密,同时提供完整性保护。
  • 密钥协商:由于对称密钥无法直接在网络上传输,现代系统通常借助非对称密码(如 RSA、ECDH)或可信第三方(如 KDC、PKI)实现安全分发,典型应用包括 TLS 握手中的密钥协商和 Kerberos 协议。