通用编码规范
首先请阅读并遵守[通用代码风格](https://github.com/xxjwxc/uber_go_guide_cn),大部分的风格约束是语言无关的,下面仅约束 Golang 语言相关编码规范。
开始之前也可以先阅读官方的 Go Code Review Comments 。
在上面两个规范的基础之外,你还需要遵守:
工欲善其事,必先利其器。在开始之前,请先参考[代码风格检查和静态代码检查工具])配置好工具,很多风格问题,都可以通过文件保存时的 hook 自动解决。
适当地在声明函数时为返回值命名,以增加可读性
func Location() (float64, float64) // bad func Location() (lat, long float64) // good
在 Golang 中使用 tab 缩进,禁止使用空格缩进
所有为函数、struct、全局变量和常量等的注释,都应该以被注释内容的名字开头( golint )
不要裸启动 goroutine
获取资源连接的时候,一定要处理资源关闭
注意:不要在 for 循环中获取并 defer close 资源*
定义内部的异常类型,而不要直接通过 errors.New/fmt.Errorf 抛出异常
对指针类型要做空指针判断,避免直接使用产生 NPE
不要做指针一定有值的假设。
Code Review 规范
目的
- 提高代码的质量,让代码具有更好的可读性、可维护性
- 提升成员之间的良好沟通,知识共享,鼓励大家互相帮助学习
- 在项目的早期发现代码或设计中的一些问题
检查内容
代码风格检查
- 是否符合代码规范,是否有良好的可读性。
- 不要有 import (Why is "import *" bad?)
测试代码
- 新增代码是否有对应的单元测试,是否通过了所有代码测试。
健壮性检查
- 程序的设计是否健壮,是否存在潜在的安全性/性能隐患。
- 程序是否可以回退,没有回退功能的设计是失败的设计,确保所有的版本都能回退。
- 涉及数据库的更改,一定要给相关 leader 进行 review。数据库的修改只能是增量的,在废除了字段依赖关系的下一个版本发布前,只能添加对应的表或字段,不能删除。一旦实施标准,应该有代码清除上一个版本遗留的不再需要的数据。
文档检查
- 是否有相应的需求/设计文档,文档是描述的是否清晰能让他人明白,代码的实现是否与文档一致。
注释检查
- 重要的代码是否有注释,注释描述的是否准确,注释的代码是否存在不必要的复杂逻辑。
- hack 和 magic number 的原因是否说明
Commits
- Commits message 是否有意义
注意事项
- Code review 不承担发现代码错误的职责。Code review 主要是审核代码的质量,如可读性,可维护性,以及程序的逻辑和对需求和设计的实现。
通用Golang 安全规范
其他
- 输入验证
- 在可信系统上进行输入验证,比如:后端服务器。
- 所有来自用户可控的输入均需验证,如:URL、参数、HTTP 头信息、cookie 等。
- 处理用户输入的数据类型及格式时,采取 Golang 原生库提供的相应方法。
- 用 strconv 包处理字符串到其它类型的转换。`Atoi ParseBool ParseFloat ParseInt`
- 用 strings 包处理字符串及字符串属性。`Trim ToLower ToTitle`
- 用 regexp 包处理正则。
- 用 utf8 包处理 utf8 相关编码处理。`Valid ValidRune ValidString EncodeRune DecodeLastRune DecodeLastRuneInString DecodeRune DecodeRuneInString`
- 校验数据有效性
- 输入口尽可能采取白名单输入
- 边界校验,判断输入长度
- 检查空字节 (%00)
- 检查换行 (%0d,%0a,\r\n)
- 检查路径符(../ 或 \\..)
- 检查扩展的 UTF-8 特殊字符
- 为防御用户输入可能带来的 SQL 注入,请采用预声明变量的方法。
- XML 外部实体注入。当允许引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口等。go 语言原生 encoding/xml 不会解析 XML entities,如果要使用第三方处理 xml 库,请注意是否有规避实体解析的安全用法。
- SSRF (服务端请求伪造) SSRF 为一种常见的 Web 漏洞,由于服务器提供了从其它服务应用获取数据的功能且没有对目标地址做限制。导致攻击者通过服务器作为代理,进一步攻击内网。防御方法为指定可访问的白名单列表,或正确识别出访问地址是否为内网地址
- 命令执行检
- 输出编码
- 身份验证与密码管理
- 验证过程必须在可信的系统上进行,比如:后端服务器。
- 除了那些特定的的设定为公开的内容外,对所有网页和资源要求身份验证。
- 身份验证的失败提示信息应当避免过于明确。比如:可以使用“用户名和/或密码错误”,而不 要使用“用户名错误”或者“密码错误”。错误提示信息在显示和源代码中应保持一致。
- 保存的密码应采用单向不可逆的方式,并有随机 salt。如 bcrypt 方法。
- 只能使用 HTTP POST 请求传递身份验证的凭据信息。
- 会话管理
- 会话标识符必须总是在一个可信系统(比如:后端服务器)上创建。
- 不能以 GET 参数传递会话标识符。
- 会话管理应当保证足够的随机会话标识符,并给 cookie设置 `Domain`、`Path`、`Expires`、`HTTPOnly`、`Secure `等参数。通常情况下,我们把 Expires 值设置为 30 分钟以此来降低应用程序风险。
- 防止产生 CSRF 漏洞(跨站请求伪造)
- 访问控制
- 使用可信系统做访问授权控制,比如:后端服务器。
- 限制只有授权用户才能访问文件、资源、受保护的 URL、受保护的功能、服务及数据。
- 对于越权的控制上,前端应禁止直接对象引用,使用访问资源的数据库字段。如:前端直接使用 member_id 来进行用户可访问资源的标示,篡改遍历此字段可访问到非授权的资源。
- 文件管理
- 上传应该仅限于经过身份验证的用户。
- 确保只有白名单中的文件类型才能上传到服务器。这里给出检测 MIME 类型的参考代码:
- 不要将文件保存在与应用程序相同的 Web 环境中。文件应当保存在内容服务器或数据库中。
- 防止或限制上传可能被 Web 服务器解析的文件。
- 不要传递目录或文件路径,使用预先设置路径列表中的匹配索引值。
- 不要将绝对路径暴露给用户。
- 不要将用户提交的数据传递到动态重定向中。如果必须允许使用,那么重定向应当只接受通 过验证的相对路径 URL。 以避免可能带来的文件遍历。攻击示例如:http://www.example.com/read?path=../../../../etc/passwd
- 内存管理
- 缓冲区边界检查,避免超过分配的空间。
- 确保资源已经被释放,在 go 语言中可以使用 Defer 来进行。
- nil 指针判断
- 避免使用已知的易受攻击的函数。在 go 语言中,Unsafe 包和 Testing 包不应被用于生产环境。
- make 分配长度验证
- 禁止重复释放 channel
- 建议不使用 slice 作为函数入参
- 其它
- 线上代码中不能出现密码、测试帐号等硬编码方式,防止为外部留下后门。
- 与第三方的接口调用,如 IAP 等严格准照官方的安全准则和接入规范,避免出现越权访问等情况。
- 避免引用存在安全风险的第三方组件。