业务安全编码规范

说明:这套编码规范是以在MOMO长期测试发现的漏洞为基础,结合OWASP编码规范提炼的一个业务层的安全要点规范。看起来有点low,也没有很强的可移植性,但是就MOMO本身的业务而已,这套规范完全覆盖了线上业务70%到80%的安全漏洞,因此,这个规范也可以看做是一个业务层场景下的安全基线。由于能力有限,其中的错误不可避免,欢迎大家指出,如果有其他的不全面的地方,也欢迎大家补充。

1.会话

序号 内容 细节&目的
1 仅在服务器上创建会话标识符(sessionID) 使用基类代码统一对每一个用户创建sessionID,sessionID具有统一的格式
目的:
1.避免会话丢失
2.降低会话被劫持的风险
2 sessionID应有效抗碰撞 sessionID的值尽可能随机,不易被预测(尽量不要携带与用户身份相关联的公开信息)
目的:
1.避免sessionID被猜测到
2.避免会话管理混乱(多个用户对应一个sessionID)
3 仅在Cookie中保存sessionID 使用Cookie作为固定的sessionID传输载体,如果是web应用,在set-cookie时设定最小使用范围的domain和path
目的:
1.避免web sever在日志中记录sessionID信息
2.防止被XSS跨域攻击
4 一个用户同一时间只维持一个会话有效 一个用户在生成新的会话的时候,应该终止同一用户上一个会话的生命周期。
目的:
1.降低会话被劫持的风险
5 注销功能应当完全终⽌止相关的会话或连接 目的:1.降低会话被劫持的风险

2.支付

序号 内容 细节&目的
6 关键参数在服务器端签名(订单号、商品ID、⾦额、数量) 将所有交易参数发送给服务器端生成一个校验和,并回传给客户端
目的:
1.防止调用支付功能时交易参数被篡改
7 签名算法不可被猜测到 服务器端的签名算法的设计应该尽可能复杂,避免被破解
目的:
防止客户端篡改参数和校验和
8 以订单号为索引记录对应所有商品信息 如果遇到比较复杂的交易逻辑,如他人代付或礼品赠送,每一笔交易支付后的回调或接口通信中传递参数,尽可能在生成订单时记录交易的商品ID、金额、数量
目的:
1.避免客户端篡改参数
9 订单号应当在服务器端生成并且不可重复 对每一笔交易在服务器端生成唯一的订单号,不接受用户指定的订单号。
目的:
1.避免逻辑错误
10 已经完成交易的订单号不可重复使⽤ “支付回调时核准订单号,对于已经完成过的订单号不可重复使用
目的:
1.防止支付成功的订单被多次使用(例如多次发放商品等)

3.上传

序号 内容 细节&目的
11 限定上传文档类型 只允许上传业务需要的文件类型
目的:
1.防止含有恶意脚本的文件被上传
12 在服务器端校验⽂文件类型 所有在客户端做的文件类型检测不能作为文件类型检测的依据,只能用来改善用户体验,文件类型检测要在服务器上执行。
目的: 
1.防止文件类型检测被绕过
13 保存在webroot以外的目录,并去掉执⾏权限 主要业务的上传文件应当存储在专门的文件服务器上,或者存放在web应用服务器非web目录中。存放文件的目录要去掉可执行权限。
目的:
1.控制上传文件的权限,避免被执行。
14 多媒体文件可执⾏一次压缩或格式变换后再存储 对于多媒体文件,可使用压缩和转码函数对数据进行转换。
目的:
1.破坏上传文件中的恶意代码”
15 在预先设置路径列表中索引文件目录路径 将使用到的目录路径,用常量的方式写死在代码中,不能使用用户传递上来的文件路径。
目的:
1.用户输入不可信,避免攻击者拼接路径越权访问操作系统上的文件。

4.验证码

序号 内容 细节&目的
16 验证码验证成功后再执⾏行其他操作 在执行所有功能性操作之前,首先检查验证码。如果验证码错误,不执行其他操作。
1.避免验证码被绕过
2.避免验证码未被绕过,但是泄露敏感信息
17 验证码应在一段时间后过期 验证码应设置合理的过期时间,尤其对于纯数字的验证码。
目的:
1.防止暴力破解。
18 验证码应当在使⽤用一次后过期 图形验证码:验证码应当在提交一次以后过期(以收到的verify_code为依据)
手机验证码:在正确提交一次以后过期,提交接口应具备一定的时间和频率限制
目的:
1.防止被暴力破解
19 图形验证码在不应以文本形式传递到客户端 “图形验证码的明文不能以任何方式传递到客户端,一些开发会把验证码发到客户端在前端做验证,这种做法是错误的。
目的:
1.防止图形验证码被绕过

5.输入验证和输出编码

序号 内容 细节&目的
20 在服务器上执行所有的输入输出编码 在服务器上执行所有的输入输出编码。
21 将数据源分为可信和不可信,所有不可信数据都要进行输入验证和输出编码。 不信任所有的用户输入,分别为输入输出的数据,制定统一的编码规则,对输入前的数据进行验证,对输出前的数据进行编码。
目的:
1.概括的说,防止数据被当做代码被执行。
22 对进行了编码的数据在字符解码后进行输入数据的验证 首先应该保证解码后的数据只包含ASCII字符,其次应该在解码后进行验证。
目的:
1.防止一些因编码引起的验证和转义的绕过
23 不信任用户输入 一切用户的输入都不可信任,包括所有参数、URL、HTTP 头信息(比如:cookie 名字和数据值)
24 验证正确的数据类型、数据范围、数据长度 对用户输入的数据进行限定,可以防止很大一部分恶意输入。
目的:
1.限制恶意用户构造恶意的脚本或数据。
25 部分需要编码的字符 1.常规的< > “ ‘ % ( ) & + \ ' " 
2.验证%0d, %0a, \r, \n
3.验证路径替代字符“点-点-斜杠”(../或 ..),如果支持 UTF-8 扩展字符集编码,验证替 代字符: %c0%ae%c0%ae

6.其他

序号 内容 细节&目的
26 验证数据的属主以防⽌平⾏权限问题 对用户的数据进行写操作时,应当判断当前用户是否对这项数据拥有权限,仅当确认用户具有这项权限时才能进行操作。
目的:
1.避免越权修改和删除他人数据。
27 多级步骤操作对每一步结果验证 某些应用的功能,需要用户在多个步骤上提交数据,甚至根据不同的状态走入不同的流程分支。后端程序应该在用户执行的每一个步骤记录一个状态,每开始新的一步操作,都应该检查上一步状态是否符合预期。对于不存在或异常的状态,应该及时中断或跳出。
目的:
1.避免攻击者绕过前面的逻辑判断,直接执行最终的结果。
28 对参数进⾏行非空校验 由于一些逻辑上的疏漏,以及后端的底层应用(数据库、中间件或编程语言)上的特性,空字符串在进行处理的时候可能会出现预期外的结果。应该尽力保证用户传入的参数是符合预期的。
目的:
1.检查是否存在用户没有传输的参数或内容为空的参数。
29 获取陌陌ID应当⽤cookie查询 应该严格的,使用sessionID这类会话标识符作为身份判断的依据,不应该接受用户上传的momoid。
目的:
1.防止用户上传momoid引起的越权操作

Author | Mushen

THE QUIETER YOU BECOME, THE MORE YOU ARE ABLE TO HEAR. 安全运营搞了十多年,现在在阿里云做安全架构和SDL。