这次面经考察的内容,我帮大家罗列一下。
-
网络:https、ca 证书
-
系统:中断、堆与栈
-
数据库:acid、隔离性、锁、乐观锁、sql 注入
-
算法:合并两个有序数组、反转链表
网络
HTTPS了解吗?讲讲 TSL 流程
传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书文件其实就是服务端的公钥,会在 TLS 握手阶段传递给客户端,而服务端的私钥则一直留在服务端,一定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会生成随机密钥,并使用服务端的公钥加密后再传给服务端。根据非对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双方就得到了相同的密钥,再用它加密应用消息。
我用 Wireshark 工具抓了用 RSA 密钥交换的 TLS 握手过程,你可以从下面看到,一共经历了四次握手:
TLS 第一次握手
首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。在这一步,客户端主要向服务器发送以下信息:(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。(2)客户端生产的随机数(Client Random),后面用于生成「会话秘钥」条件之一。(3)客户端支持的密码套件列表,如 RSA 加密算法。
TLS 第二次握手
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello。服务器回应的内容有如下内容:(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。(2)服务器生产的随机数(Server Random),也是后面用于生产「会话秘钥」条件之一。(3)确认的密码套件列表,如 RSA 加密算法。(4)服务器的数字证书。
TLS 第三次握手
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:(1)一个随机数(pre-master key)。该随机数会被服务器公钥加密。(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
TLS 第四次握手
服务器收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
然后,向客户端发送最后的信息:(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
CA 签发流程是什么?
image.png
-
服务方S向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证 (不交私钥)
-
CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等
-
如信息审核通过,CA会向申请者签发认证文件-证书
-
证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名
-
签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用CA的私钥对信息摘要进行加密,密文即签名
-
浏览器缓存内CA 证书哪里来的?
浏览器缓存中的CA证书通常来自于浏览器预装的根证书颁发机构(Root Certificate Authority),这些根证书是浏览器或操作系统内置的信任的CA机构颁发的证书。
当用户访问一个使用SSL/TLS加密的网站时,浏览器会检查网站提供的数字证书是否由根证书中的CA颁发,从而验证网站的身份和安全性。如果数字证书的颁发机构是根证书中的CA之一,浏览器会信任该证书,建立安全连接。
最后通信的时候,是怎么加密的?是用三个随机数加密吗?
客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。用这三个作为原始材料,就可以生成用于加密会 话的主密钥,叫“Master Secret”。而黑客因为拿不到“Pre-Master”,所以也就得不到主密钥。
为了保证真正的“完全随机”“不可预测”,把三个不可靠的随机数混合起来,那么“随机”的程度就非常高了,足够让黑客难以猜测。
Master Secret 计算方式贴一下 RFC 文档里的公式:
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
这里的“PRF”就是伪随机数函数,它基于密码套件里的最后一个参数,比如这次的 SHA384,通过摘要算法来再一次强化“Master Secret”的随机性。
主密钥有 48 字节,但它也不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等等,避免只用一个密钥带来的安全隐患。
系统
讲讲中断的流程
中断是计算机系统中一种机制,用于在处理器执行指令时暂停当前任务,并转而执行其他任务或处理特定事件。
以下是中断的基本流程:
-
发生中断:当外部设备或者软件程序需要处理器的注意或者响应时,会发出中断信号。处理器在接收到中断信号后,会停止当前执行的指令,保存当前执行现场,并跳转到中断处理程序执行。
-
中断响应:处理器接收到中断信号后,会根据中断向量表找到对应的中断处理程序的入口地址。处理器会保存当前执行现场(如程序计数器、寄存器状态等),以便在中断处理完成后能够恢复执行。
-
中断处理:处理器跳转到中断处理程序的入口地址开始执行中断处理程序。中断处理程序会根据中断类型进行相应的处理,可能涉及到保存现场、处理中断事件、执行特定任务等。
中断的类型有哪些?
中断按事件来源分类,可以分为外部中断和内部中断。中断事件来自于CPU外部的被称为外部中断,来自于CPU内部的则为内部中断。
进一步细分,外部中断还可分为可屏蔽中断(maskable interrupt)和不可屏蔽中断(non-maskable interrupt)两种,而内部中断按事件是否正常来划分可分为软中断和异常两种。
-
外部中断的中断事件来源于CPU外部,必然是某个硬件产生的,所以外部中断又被称为硬件中断(hardware interrupt)。计算机的外部设备,如网卡、声卡、显卡等都能产生中断。外部设备的中断信号是通过两根信号线通知CPU的,一根是INTR,另一根是NMI。CPU从INTR收到的中断信号都是不影响系统运行的,CPU可以选择屏蔽(通过设置中断屏蔽寄存器中的IF位),而从NMI中收到的中断信号则是影响系统运行的严重错误,不可屏蔽,因为屏蔽的意义不大,系统已经无法运行。
-
内部中断来自于处理器内部,其中软中断是由软件主动发起的中断,常被用于系统调用(system call);而异常则是指令执行期间CPU内部产生的错误引起的。异常也和不可屏蔽中断一样不受eflags寄存器的IF位影响,区别在于不可屏蔽中断发生的事件会导致处理器无法运行(如断电、电源故障等),而异常则是影响系统正常运行的中断(如除0、越界访问等)。
中断的作用是什么?
中断使得计算机系统具备应对对处理突发事件的能力,提高了CPU的工作效率。
如果没有中断系统,CPU就只能按照原来的程序编写的先后顺序,对各个外设进行查询和处理,即轮询工作方式,轮询方法貌似公平,但实际工作效率却很低,却不能及时响应紧急事件。
堆和栈的作用和区别是什么?
-
堆是用于动态分配内存的区域,需要手动管理内存;栈用于存储函数调用和局部变量,由编译器自动管理。
-
堆中的内存分配通常由程序员通过调用类似 malloc()、new等函数来实现,但需要手动释放,如果没有正确释放堆中分配的内存,可能会导致内存泄漏。;栈中的内存分配和释放自动化,遵循“后进先出”的原则。
-
栈空间的数据线程中是独立的,堆空间的数据在线程中是共享的,读写操作的时候,需要加锁,保证并发安全。
数据库
事务四个特性是什么?
实现事务必须要遵守 4 个特性,分别如下:
-
原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没花出去。
-
一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。
-
隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的。也就是说,消费者购买商品这个事务,是不影响其他消费者购买的。
-
持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
隔离性怎么实现的?
隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
mysql 有哪些锁?
-
全局锁:通过flush tables with read lock 语句会将整个数据库就处于只读状态了,这时其他线程执行以下操作,增删改或者表结构修改都会阻塞。全局锁主要应用于做全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。
-
表级锁:MySQL 里面表级别的锁有这几种:
-
表锁:通过lock tables 语句可以对表加表锁,表锁除了会限制别的线程的读写外,也会限制本线程接下来的读写操作。
-
元数据锁:当我们对数据库表进行操作时,会自动给这个表加上 MDL,对一张表进行 CRUD 操作时,加的是 MDL 读锁;对一张表做结构变更操作的时候,加的是 MDL 写锁;MDL 是为了保证当用户对表执行 CRUD 操作时,防止其他线程对这个表结构做了变更。
-
意向锁:当执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。意向锁的目的是为了快速判断表里是否有记录被加锁。
-
-
行级锁:InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。
-
记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的,满足读写互斥,写写互斥
-
间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。
-
Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
-
一致性怎么实现的?
-
持久性是通过 redo log (重做日志)来保证的;
-
原子性是通过 undo log(回滚日志) 来保证的;
-
隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
-
一致性则是通过持久性+原子性+隔离性来保证;
用mysql怎么实现乐观锁?
乐观锁是一种并发控制机制,主要用于管理数据库中记录的并发修改。它假设多个事务在大多数时间不会同时修改同一条记录,因此在实际提交更改之前不加锁。乐观锁通常通过记录版本号或时间戳来实现。
在表中添加一个version
字段,每次更新记录时,将这个版本号加一。更新操作时,检查版本号是否与数据库中的版本号一致,如果一致,执行更新并将版本号加一;如果不一致,表示记录已经被其他事务更新,此次更新失败。
假设有一个articles
表,包含字段article_id
, title
, content
, version
。
CREATE TABLE articles (
article_id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255),
content TEXT,
version INT DEFAULT 1
);
当你想更新一篇文章时,你会同时检查article_id
和version
字段,确保没有其他人在你读取数据后更新了它。
UPDATE articles SET
title = '新标题',
content = '新内容',
version = version + 1
WHERE article_id = 1 AND version = ?;
这里的?
是你从数据库读取记录时获取的当前版本号。如果version
字段的值已经改变(意味着其他事务已经更新了这条记录),这个更新操作将不会影响任何记录(因为WHERE
条件不满足),你可以据此判断更新是否成功。在应用程序中,执行上述SQL后,检查更新操作影响的行数。如果影响行数为0,表示更新失败,可能因为记录已被其他事务修改。
乐观锁适用于读多写少的场景,可以有效减少数据库锁的开销,提高系统的并发性能。但在高冲突环境下,可能会导致大量的更新操作失败,需要重试,从而影响性能。
sql注入了解吗?怎么防止 sql注入?
程序没有有效过滤用户的输入,使攻击者成功的向服务器提交恶意的 SQL 脚本,程序在接收后错误的将攻击者的输入作为 SQL 语句的一部分执行,导致原始的查询逻辑被改变,执行了攻击者精心构造的恶意 SQL 语句。
如从用户表根据用户名 ConstXiong 和密码 123 查用户信息
select * from user where username = 'ConstXiong' and password = '123'
恶意修改用户名参数 ConstXiong -> ConstXiong' or 1=1 --
select * from user where username = 'ConstXiong' or 1=1 --' and password = '123'
SQL 中 -- 是注释标记,如果上面这个 SQL 被执行,就可以让攻击者在不知道任何用户名和密码的情况下成功登录。
预防措施:
-
严格限制 Web 应用的数据库的操作权限,给连接数据库的用户提供满足需要的最低权限,最大限度的减少注入攻击对数据库的危害
-
校验参数的数据格式是否合法(可以使用正则或特殊字符的判断)
-
对进入数据库的特殊字符进行转义处理,或编码转换
-
预编译 SQL(Java 中使用 PreparedStatement),参数化查询方式,避免 SQL 拼接
算法
-
合并两个有序数组
-
反转链表