2.SDL规范文档

01.安全设计Checklist

输入验证

  • 校验跨信任边界传递的不可信数据(策略检查数据合法性,含白名单机制等)格式化字符串时,依然要检验用户输入的合法性,避免可造成系统信息泄露或者拒绝服务

  • 禁止向Java Runtime.exec()方法传递不可信、未净化的数据(当参数中包含空格,双引号,以-或者/符号开头表示一个参数开关时,可能会导致参数注入漏洞),建议如果可以禁止JVM执行外部命令,未知漏洞的危害性会大大降低,可以大大提高JVM的安全性。

  • 验证路径之前应该先将其标准化为实际路径(特殊的文件名,比如“..”,symbolic links、hard links、shortcuts)

  • 从ZipInputStream提取文件,如果不在程序预期计划的目录之内时,应拒绝将其提取出来,或者将其提取到一个安全的位置

  • 从ZipInputStream提取文件,若解压之后的文件大小超过一定的限制时,必须拒绝将其解压

  • 在处理以前,验证所有来自客户端的数据,包括:所有参数、URL、HTTP头信息(比如:cookie名字和数据值),确定包括了来自 JavaScript、Flash 或其他嵌入代码的post 返回信息

  • 如果任何潜在的危险字符必须被作为输入,请确保您执行了额外的安全控制,比如:输入转义、输出编码、特定的安全 API等。部分常见的危险字符,包含但不限于: < > " ' % ( ) & + \ \' \"

  • 如果您使用的标准验证规则无法验证下面的输入,那么它们需要被单独验证,比如验证空字节 (%00); 验证换行符 (%0d, %0a, \r, \n); 验证路径替代字符“点-点-斜杠”(../或 ..\);如果支持 UTF-8 扩展字符集编码,验证替代字符: %c0%ae%c0%ae/ (使用规范化验证双编码或其他类型的编码)

  • 严格验证来自重定向输入的数据(一个攻击者可能向重定向的目标直接提交恶意代码,从而避开应用程序逻辑以及在重定向前执行的任何验证)

  • 验证数据类型

  • 验证数据范围

  • 验证数据长度


输出编码

  • 为每一种输出编码方法采用一个标准的、已通过测试的规则

  • 通过语义输出编码方式,对所有从服务端返回到客户端的数据进行编码。比如HTML编码、URL编码等,编码形式需根据具体的应用场景选择

  • 除非对目标编译器是安全的,否则请对所有字符进行编码

  • 针对 SQL、XML 和 LDAP 查询,语义净化所有不可信数据的输出

  • 对于操作系统命令,净化所有不可信数据输出


异常处理

  • 禁止在异常中泄露敏感信息(敏感数据的范围应该基于应用场景以及产品威胁分析的结果来确定。典型的敏感数据包括口令、银行账号、个人信息、通讯记录、密钥等)

  • 禁止在异常中泄露应用服务器的指纹信息(如版本,路径,架构)

  • 方法发生异常时要恢复到之前的对象状态(业务操作失败时,进行回滚业务;或者避免去修改对象状态,维持对象状态一致性)

  • I/O操作- 临时文件使用完毕应及时删除

  • 不要将Buffer对象封装的数据暴露给不可信代码

  • 在多用户系统中创建文件时指定合适的访问许可,以防止未授权的文件访问

  • 当一个外部进程通过其输出流对外输出信息或错误时,必须及时清空其输出流,以防止输出流中的缓冲区被耗尽而导致外部进程被阻塞。

  • 白名单控制共享目录操作文件权限,比如读/写/可执行权限


运行环境

  • 不要使用危险的许可与目标组合(比如不要将AllPermission许可赋予给不信任的代码,不要将ReflectPermission许可和suppressAccessChecks目标组合使用,不要将java.lang.RuntimePermission许可与createClassLoader目标组合)

  • 不要禁用JVM字节码验证,如果使用的字节码,如class文件被恶意篡改过,将会存在安全风险

  • 建议监控平台不要对互联网开放,仅限于内网环境访问;如果监控平台存在远程执行漏洞,将会给所监控的应用带来安全风险

  • 建议将所有安全敏感代码(例如进行权限控制或者用户名密码校验的代码)都放在一个jar包中

  • 生产代码不能包含任何调试代码或接口


身份验证

  • 除了那些特定设为“公开”的内容以外,对所有的网页和资源都要求进行身份验证,并正确设计身份验证功能

  • 所有的身份验证过程必须在服务器后端上执行

  • 在任何可能的情况下,建立并使用标准的、已通过安全测试的身份验证服务(比如 C4A)

  • 所有的身份验证控制应当安全的处理未成功的身份验证,比如给出错误模糊提示,隐藏敏感信息

  • 登录入口应具有防止暴力猜解及撞库猜解(利用已泄漏的密码字典进行批量登录尝试)的措施,超过设定失败次数需要启用锁定或图片随机码进行访问限制

  • 采用https post请求方式传输身份验证的凭据信息

  • 身份验证的失败提示信息采用模糊处理,比如可以使用“用户名或密码错误”,而不要使用“用户名错误”或者“密码错误”明确提示。

  • 涉及敏感信息或功能的外部系统连接应配置身份验证功能,并进行有效身份验证控制

  • 在执行关键操作(如个人信息密码修改操作)时,应对用户身份进行再次验证

  • 为高度敏感或重要的交易账户使用多因子身份验证机制,如支付密码、短信验证码等


短信验证码

  • 一次一用

  • 发送频率控制(建议60s获取一次)

  • 验证码有效期(建议60s内有效,发短信时进行友好提示)

  • 复杂度(短信验证码建议6位数字)

  • 安全提示:是否是个人自己操作等风险提示信息

  • 在前端校验(客户端的校验只能作为辅助手段,很容易被绕过),必须使用服务端代码对输入数据进行最终校验

  • 短信验证码需要限制频率使用,例如:每天一个手机号码只允许发送5次,防止被黑客恶意消耗短信

  • 不同场景的短信验证码不可通用

  • 单个短信验证码限制有效验证次数

  • 验证码需要对应手机号不可通用

  • 限制对短信接口的调用(1.推荐添加验证码保证需要人类交互才可以发送短信2.根据自己的业务特点限制每个IP每天的最大发送量)


图形验证码

  • 一次一用

  • 验证码有效期(10分钟内有效,可根据场景兼容安全和体验灵活设置)

  • 复杂度(4位及以上数字、字母交替),根据需要也可采用当下流行的拖拽验证码或计算值的验证方式

  • 服务器端进行认证

  • 从用户体验和安全角度出发,可设计为当用户输3次错误密码后自动弹出验证码输入框进行验证操作


密码管理

  • 禁止使用私有或者弱加密算法(比如禁止使用DES,SHA1等,推荐使用AES: 128位,RSA: 2048位,DSA: 2048位)

  • 采用基于哈希算法和加入盐值(salt)方式安全存储口令信息

  • 数据库连接配置中的用户密码要以加密的形式存储(建议所有涉及密码存储的功能点进行加密存储)

  • 保证密码传输过程需要加密(建议使用https)

  • 密码输入框,可设计为显示密码和隐藏密码切换功能

  • 密码重设和更改操作,需要进行二次合法身份验证

  • 密码重设时,应对注册手机号和邮箱进行有效验证,链接只能发送到预先注册的邮件地址或预先绑定的手机号

  • 临时密码和链接应设计一个短暂的有效期(比如5分钟),防止暴力破解

  • 当密码重新设置时,应短信通知用户是否是本人在操作,告知安全风险

  • 密码复杂度设置:建议8个字符以上,包含字母、数字及特殊字符等

  • 密码设置场景中应具有密码复杂度检查功能(建议在后台验证密码复杂度)

  • 密码不能输出到日志和控制台

  • 建议设计密码定期修改提醒机制


会话安全

  • 用户登出后应立即清理会话及其相关登录信息

  • 注销功能应当完全终止相关的会话或连接

  • 增加Cookie 安全性,添加“HttpOnly”和“secure”属性(当“secure”属性设置为true时表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在HTTPS 连接中被浏览器传递到服务器端进行会话验证,在 HTTP 连接中不会传递该信息,也就不会存在Cookie被窃取的问题;设置了"HttpOnly"属性,通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样也能减少XSS跨站脚本攻击风险)

  • 会话cookie应设计有效期,超时后立即失效

  • 当设计允许用户在多渠道终端同时登录时,建议应进行常用设备登录限制

  • 为包含已验证的会话标识符的 cookie 设置域和路径,为站点设置一个恰当的限制值。默认cookie的域是当前域名,默认cookie的路径是当前页面的目录路径。如果想要跨域或者在其他的路径下访问cookie就必须要重新设置这两个属性,domain和path。

  • 注销功能应当可用于所有受身份验证保护的网页

  • 在平衡风险和业务功能需求的基础上,设置一个尽量短的会话超时时间。通常情况下,应当不超过几个小时。

  • 不要在URL、错误信息或日志中暴露会话标识符,会话标识符应当只出现在http头信息中,不要将会话标识符以 GET 参数进行传递

  • 定期生成一个新的会话标识符并周期性地使上一个会话标识符失效(这可以缓解那些原标识符被获得的特定会话劫持情况)

  • 在身份验证的时候,如果连接从 HTTP 变为 HTTPS,则会生成一个新的会话标识符。在应用程序中,推荐持续使用 HTTPS,不应在 HTTP 和 HTTPS 之间来回转换,有效避免切换过程会话被劫持篡改。

  • 为服务器端的操作执行标准的安全会话管理,为每个会话执行合法的身份验证和权限控制,防止存在CSRF跨站点请求伪造漏洞


访问控制

  • 将具有特权的逻辑从其他应用程序代码中隔离开

  • 限制只有授权的用户才能访问文件资源

  • 限制只有授权的用户才能访问受保护的URL

  • 限制只有授权的用户才能访问受保护的功能或服务

  • 建议只有授权的用户才能访问直接对象引用

  • 限制只有授权的用户才能访问受保护的应用程序数据

  • 限制只有授权的用户才能访问与安全相关的配置信息

  • 限制只有授权的外部应用程序或接口才能访问受保护的本地程序或资源

  • 服务器端执行的访问控制规则和前端实施的访问控制规则必须匹配

  • 服务器中创建文件时需指定合理的访问权限(读/写/可执行)

  • 当权限重新设置发生变更时,应记录好日志,并短信通知用户是否是本人在操作,告知可能存在的安全风险


日志规范

  • 不要在日志中保存敏感信息,包括系统指纹信息、会话标识符、账号密码、证件、ID等

  • 确保日志记录包含了重要的日志事件数据

  • 记录所有失败和成功的输入验证

  • 记录所有失败和成功的身份验证记录

  • 记录所有失败和成功的访问和操作记录

  • 记录明显的修改事件,包括对于状态数据的修改

  • 记录连接无效或者已过期的会话令牌尝试

  • 记录所有的管理功能操作行为,包含但不限于安全配置设置的变更

  • 记录所有失败和成功的后端连接

  • 记录加密模块的错误信息

  • 禁止将日志直接保存在可被浏览器访问到的WEB目录中


敏感信息

  • 临时产生的敏感数据(写入内存或文件),应具有及时清除和释放机制

  • 不要在 HTTP GET 请求参数中包含敏感信息,如用户名、密码、卡号、ID等

  • 禁止表单中的自动填充功能,因为表单中可能包含敏感信息,包括身份验证信息

  • 不要在客户端上以明文形式保存密码或其他敏感信息

  • 为所有敏感信息采用SSL加密传输

  • 禁止将敏感信息(包含加密秘钥等)硬编码在程序中

  • 禁止明文存储用户的密码、身份证号、银行卡号、持卡人姓名等敏感信息

  • 不要在日志中保存敏感信息,包含但不限于系统详细信息、会话标识符、密码等

  • 禁止在异常中泄露应用服务器的指纹信息,如版本,路径,组件版本等

  • 禁止将源码或sql上传到开源平台或社区,如github、开源中国等

  • 请求中含有敏感参数(如订单号、ID等),应进行混淆方式处理,防止产生参数遍历获取信息风险

  • 敏感信息需要展示在web页面上时,应在后台进行敏感字段脱敏处理

  • 请求返回数据不应包含请求之外的业务数据,特别是敏感信息数据


密码找回安全

  • 服务器端要做认证,避免绕过前端控制

  • 增加二次认证因子,如验证码

  • 涉及登录验证token之类的,不要直接将验证内容直接返回给用户

  • 认证凭证加密,推荐强算法(推荐使用AES: 128位,RSA: 2048位,DSA: 2048位)

  • 认证凭证中的参数应进行混淆处理

  • 在多个验证操作中,要对各验证机制进行排序,以防出现跳过前面验证机制直接到最后一步认证的安全风险

  • 手机短信码验证,需同时校验手机号和短信是否对应

  • 输入框中,应校验输入数据合法性,防止产生XSS跨站脚本攻击

  • 密码找回链接限制有效访问时间和复用次数(不可重复使用)


SQL注入

  • 永远不要信任用户的输入,要对用户的所有输入进行校验,包含SQL语句的过滤和转义

  • 永远不要使用动态拼装SQL,可以使用参数化的SQL或者使用存储过程进行数据查询存取

  • 永远不要使用管理员权限进行数据库连接,为每个应用使用单独的非特权权限,且配置有限的数据库连接数

  • 不要把敏感信息明文存放,采用加密或者哈希、混淆等方式对敏感信息进行脱敏存储

  • 应用的异常信息应不带有敏感信息,给出尽可能少的提示;建议使用自定义的错误信息对原始错误信息进行包装,可把异常信息存放在独立的数据库表中

  • XML注入- 不要使用字符串/StringBuffer/StringBuilder/StringFormat组装XML

  • 建议对XML元素属性或者内容进行转义


XSS跨站脚本攻击

  • 对输入的数据进行过滤和转义,包含但不限于< >" ' % ( ) & + \ \' \"等危险特殊字符

  • 数据添加到html元素属性或者内容中时,对数据进行HTML转义

  • 数据添加到script脚本中时,对数据进行script转义

  • 数据添加到style中时,对数据进行css转义


CSRF跨站请求伪造

  • 建议在每个关键表单中引入了CSRF Token验证(会话中生成的随机串,提交后校验)

  • 在关键表单提交时要求用户进行二次身份验证(录入密码、插KEY、输入图片验证码、短信验证码)

  • 对请求referer做验证(比如跨域、系统内部应用)


文件上传安全

  • 上传操作应设计身份验证机制,并进行合法身份校验

  • 只允许上传满足业务需要的相关文档类型

  • 通过检查文件头信息,比如JPEG (jpg)文件头信息(十六进制):FFD8FF,验证上传文档是否是所期待的类型

  • 不要把文件保存在与应用程序相同的 Web 环境中,建议将文件保存在专用的文档服务器中,单独给文档服务器配置域名访问更好

  • 限制上传任意可能被 Web 服务器解析的文件 ,比如jsp、php等

  • 上传文件以二进制形式下载,建议不提供直接访问(防止木马文件直接执行)

  • 禁止授予上传文件存储目录的可执行权限

  • 禁止客户端自定义文件上传/下载路径(如:使用../../../../进行跳转)

  • 文件上传后重命名(需根据业务实际需求制定命名规则)


组件安全

  • 在使用随机数函数时,推荐使用强随机数函数(例如java.security.SecureRandom类)

  • 精简组件中不需要的功能、方法,以免带来未知的安全风险

  • 不可将系统内部使用的锁对象暴露给不可信代码

  • 建议使用SSL Socket代替Socket来进行安全数据交互

  • 封装本地方法调用(所有的本地方法都应该被定义为私有的,然后仅通过一个封装方法来调用)

  • 使用安全管理器(比如java.security或第三方安全组件)来保护敏感操作

  • 编写自定义类加载器必须覆盖getPermissions()函数时,在为代码源分配任意权限前,应调用超类super.getPermissions()函数,实现除了自定义策略外,系统全局的默认安全策略也被应用。

  • 避免完全依赖URLClassLoader和java.util.jar提供的默认自动签名认证机制,应从加载类的代码源(Code-Source)中获取证书链,然后检查证书是否属于本地密钥库(KeyStore)中的受信任签名者


接口安全

  • 调用方来源IP控制,比如可通过防火墙、主机host deny、Nginx deny等技术措施进行实施

  • 调用方身份认证,比如key、secret、证书等技术措施进行实施

  • 调用参数认证,需设计参数容错机制,避免出现参数可遍历敏感数据安全问题

  • 采用数字签名保障接口身份来源可信,数据防篡改

  • 调用方权限控制设置

  • 调用频率、有效期进行控制

  • 调用行为实时检测,对异常阻拦

  • 幂等性校验,保持数据一致性

  • 采用应用接入安全网关,实现APPID/KEY身份认证,加密传输,摘要签名安全保障


Dubbo调用安全

  • 采用token验证访问控制,防止消费者绕过注册中心访问提供者;在注册中心控制权限以决定要不要下发令牌给消费者

  • 采用filter IP白名单访问控制,同时也可预防生产系统和测试系统之间Dubbo混乱调用问题

  • 在必要情况下(如敏感信息操作),连接注册中心Dubbo时要进行用户名和密码校验


Redis调用安全

  • 应启用客户端IP访问控制验证功能

  • 应启用客户端身份验证功能

  • 敏感信息不要明文存储于Redis

11. SDL上线规定文档

1.1. 编制目的

为了提升公司各业务线产品的安全质量,降低安全风险,进一步提升公司整体安全水平,结合公司现有产品设计、开发、测试及上线流程,特制定此《XX产品安全开发流程》。

1.2. 适用范围

此流程适用于 XX 所有业务线产品的安全设计、开发、测试及上线。

2. 什么是安全开发流程(SDL)?

SDL 的全称是 Security Development Lifecycle,即:安全开发生命周期。它是由微软最早提出的,是一种帮助解决软件安全问题的方法。SDL 中的方法,试图从安全漏洞产生的根源上解决问题。SDL 是一个安全保证的过程,通过对软件工程的控制,从而保证产品的安全性。它能够帮助企业以最小的成本提高产品的安全性,对企业安全的发展来说,可以起到事半功倍的效果。

3. 实施 SDL 的目的

实施 SDL 的核心目的只有一个,那就是通过在开发过程中加入安全控制的方法,提升各业务线项目、系统的安全性。避免因项目上线后存在安全漏洞被攻击者恶意利用而造成不必要的安全风险和损失。

4. 实施 SDL 的好处

  • 提前主动发现安全漏洞,降低安全风险和漏洞修复成本
  • 在项目开发前期就引入安全控制的方法,构建标准化安全开发流程,从根源上减少安全漏洞和降低安全风险
  • 保证所有项目在上线前都通知到安全团队

5. 如何实施安全开发流程

5.1. XX SDL 整体流程

整体流程

5.2. 项目立项

要求:需要保证所有项目在立项时都通知到安全团队。
通知方式:邮件+企业微信、立项会议

5.3. 需求分析

要求:所有需求必须通过安全评审,由安全团队来评估需求是否存在安全风险或是否需要设计安全功能;如果安全评审未通过,不允许进入下一步研发流程

安全评审流程:

安全评审流程

安全评审成果:安全评审报告 安全评审沟通方式:邮件+企业微信、jira安全评审提交

5.4. 设计

在设计阶段,由产品和研发团队自查,确认是否有违反《XX开发安全红线准则》的行为。

5.5. 编码实现

在编码实现阶段,研发工程师应遵循 《XX安全开发手册》

5.6. 测试验证

在开发工作完成后,应与功能测试同步进行安全测试,安全测试过程中发现的安全漏洞通过“jira XX安全团队漏洞提交”管理。

安全测试需求提交方式:邮件+企业微信、项目安全提测系统

5.7. 业务验收

在业务验收时,应同步进行安全验收,完成后由安全团队发布《XX项目安全验收报告》,如 果在安全验收过程中发现存在未修复的高危及严重级别安全漏洞,不允许进入上线发布环节。

5.8. 上线与维护

  • 如果未收到“通过安全验收”的报告,不允许私自上线发布项目。
  • 在项目上线前,由安全团队添加相应必要的安全监控、定期漏洞扫描。

02.php安全编码规范

1.配置

php.ini基本安全配置


1.1应启用“cgi.force_redirect”

cgi.force_redirect在php.ini中修改,默认是开启的,它可以防止当PHP运行的CGI脚本未经验证的访问。在IIS,OmniHTTPD和Xitami上是禁用的,但在所有其他情况下它应该打开。

; php.ini
cgi.force_redirect=1 ; 

1.2应禁用“enable_dl”

该指令仅对Apache模块版本的PHP有效。你可以针对每个虚拟机或每个目录开启或关闭dl()动态加载PHP模块。关闭动态加载的主要原因是为了安全。通过动态加载,有可能忽略所有open_basedir限制。默认允许动态加载,除了使用安全模式。在安全模式,总是无法使用dl()。

; php.ini
enable_dl=0 ; 

1.3应禁用“file_uploads”

file_uploads默认是开启的,允许将文件上传到您的站点。因为来自陌生人的文件本质上是不可信甚至危险的,除非您的网站绝对需要,否则应禁用此功能。如果开启请进行相应的限制,参考upload_max_filesize, upload_tmp_dir,和post_max_size。

; php.ini
file_uploads = 0 ; 

1.4通过“open_basedir”限制文件访问权限

open_basedir默认是打开所有文件,它将 PHP 所能打开的文件限制在指定的目录树,包括文件本身。本指令不受安全模式打开或者关闭的影响。当一个脚本试图用例如 fopen() ,include或者 gzopen() 打开一个文件时,该文件的位置将被检查。当文件在指定的目录树之外时 PHP 将拒绝打开它。所有的符号连接都会被解析,所以不可能通过符号连接来避开此限制。

open_basedir应该配置一个目录,然后可以递归访问。但是,应该避免使用. (当前目录)作为open_basedir值,因为它在脚本执行期间动态解析特殊值 . 指明脚本的工作目录将被作为基准目录,但这有些危险,因为脚本的工作目录可以轻易被 chdir() 而改变。

在 httpd.conf 文件中,open_basedir 可以像其它任何配置选项一样用“php_admin_value open_basedir none”的方法关闭(例如某些虚拟主机中)。在 Windows 中,用分号分隔目录。在任何其它系统中用冒号分隔目录。作为 Apache 模块时,父目录中的 open_basedir 路径自动被继承。

用 open_basedir 指定的限制实际上是前缀,不是目录名。也就是说“open_basedir = /dir/incl”也会允许访问“/dir/include”和“/dir/incls”,如果它们存在的话。如果要将访问限制在仅为指定的目录,用斜线结束路径名。例如:“open_basedir = /dir/incl/”。

; php.ini
open_basedir="${USER}/scripts/data" ; 

1.5应禁用“session.use_trans_sid”

默认为 0(禁用)。当禁用cookie时,如果它开启,PHP会自动将用户的会话ID附加到URL。基于 URL 的会话管理比基于 cookie 的会话管理有更多安全风险,从表面上看,这似乎是让那些禁用cookie的用户正常使用您的网站的好方法。实际上,它使那些用户容易被任何人劫持他们的会话。例如用户有可能通过 email 将一个包含有效的会话 ID 的 URL 发给他的朋友,或者用户总是有可能在收藏夹中存有一个包含会话 ID 的 URL 来以同样的会话 ID 去访问站点。也可以从浏览器历史记录和服务器日志中检索URL获取会话ID。

; php.ini
session.use_trans_sid = 0 ; 

1.6会话管理cookie不能是持久的

没有固定生命周期或到期日期的Cookie被称为非持久性或“会话”cookie,这意味着它们只会持续与浏览器会话一样长,并且在浏览器关闭时会消失。具有到期日期的Cookie叫做“持久性”Cookie,他们将被存储/保留到这些生存日期。

管理网站上的登录会话应用非持久性cookie。要使cookie非持久化,只需省略该 expires属性即可。也可以使用session.cookie_lifetime实现。


1.7应禁用"allow_url_fopen"和"allow_url_include"

allow_url_fopen和allow_url_include默认是开启的,他们允许代码从URL中读入脚本。从站点外部吸入可执行代码的能力,加上不完美的输入清理可能会使站点裸露给攻击者。即使该站点的输入过滤在今天是完美的,但不能保证以后也是。

; php.ini
allow_url_fopen = 0
allow_url_include = 0

2.编码

php安全编码建议


2.1慎用sleep()函数

sleep()有时用于通过限制响应率来防止拒绝服务(DoS)攻击。但是因为它占用了一个线程,每个请求需要更长的时间来服务,这会使应用程序更容易受到DoS攻击,而不是减少风险。

if (is_bad_ip($requester)) {
  sleep(5);  // 不合规的用法
}

2.2禁止代码动态注入和执行

eval()函数是一种在运行时运行任意代码的方法。 函数eval()语言结构是非常危险的,因为它允许执行任意 PHP 代码。因此不鼓励使用它。如果您仔细的确认过,除了使用此结构以外别无方法,请多加注意,不要允许传入任何由用户提供的、未经完整验证过的数据。

eval($code_to_be_dynamically_executed)  // 不合规的用法

2.3禁止凭据硬编码

因为从编译的应用程序中提取字符串很容易,所以永远不应对凭证进行硬编码。对于分发的应用程序尤其如此。 凭据应存储在受强保护的加密配置文件或数据库中的代码之外。

// 合规的用法
$uname = getEncryptedUser();
$password = getEncryptedPass();
connect($uname, $password); 
// 不合规的用法
$uname = "steve";
$password = "blue";
connect($uname, $password);

2.4禁止危险函数

有时候,我们不希望执行包括system()等在那的能够执行命令的php函数,或者能够查看phpinfo信息的

phpinfo()等函数,那么我们就可以禁止它们:

disable_functions = system,passthru,exec,shell_exec,popen,phpinfo

如果你要禁止任何文件和目录的操作,那么可以关闭很多文件操作

disable_functions = chdir,chroot,dir,getcwd,opendir,readdir,scandir,fopen,unlink,delete,copy,mkdir,   rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown

以上只是部分常用的文件处理函数,你也可以把上面执行命令函数和这个函数结合,应该就能够抵制大部分的phpshell了。

03.java安全编码规范

1输入验证和数据合法性校验

程序接受数据可能来源于未经验证的用户,网络连接和其他不受信任的来源,如果未对程序接受数据进行校验,则可能会引发安全问题。

1.1避免SQL注入

使用PreparedStatement预编译SQL,解决SQL注入问题,传递给PreparedStatement对象的参数可以被强制进行类型转换,确保在插入或查询数据时与底层的数据库格式匹配。 

String sqlString = "select * from db_user where username=? and password=?";
PreparedStatement stmt = connection.prepareStatement(sqlString);
stmt.setString(1, username);
stmt.setString(2, pwd);
ResultSet rs = stmt.executeQuery();

1.2避免XML注入

通过StringBulider 或 StringBuffer 拼接XML文件时,需对输入数据进行合法性校验。 对数量quantity 进行合法性校验,控制只能传入0-9的数字:

if (!Pattern.matches("[0-9]+", quantity)) {
    // Format violation
  }
  String xmlString = "<item>\n<description>Widget</description>\n" +
                     "<price>500</price>\n" +
                     "<quantity>" + quantity + "</quantity></item>";
  outStream.write(xmlString.getBytes());
  outStream.flush();

1.3避免跨站点脚本(XSS)

对产生跨站的参数进行严格过滤,禁止传入<SCRIPT>标签

//定义需过滤的字段串<script>

String s = "\uFE64" + "script" + "\uFE65";

// 过滤字符串标准化

s = Normalizer.normalize(s, Form.NFKC);

// 使用正则表达式匹配inputStr是否存在<script>

Pattern pattern = Pattern.compile(inputStr);
Matcher matcher = pattern.matcher(s);
if (matcher.find()) {
  // Found black listed tag
  throw new IllegalStateException();
} else {
  // ...
}

2声明和初始化

2.1避免类初始化的相互依赖

例:

错误的写法:

public class Cycle {
  private final int balance;
  private static final Cycle c = new Cycle();
  private static final int deposit = (int) (Math.random() * 100); // Random deposit
  public Cycle() {
    balance = deposit - 10; // Subtract processing fee
  }
  public static void main(String[] args) {
    System.out.println("The account balance is: " + c.balance);
  }
}

类加载时初始化指向Cycle类的静态变量c,而类Cycle的无参构造方法又依赖静态变量deposit,导致无法预期的结果。 正确的写法:

public class Cycle {
  private final int balance;
  private static final int deposit = (int) (Math.random() * 100); // Random deposit
  private static final Cycle c = new Cycle();  // Inserted after initialization of required fields
  public Cycle() {
    balance = deposit - 10; // Subtract processing fee
  }

  public static void main(String[] args) {
    System.out.println("The account balance is: " + c.balance);
  }
}

3表达式

3.1不可忽略方法的返回值

忽略方法的放回值可能会导致无法预料的结果。

错误的写法:

public void deleteFile(){
  File someFile = new File("someFileName.txt");
   someFile.delete();
}

正确的写法:

public void deleteFile(){
  File someFile = new File("someFileName.txt");
   if (!someFile.delete()) {
    // handle failure to delete the file
  }
}

3.2不要引用空指针

当一个变量指向一个NULL值,使用这个变量的时候又没有检查,这时会导致。NullPointerException。

在使用变量前一定要做是否为NULL值的校验。

3.3使用Arrays.equals()来比较数组的内容

数组没有覆盖的Object. equals()方法,调用Object. equals()方法实际上是比较数组的引用,而不是他们的内容。程序必须使用两个参数Arrays.equals()方法来比较两个数组的内容

public void arrayEqualsExample() {
  int[] arr1 = new int[20]; // initialized to 0
  int[] arr2 = new int[20]; // initialized to 0
  Arrays.equals(arr1, arr2); // true
}

4数字类型和操作

4.1防止整数溢出

使用java.lang.Number. BigInteger类进行整数运算,防止整数溢出。

public class BigIntegerUtil {

    private static final BigInteger bigMaxInt = BigInteger.valueOf(Integer.MAX_VALUE);
    private static final BigInteger bigMinInt = BigInteger.valueOf(Integer.MIN_VALUE);

    public static BigInteger intRangeCheck(BigInteger val) throws ArithmeticException {
        if (val.compareTo(bigMaxInt) == 1 || val.compareTo(bigMinInt) == -1) {
            throw new ArithmeticException("Integer overflow");
        }
        return val;
    }

    public static int addInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.add(b2));
        return res.intValue(); 
    }

    public static int subInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.subtract(b2));
        return res.intValue(); 
    }

    public static int multiplyInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.multiply(b2));
        return res.intValue(); 
    }

    public static int divideInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.divide(b2));
        return res.intValue(); 
    }
}

4.2避免除法和取模运算分母为零

要避免因为分母为零而导致除法和取模运算出现异常。

if (num2 == 0) {
  // handle error
} else {
 result1= num1 /num2;
  result2= num1 % num2;
}

5类和方法操作

5.1数据成员声明为私有,提供可访问的包装方法

攻击者可以用意想不到的方式操纵public或protected的数据成员,所以需要将数据成员为private,对外提供可控的包装方法访问数据成员。

5.2敏感类不允许复制

包含私人的,机密或其他敏感数据的类是不允许被复制的,解决的方法有两种:

1、类声明为final

final class SensitiveClass {
  // ...
}

2、Clone 方法抛出CloneNotSupportedException异常

class SensitiveClass {
  // ...
  public final SensitiveClass clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
  }
}

5.3比较类的正确做法

如果由同一个类装载器装载,它们具有相同的完全限定名称,则它们是两个相同的类。 不正确写法:

// Determine whether object auth has required/expected class object
 if (auth.getClass().getName().equals(
      "com.application.auth.DefaultAuthenticationHandler")) {
   // ...
}
正确写法:
// Determine whether object auth has required/expected class name
 if (auth.getClass() == com.application.auth.DefaultAuthenticationHandler.class) {
   // ...
}

5.4不要硬编码敏感信息

硬编码的敏感信息,如密码,服务器IP地址和加密密钥,可能会泄露给攻击者。

敏感信息均必须存在在配置文件或数据库中。

5.5验证方法参数

验证方法的参数,可确保操作方法的参数产生有效的结果。不验证方法的参数可能会导致不正确的计算,运行时异常,违反类的不变量,对象的状态不一致。 对于跨信任边界接收参数的方法,必须进行参数合法性校验

private Object myState = null;
//对于修改myState 方法的入参,进行非空和合法性校验
void setState(Object state) {
  if (state == null) {
    // Handle null state
  }
  if (isInvalidState(state)) {
    // Handle invalid state
  }
  myState = state;
}

5.6不要使用过时、陈旧或低效的方法

在程序代码中使用过时的、陈旧的或低效的类或方法可能会导致错误的行为。

5.7数组引用问题

某个方法返回一个对敏感对象的内部数组的引用,假定该方法的调用程序不改变这些对象。即使数组对象本身是不可改变的,也可以在数组对象以外操作数组的内容,这种操作将反映在返回该数组的对象中。如果该方法返回可改变的对象,外部实体可以改变在那个类中声明的 public 变量,这种改变将反映在实际对象中。

不正确的写法:

public class XXX {
    private String[] xxxx;
    public String[] getXXX() {
            return xxxx;
    }
}

正确的写法:

public class XXX {
    private String[] xxxx;
    public String[] getXXX() {
            String temp[] = Arrays.copyof(…);  // 或其他数组复制方法
            return temp;
    }
}

5.8不要产生内存泄露

垃圾收集器只收集不可达的对象,因此,存在未使用的可到达的对象,仍然表示内存管理不善。过度的内存泄漏可能会导致内存耗尽,拒绝服务(DoS)。


6异常处理

6.1不要忽略捕获的异常

对于捕获的异常要进行相应的处理,不能忽略已捕获的异常

不正确写法:

class Foo implements Runnable {
  public void run() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      // 此处InterruptedException被忽略
    }
  }
}

正确写法:

class Foo implements Runnable {
  public void run() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    }
  }
}

6.2不允许暴露异常的敏感信息

没有过滤敏感信息的异常堆栈往往会导致信息泄漏,

不正确的写法:

try {
  FileInputStream fis =
      new FileInputStream(System.getenv("APPDATA") + args[0]);
} catch (FileNotFoundException e) {
  // Log the exception
  throw new IOException("Unable to retrieve file", e);
}

正确的写法:

class ExceptionExample {
  public static void main(String[] args) {
    File file = null;
    try {
      file = new File(System.getenv("APPDATA") +
             args[0]).getCanonicalFile();
      if (!file.getPath().startsWith("c:\\homepath")) {
        log.error("Invalid file");
        return;
      }
    } catch (IOException x) {
     log.error("Invalid file");
      return;
    }
    try {
      FileInputStream fis = new FileInputStream(file);
    } catch (FileNotFoundException x) {
      log.error("Invalid file");
      return;
    }
  }
}

6.3不允许抛出RuntimeException, Exception,Throwable

不正确的写法:

boolean isCapitalized(String s) {
  if (s == null) {
    throw new RuntimeException("Null String");
  }
}

private void doSomething() throws Exception {
  //...
}

正确写法:

boolean isCapitalized(String s) {
  if (s == null) {
    throw new NullPointerException();
  }
}

private void doSomething() throws IOException {
  //...
}

6.4不要捕获NullPointerException或其他父类异常

不正确的写法:

boolean isName(String s) {
  try {
    String names[] = s.split(" ");
    if (names.length != 2) {
      return false;
    }
    return (isCapitalized(names[0]) && isCapitalized(names[1]));
  } catch (NullPointerException e) {
    return false;
  }
}

正确的写法:

boolean isName(String s) /* throws NullPointerException */ {
  String names[] = s.split(" ");
  if (names.length != 2) {
    return false;
  }
  return (isCapitalized(names[0]) && isCapitalized(names[1]));
}

7多线程编程

7.1确保共享变量的可见性

对于共享变量,要确保一个线程对它的改动对其他线程是可见的。 线程可能会看到一个陈旧的共享变量的值。为了共享变量是最新的,可以将变量声明为volatile或同步读取和写入操作。 将共享变量声明为volatile

final class ControlledStop implements Runnable {
  private volatile boolean done = false;
  @Override public void run() {
    while (!done) {
      try {
        // ...
        Thread.currentThread().sleep(1000); // Do something
      } catch(InterruptedException ie) { 
        Thread.currentThread().interrupt(); // Reset interrupted status
      } 
    }    
  }
  public void shutdown() {
    done = true;
  }
}

同步读取和写入操作:

final class ControlledStop implements Runnable {
  private boolean done = false;
  @Override public void run() {
    while (!isDone()) {
      try {
        // ...
        Thread.currentThread().sleep(1000); // Do something
      } catch(InterruptedException ie) { 
        Thread.currentThread().interrupt(); // Reset interrupted status
      } 
    }    
  }
  public synchronized boolean isDone() {
    return done;
  }
  public synchronized void shutdown() {
    done = true;
  }
}

7.2确保共享变量的操作是原子的

除了要确保共享变量的更新对其他线程可见的,还需要确保对共享变量的操作是原子的,这时将共享变量声明为volatile往往是不够的。需要使用同步机制或Lock 同步读取和写入操作:

final class Flag {
  private volatile boolean flag = true;
  public synchronized void toggle() {
    flag ^= true; // Same as flag = !flag;
  }
  public boolean getFlag() {
    return flag;
  }
}

//使用读取锁确保读取和写入操作的原子性

final class Flag {
  private boolean flag = true;
  private final ReadWriteLock lock = new ReentrantReadWriteLock();
  private final Lock readLock = lock.readLock();
  private final Lock writeLock = lock.writeLock();
  public void toggle() {
    writeLock.lock();
    try {
      flag ^= true; // Same as flag = !flag;
    } finally {
      writeLock.unlock();
    }
  }
  public boolean getFlag() {
    readLock.lock();
    try {
      return flag;
    } finally {
      readLock.unlock();
    }
  }
}

7.3不要调用Thread.run(),不要使用Thread.stop()以终止线程

7.4确保执行阻塞操作的线程可以终止

  public final class SocketReader implements Runnable {
  private final SocketChannel sc;
  private final Object lock = new Object();
  public SocketReader(String host, int port) throws IOException {
    sc = SocketChannel.open(new InetSocketAddress(host, port));
  }
  @Override public void run() {
    ByteBuffer buf = ByteBuffer.allocate(1024);
    try {
      synchronized (lock) {
        while (!Thread.interrupted()) {
          sc.read(buf);
          // ...
        }
      }
    } catch (IOException ie) {
      // Forward to handler
    }
  }
  public static void main(String[] args) 
                          throws IOException, InterruptedException {
    SocketReader reader = new SocketReader("somehost", 25);
    Thread thread = new Thread(reader);
    thread.start();
    Thread.sleep(1000);
    thread.interrupt();
  }
}

7.5相互依存的任务不要在一个有限的线程池执行

有限线程池指定可以同时执行在线程池中的线程数量的上限。程序不得使用有限线程池线程执行相互依赖的任务。可能会导致线程饥饿死锁,所有的线程池执行的任务正在等待一个可用的线程中执行一个内部队列阻塞


8输入输出

8.1程序终止前删除临时文件

8.2检测和处理文件相关的错误

Java的文件操作方法往往有一个返回值,而不是抛出一个异常,表示失败。因此,忽略返回值文件操作的程序,往往无法检测到这些操作是否失败。Java程序必须检查执行文件I / O方法的返回值。

不正确的写法:

File file = new File(args[0]);
file.delete();
正确的写法:
File file = new File("file");
if (!file.delete()) {
  log.error("Deletion failed");
}

8.3及时释放资源

垃圾收集器无法释放非内存资源,如打开的文件描述符与数据库的连接。因此,不释放资源,可能导致资源耗尽攻击。

try {
  final FileInputStream stream = new FileInputStream(fileName);
  try {
    final BufferedReader bufRead =
        new BufferedReader(new InputStreamReader(stream));

    String line;
    while ((line = bufRead.readLine()) != null) {
      sendLine(line);
    }
  } finally {
    if (stream != null) {
      try {
        stream.close();
      } catch (IOException e) {
        // forward to handler
      }
    }
  }
} catch (IOException e) {
  // forward to handler
}

9序列化

9.1不要序列化未加密的敏感数据

序列化允许一个对象的状态被保存为一个字节序列,然后重新在稍后的时间恢复,它没有提供任何机制来保护序列化的数据。敏感的数据不应该被序列化的例子包括加密密钥,数字证书。 解决方法:

  1. 对于数据成员可以使用transient ,声明该数据成员是瞬态的。
  2. 重写序列化相关方法writeObject、readObject、readObjectNoData,防止被子类恶意重写
class SensitiveClass extends Number {
  // ...
  protected final Object writeObject(java.io.ObjectOutputStream out) throws NotSerializableException {
    throw new NotSerializableException();
  }
  protected final Object readObject(java.io.ObjectInputStream in) throws NotSerializableException {
    throw new NotSerializableException();
  }
  protected final Object readObjectNoData(java.io.ObjectInputStream in) throws NotSerializableException {
    throw new NotSerializableException();
  }
}

9.2在序列化过程中避免内存和资源泄漏

不正确的写法:

class SensorData implements Serializable {
  // 1 MB of data per instance!
   public static SensorData readSensorData() {...}
  public static boolean isAvailable() {...}
}
class SerializeSensorData {
  public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(
          new BufferedOutputStream(new FileOutputStream("ser.dat")));
      while (SensorData.isAvailable()) {
        // note that each SensorData object is 1 MB in size
        SensorData sd = SensorData.readSensorData();
        out.writeObject(sd);
      }
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }
}

正确写法:

class SerializeSensorData {
  public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(
          new BufferedOutputStream(new FileOutputStream("ser.dat")));
      while (SensorData.isAvailable()) {
        // note that each SensorData object is 1 MB in size
        SensorData sd = SensorData.readSensorData();
        out.writeObject(sd);
        out.reset(); // reset the stream
      }
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }
}

9.3反序列化要在程序最小权限的安全环境中

(完成)

04.python安全编码规范

python语言安全

本身要注意的有,一些危险函数,危险模块的调用,主要是系统调用。这个如果调用一定要对输入输出做好过滤,以下是代码中各种导致进行系统调用的方式。尽量避免。

  • 避免各种情况导致系统调用

  • 谨慎使用Eval

  • 数据序列化

Web编程

对应Web编程中安全概念在python web框架中的实现。url跳转,目录遍历,任意文件读取也需要考虑在内。针对不同的框架也需要。

Flask 安全

  • 使用Flask-Security
  • 直接生成 HTML 而不通过使用Jinja2
  • 不要在用户提交的数据上调用Markup
  • 使用 Content-Disposition: attachment 标头去避免上传html文件
  • 防止CSRF,flask本身没有实现该功能

Django 安全

可参考phithon的博客,有较多相关资料。

  • 关闭DEBUG模式
  • 关闭swagger调试
  • 妥善保存SECRET_KEY
  • 使用SecurityMiddleware
  • 设置SECURE_HSTS_SECONDS开启HSTS头,强制HTTPS访问
  • 设置SECURE_CONTENT_TYPE_NOSNIFF输出nosniff头,防止类型混淆类漏洞
  • 设置SECURE_BROWSER_XSS_FILTER输出x-xss-protection头,让浏览器强制开启XSS过滤
  • 设置SECURE_SSL_REDIRECT让HTTP的请求强制跳转到HTTPS
  • 设置SESSION_COOKIE_SECURE使Cookie为Secure,不允许在HTTP中传输
  • 设置CSRF_COOKIE_SECURE使CSRF Token Cookie设置为Secure,不允许在HTTP中传输
  • 设置CSRF_COOKIE_HTTPONLY为HTTP ONLY
  • 设置X_FRAME_OPTIONS返回X-FRAME-OPTIONS: DENY头,以防止被其他页面作为框架加载导致ClickJacking
  • 部署前运行安全性检测 django-admin.py checksecure --settings=production_settings

审计工具

安装使用方式较为简单,所以不做介绍。 - AST-based static Analyzer: Bandit - Static Analyzer: PYT

引用

05.nodejs安全编码规范

待定

06.移动安全规范

1 移动安全入门

移动安全带来了许多Web安全的挑战 - 广泛的受众,快速开发和持续的网络连接 - 加上更多传统胖客户端应用程序(例如缓冲区管理,本地加密和恶意软件)的常见风险。 移动环境的一个独特功能是来自未知开发人员的已安装应用程序的流行,这些应用程序应被视为“不受信任的”。

1.1 移动应用的被攻击面

如下所示,移动攻击可以涉及设备层,网络层,数据中心或这些的组合。 固有平台漏洞和社会工程继续为网络盗贼提供重要机会,从而为那些寻求保护用户数据的人带来重大挑战。

攻击方向

在移动技术链中有三个点,恶意方可能利用漏洞来发起恶意攻击:

  • 设备
  • 网络
  • 数据

1.2 设备

移动设备对敏感的公司信息(SCI)构成重大风险; 主要风险包括数据丢失和安全性受损。 无论是iPhone,Android或其他智能手机,针对设备本身的攻击者可以使用各种入口点:

  • 浏览器, 邮件, 或其他预加载数据的应用
  • 电话/短信
  • 第三方应用 (apps)
  • 操作系统
  • 射频(例如基带), 蓝牙或其他通信通道

基于浏览器的攻击

基于浏览器的攻击点可以包括:

网络钓鱼 – 涉及通过电子邮件欺骗来伪装为受信任实体来获取个人信息,例如用户名,密码和信用卡详细信息。 研究表明,移动用户比网络用户提交个人信息的可能性是钓鱼网站的三倍。 这在一定程度上可能是由于移动浏览器运行的缩小的环境,由于有限的屏幕不动产,有限的警告对话,缩小的安全锁图标,并且放弃了许多用户界面指示器,所述环境只显示URL的一小部分 例如大的STOP图标,突出显示的地址栏和其他可视指示器。

框架 - 构架涉及在iFrame中传送Web / WAP站点,这可以使“包装器”站点执行点击劫持攻击。

点击劫持 – 也称为UI修复,点击劫持涉及欺骗用户揭露机密信息或当用户点击看起来无害的链接或按钮时控制他们的设备。 此攻击采用嵌入式代码或脚本的形式,无需用户知识。 点击劫持已被利用在包括Facebook的网站窃取信息或直接用户攻击网站。

下载驱动攻击 – Android尤其容易受到这种攻击,其中网站访问导致发生没有用户知识的下载,或通过诱骗用户具有欺骗性的提示。 下载可以是恶意应用,并且用户然后可以由设备自动提示以安装应用。 当Android设备设置为允许来自“未知来源”的应用时,允许安装。

Man-in-the-Mobile (MitMo) – 允许恶意用户利用放置在移动设备上的恶意软件绕过通过短信向用户的手机发送代码以进行身份确认的密码验证系统。

基于电话或短信的攻击

电话/短信攻击点可以包括:

基带攻击 – 利用在手机的GSM / 3GPP基带处理器中发现的漏洞的攻击,GSM / 3GPP基带处理器是向小区基站发送和接收无线电信号的硬件。

短信钓鱼 – 类似于钓鱼,但使用手机短信代替电子邮件,以提示用户访问非法网站并输入敏感信息,如用户名,密码和信用卡号。

射频攻击 – 蓝牙,NFC攻击和其他RF攻击在各种外围通信信道上发现通常在附近的设备到设备通信中使用的漏洞。

基于应用程序的攻击

基于应用的攻击点可以包括:

敏感数据存储 – 2011年viaForensics研究发现83%的流行应用程序采样存储数据不安全。

无加密或弱加密 – 允许传输未加密或弱加密数据的应用程序容易受到攻击。

SSL验证不正确 – 应用程序的安全套接字层(SSL)验证过程中的错误可能会允许数据安全漏洞。

配置操作 – 包括未经授权访问管理界面,配置存储和检索明文配置数据。

动态运行时注入 – 允许攻击者操纵和滥用应用程序的运行时,绕过安全锁,绕过逻辑检查,访问应用程序的特权部分,甚至窃取存储在内存中的数据。

非必要权限 – 错误配置的应用有时可能会通过授予非必要的权限来打开攻击者的大门。

提升的权限 – 利用一个错误,设计缺陷或配置监督,以获得通常受应用程序或用户保护的资源。

基于操作系统的攻击

基于操作系统的攻击点可以包括:

没有密码 – 许多用户选择不设置密码,或使用弱PIN,密码或模式锁定。

iOS 越狱 – “越狱”是用于消除制造商和运营商提出的防止未经授权的代码在设备上运行的安全机制的术语。 一旦这些限制被删除,设备可以成为恶意软件和其他攻击的网关。

Android rooting – 类似于越狱,rooting允许Android用户更改或替换系统应用程序和设置,运行需要管理员级权限的专门应用程序。 像越狱,它可能导致敏感数据的暴露。

密码和数据的可访问性 – 诸如Apple的iOS设备系列的设备在其用于存储加密密码和数据的加密机制中已经存在漏洞。 具有这些漏洞知识的攻击者可以解密设备的钥匙串,暴露用户密码,加密密钥和其他私人数据。

运营商预安装软件 – 预安装在设备上的软件可能包含安全漏洞。 最近,在Android手机上的一些预加载应用程序被发现包含安全漏洞,可用于擦除手机,窃取数据,甚至窃听电话。

零日漏洞 – 攻击经常发生在漏洞被首次利用的时间和软件开发人员能够发布解决问题的版本之间的窗口期间。

1.3 网络

基于网络的攻击点可以包括:

Wi-Fi (弱加密/无加密) – 当在Wi-Fi网络上使用无法实施加密的应用程序时,存在恶意攻击者窃听无线连接而窃取数据的风险。 许多应用程序使用SSL / TLS,这提供了一定程度的保护; 然而一些针对SSL / TLS的攻击也被证明可以将关键用户数据暴露给攻击者。

恶意接入点 – 涉及物理安装授权各方访问安全网络的未授权无线接入点。

数据包嗅探 – 允许恶意入侵者捕获和分析网络流量,这通常包括以明文传输的用户名和密码信息。

中间人攻击 (MITM) – 涉及窃听现有网络连接,侵入该连接,拦截消息和修改选择数据。

SSLStrip – 一种中间人攻击的形式,利用了网站上的SSL / TLS实现的弱点,这可以依赖于用户验证HTTPS连接是否存在。 攻击将连接降级到HTTP,无需加密,用户很难在移动浏览器中检测到。

会话劫持 – 涉及会话密钥的利用以获得对用户和网络信息的未授权访问。

DNS 劫持 – 利用网络DNS可以将网站的用户引导到攻击者选择的另一个网站。 在某些情况下,攻击还可以通过应用程序注入内容。

假SSL证书 – 另一个中间人攻击涉及发出假SSL证书,允许恶意用户拦截假定安全的HTTPS连接上的流量。

1.4 数据

针对数据中心的攻击者使用两个主要入口点:

  • Web server
  • 数据库

基于web server的攻击

基于Web服务器的攻击和漏洞包括:

平台漏洞 – 操作系统,服务器软件或Web服务器上运行的应用程序模块的漏洞可能被攻击者利用。 有时可以通过监视移动设备和web服务器之间的通信来发现脆弱性,以发现协议或访问控制中的弱点。

服务器错误配置 – 配置不良的Web服务器可能允许对通常受保护的资源的未授权访问。

跨站点脚本 (XSS) – 跨站点脚本是一种涉及将恶意JavaScript代码注入网站的攻击。 容易受到此类攻击的页面会将用户输入返回浏览器,而不会正确地对其进行整理。 此攻击通常用于在用户访问页面时自动运行代码,从而控制用户的浏览器。 在已经建立对浏览器的控制之后,攻击者可以将该控制用于各种攻击,诸如内容注入或恶意软件传播。

跨站点请求伪造 (CSRF) – 跨站点请求伪造涉及攻击者根据特定Web应用程序的功能知识创建HTTP(Web)请求,并诱骗用户或浏览器提交这些请求。 如果Web应用程序易受攻击,攻击可以执行似乎来自用户的事务或提交。 CSRF通常在攻击者已经通过XSS,社交工程或其他方法获得对用户会话的控制之后使用。

弱输入验证 – 许多Web服务过度信任来自移动应用程序的输入,依靠应用程序来验证最终用户提供的数据。 但是,攻击者可以伪造自己与Web服务器的通信,或完全绕过应用程序的逻辑检查,允许他们利用服务器上缺少的验证逻辑来执行未经授权的操作。

暴力攻击 – 暴力攻击只是试图猜测对字段的有效输入,通常使用高速率的尝试和可能值的字典。 暴力攻击的最常见用法是身份验证,但也可用于在Web应用程序中发现其他有效值。

基于数据库的攻击

数据库攻击和漏洞包括:

SQL 注入 – 没有正确验证用户输入的接口可能导致SQL被注入到其他无害的应用程序查询中,导致数据库暴露或以其他方式操纵通常应该限制用户或应用程序的数据。

执行操作系统级命令 – 与SQL注入类似,某些数据库系统提供了执行操作系统级命令的方法。 攻击者可以将这些命令插入到查询中,使数据库在服务器上执行这些命令,从而为攻击者提供额外的权限,直至并包括根级别系统访问。

权限提升 – 当攻击利用一些漏洞获得更大的访问时,会发生这种情况。 在数据库上,这可能导致敏感数据被盗。 数据转储 - 攻击者导致数据库转储数据库中的一些或所有数据,暴露敏感记录。

1.5 移动应用的类型

移动应用通常分为三个操作类别:

Web – 通过通用网络浏览器操作的应用。 有时被称为WAP或移动网站,这些是移动等同于在过去十年中激增的功能性网络应用程序,提供许多功能,如网上银行和购物。 虽然常规网站可用于移动网络浏览器,但许多公司现在创建单独的移动网络应用以优化移动属性,例如更小的屏幕尺寸,基于触摸的导航和GPS位置的可用性。

Native – 安装的应用程序,其操作本机移动设备操作系统,为特定的移动平台编译并利用其API。 这些通常(但不总是)通过应用程序市场下载和安装。

Wrapper – 通过在专用本机应用程序包装器(有时也称为“shell应用程序”或“混合应用程序”)中利用网页操作的应用程序。虽然对最终用户显示为本机应用程序,但基于Web的功能可能导致不同的漏洞 比在完全本地编码的应用程序中发现。

2. 编码实践

2.1 增加代码复杂性和使用混淆

详细描述

反向工程应用程序可以提供有价值的洞察您的应用程序的工作原理。 使您的应用程序在内部更复杂,攻击者更难以看到应用程序如何操作,这可以减少攻击面的数量。

建议

反向工程一个Android应用程序(.apk文件)是很容易实现的,然后可以检查应用程序的内部工作。 混淆代码,以使恶意用户更难以检查应用程序的内部工作,如下面链接到Android开发人员#### 参考文章中所述。

此外,由于iOS应用程序的设计方式,它们容易受到逆向工程攻击。 应用程序的类和协议存储在对象文件中,允许攻击者完全映射应用程序的设计。 Objective-C本身是一种反射语言,能够感知和修改自己的状态; 具有正确工具的攻击者可以以与运行时管理应用程序相同的方式感知和修改应用程序的状态。 Objective-C包含一个简单的消息框架,它非常容易跟踪,并且可以被操纵来拦截甚至篡改应用程序的运行时。 可以使用相对简单的攻击来操纵Objective-C运行时绕过身份验证和策略检查,内部应用程序健全检查或警告应用程序策略的那种逻辑检查。

如果应用程序处理高度敏感的数据,请考虑实施反调试技术。 存在可以增加逆向工程代码的复杂性的各种技术。 一种技术是使用C / C ++来限制攻击者轻松地运行操作。 有丰富的C和C ++库,它们非常成熟,并且易于与Objective-C集成,并且Android提供了JNI(Java Native Interface)。 在iOS上,考虑在低级C中编写代码的关键部分,以避免Objective-C运行时或Objective-C逆向工程工具(如class-dump,class-dump-z,Cycript或Frida)的暴露和操作。

限制调试器 – 应用程序可以使用特定的系统调用来指定,以防止操作系统允许调试器附加到进程。 通过防止调试器attach到进程,攻击者干扰低级运行时的能力受到限制。 攻击者必须首先规避调试限制,以便在低级别上攻击应用程序。 这增加了攻击的进一步复杂性。 Android应用程序应该在应用程序清单中设置“android:debuggable =”false“”,以防止攻击者或恶意软件轻松运行运行时操作。 在iOS上,您可以使用PT_DENY_ATTACH

跟踪检查 – 应用程序可以确定其当前是否由调试器或其他调试工具跟踪。 如果正在被跟踪,则应用可以执行任何数量的响应动作,例如,丢弃加密密钥以保护用户数据,通知服务器管理员或其他这样的响应以试图保护自身。 可以通过检查进程状态标志或使用其他技术(例如比较“ptrace attach”的返回值,检查父进程,将进程列表中的调试器列入黑名单或比较程序不同部分的时间戳)来检测调试器跟踪。

优化 - 为了隐藏高级数学计算和其他类型的复杂逻辑,利用编译器优化可以帮助混淆目标代码,使其不容易被攻击者反汇编。 这使得攻击者更难以获得对特定代码的理解。 在Android中,通过使用本机编译库和本机开发工具包(NDK)可以更容易地实现。 此外,使用LLVM Obfuscator或任何保护程序SDK将提供更好的机器代码混淆。

Stripping binaries – 清除原生二进制文件是增加攻击者所需的时间和技能以便查看应用程序低级功能的有效方法。 通过剥离二进制,二进制的符号表被剥离,以便攻击者不能轻易地调试或反向工程应用程序。 剥离二进制文件不会丢弃iOS上的Objective-C类或对象映射数据。 在Android上,您可以重用在gNU / Linux系统上使用的技术,例如sstrip或使用UPX。

分布在App Store中的iOS应用程序中的二进制文件被加密,增加了另一层的复杂性。 虽然存在从这些二进制文件中剥离FairPlay数字版权管理(DRM)加密的工具,但这一层DRM增加了攻击二进制所需的时间和熟练程度。 然而,App Store应用程序中使用的加密可能会被熟练的攻击者剥离。 攻击者通过转储在运行时从设备的内存中直接加载应用程序的内存来实现这一点。

参考

Android

iOS

Other / Multi

CWE/OWASP

2.2 避免简单逻辑

详细描述

代码中的简单逻辑测试更容易受到攻击。 例如:

if sessionIsTrusted == 1

这是一个简单的逻辑测试,如果攻击者可以改变这个值,他们可以绕过安全控制。 苹果iOS被攻击使用这种类型的弱点和Android应用程序已经他们的Dalvik二进制补丁绕过各种保护机制。 这些逻辑测试很容易在许多级别上回避。 在组装级别上,攻击者可以仅使用调试器来攻击iOS应用程序,以找到正确的CBZ(零比较和分支)或CBNZ(比较和非零分支)指令并将其反转。 这可以在运行时执行,只需遍历对象的内存地址,并在应用程序运行时更改其实例变量。 在Android上,应用程序可以反编译为SMALI,并在重新编译之前修补分支条件。

建议

考虑一个更好的编程范例,当会话不受信任时,由服务器强制执行权限,或者通过防止某些数据被解密或以其他方式可用,直到应用程序可以使用质询/响应,OTP或其他 形式的认证。 此外,#### 建议将所有健全检查功能声明为静态内联。 使用这种方法,它们被内联编译,使得更难以修补(即攻击者不能简单地重写一个函数或修补一个函数)。 这种技术将需要攻击者从应用程序中寻找并修补检查的每个实例,增加了攻击所需的复杂性。 对于高度敏感的应用程序,建立在安全编码原则中的更复杂的方法可能值得进一步调查。 集成技术(如加密,定时回调和基于流的编程)可能增加攻击者的复杂性。

同样,存储在对象中的简单逻辑变量可以容易地被攻击者操纵。 例如:

session.trusted = TRUE

这样的值可以由应用程序当前使用的类的实例内的攻击者读取和写入。 在iOS上,通过操作Objective-C运行时,可以操纵这些变量,以便下次应用程序引用这些变量时,将读取任何操作的值。

CWE/OWASP

2.3 测试第三方库

详细描述

开发人员非常依赖第三方库。 在测试代码时,彻底探查和测试这一点很重要。 第三方库可以包含漏洞和弱点。 许多开发人员认为第三方库已经完善并经过测试,然而,问题可能而且确实存在于他们的代码中。

建议

安全审计必须彻底测试第三方库和功能。 这应该包括核心的iOS和Android代码/库。 升级到新版本的第三方库(或操作系统版本)应视为您的应用程序的一部分。 更新的第三方库(或新操作系统版本)可能包含新的漏洞或暴露您的代码中的问题。 应该测试它们,就像为应用程序测试新代码一样。 在iOS上,静态编译第三方库以避免LD_PRELOAD攻击; 在这种攻击中,一个库及其功能可以被替换为具有被恶意代码替换的攻击者库。

CWE/OWASP

2.4 实施防篡改技术

详细描述

攻击者可以在应用上篡改或安装后门,重新签名并将恶意版本发布到第三方应用市场。 这种攻击通常针对流行的应用程序和金融应用程序。

建议

采用防篡改和篡改检测技术来防止非法应用程序执行。

使用校验和,数字签名和其他验证机制来帮助检测文件篡改。 当攻击者试图操纵应用程序时,不会保留正确的校验和,并且这可以检测和防止非法执行。 注意,这样的技术不是万无一失的,并且可以被充分动机的攻击者绕过。 校验和,数字签名和其他验证技术增加了攻击者必须花费的时间和精力才能成功地破坏应用程序。 应用程序可以在检测到篡改的情况下以静默方式擦除其用户数据,密钥或其他重要数据,以进一步挑战攻击者。 检测到篡改的应用程序还可以通知管理员。

在Android上,用于签名应用程序的公钥可以从应用程序的证书中读取,并用于验证应用程序是否已使用开发人员的私钥签名。 使用PackageManager类,可以检索应用程序的签名,然后将它们与正确的值进行比较。 如果有人篡改或重新签名了应用程序,比较将失败,导致检测到篡改应用程序。

参考

CWE/OWASP

2.5 在内存中安全的存储敏感数据

通常,iOS开发人员会将应用程序设置存储在plist文件中,在某些情况下这可能会受到影响。

详细描述

当应用程序正在使用时,用户或应用程序特定的数据可以存储在RAM中,并且在用户注销或会话超时时不会正确清除。 因为Android将应用程序存储在内存中(即使在使用后),直到内存被回收,加密密钥可能会保留在内存中。 发现或窃取设备的攻击者可以附加调试器并从应用程序转储内存,或者加载内核模块以转储内存中的全部内容。

当管理密码和其他敏感信息时,应用程序会将该信息保存在内存中,即使缓冲区释放了一段时间。 如果应用程序容易出现缓冲区溢出,格式化字符串,数据泄露和其他漏洞,这可能是一个安全问题,这可能允许攻击者转储进程的内存以恢复敏感信息。

建议

不要将内存中保存敏感数据(例如加密密钥)长于所需的时间。 清除使用后保存密钥的任何变量。 避免对敏感的密钥或密码使用不可变对象,如在Androidjava.lang.String中,而是使用char数组。 即使对不可变对象的引用被删除或清空,它们可能保留在内存中,直到垃圾回收发生(这不能被应用程序强制)。

这只能通过低级语言完成,因为如果优化例程检测到缓冲区在覆盖后不再使用,编译器和即时虚拟机将会因性能原因而忽略这些操作。

有一些工具可以来绕过编译器优化以清除这些缓冲区,但他们都依赖工具链,特定的语言和平台。

CWE/OWASP

2.6 安全的删除数据

详细描述

在Android上,调用file.delete()将不会安全地擦除目标文件,并且只要它没有被覆盖,它可以从设备的物理图像刻出。 由于对NAND闪存的积极管理,擦除文件的传统方法通常不在移动设备上工作。

建议

在假设任何写入设备的数据都可以恢复的情况下操作。 在某些情况下,加密可能会增加额外的保护层。

对于大多数应用程序不推荐使用以下方法,但可能删除文件并用大文件覆盖所有可用空间(这将迫使NAND闪存擦除所有未分配的空间)。 这种技术的缺点包括耗尽NAND闪存,导致应用和整个设备响应缓慢,并显着的功耗。

尽可能避免在设备上存储敏感数据。

加密存储在文件中的敏感数据,在删除之前重写文件的内容和同步可以帮助,但是如上所述,它们不是完全可靠的解决方案。

参考

CWE/OWASP

2.7 避免敏感数据的查询字符串

详细描述

一个主要的突破口是执行一个简单的经过修改过的查询字符串。查询字符串参数是可见的,并且可能经常意外地缓存(从网络历史记录,Web服务器或代理日志等)。应该避免使用未加密的有意义的数据作为查询字符串。 如果用户凭证作为查询字符串参数传输,而不是在POST请求的正文中传输,那么这些凭证可能会被记录在各种地方 - 例如,在用户的浏览器历史记录中,在Web服务器日志中,以及在日志中或者在基础设施服务商采用的任何反向代理。 如果攻击者成功地获得这些资源中的任何一个,则他可以通过捕获存储在那里的用户凭证来提升权限。

建议

使用安全的具有XSRF令牌保护的POST方法来发送用户数据。 在可以找到查询字符串数据的区域中,默认情况下不会记录POST数据。 无论是POST还是GET,都应使用临时会话Cookie。 使用非零初始化向量和临时会话密钥加密数据也可以帮助防止重放攻击。 如果需要,可以使用在主机之间使用安全算法(例如Diffie-Hellman)协商的临时会话密钥来加密查询字符串数据。

参考

Pinto, Marcus (2007). The Web Application Hacker’s Handbook: Discovering and Exploiting Security Flaws (Kindle Locations 2813-2816). Wiley. Kindle Edition.

CWE/OWASP

3 敏感数据处理

3.1 实现安全数据存储

详细信息

在移动设备上安全地存储数据需要适当的技术。 只要有可能“简单地不要存储或缓存数据” 是避免数据在设备上被获取最直接的方式。

建议

尽量不要存储敏感数据。 减少用户信息存储的方法包括:

  • 仅仅传输和显示,但不持久化存储中。 这还需要特别注意防止敏感数据的屏幕截图被写入磁盘。
  • 仅存储在内存中(在应用程序关闭时清除)。

附加分层加密

如果在设备上存储敏感数据是应用程序需求,则应向数据添加额外的经过验证的第三方加密(例如,SQLCipher)。

通过添加另一层加密,您可以更好地控制实施和攻击,主要集中在主要的操作系统加密类。 例如,对iOS数据保护类(现在已被入侵)的攻击将无法直接危害您的应用程序。 这种方法具有更复杂的缺点,并且如果实施得不好,实际上会减少安全系数。 如果您不确定是否包括经过验证的第三方加密库,Apple和Android的通用加密库提供了许多标准加密功能,如果使用得当,可以提供相当安全的加密实现。

下面是一些方法:

  • 使用SQLCipher在SQLite数据库中加密敏感值,使用PRAGMA key加密整个数据库
  • PRAGMA key可以在用户初次安装应用程序或第一次启动时在运行时生成
  • 为每个用户和设备生成唯一的PRAGMA密钥
  • 密钥生成的源应当具有足够的熵(即,避免从诸如用户名的易于预测的数据生成密钥材料)

每当您加密用户数据时,旨在使用随机生成的主密钥对其进行加密,该主密钥在用户访问数据时也使用用户提供的密码加密。 这将防止数据被容易地恢复,如果攻击者从设备中提取主密钥。 由于Apple的数据保护API和钥匙串中的漏洞数量以及大多数Android手机上缺少设备加密,因此不#### 建议将主密钥或密码存储在设备上。

Android

在Android中,记住外部存储设备(如SD卡)没有细粒度的权限,任何应用程序默认情况下具有对存储的读访问权限,可以读取所有文件。 从Android 4.4开始应用程序可以在某些情况下以受保护的方式在SD卡上存储数据 (#### 参考 http://source.android.com/devices/tech/storage/).

Android和iOS实现标准加密库,例如AES,可用于保护数据。 请注意被此方法加密的数据安全依赖于用于导出密钥和密钥管理的密码安全。 需要考虑密码策略,长度和复杂性与用户方便性以及加密密钥如何存储在内存中。 而且root权限可以转储正在运行的进程的内存,并搜索它的加密密钥。

还要注意,使用标准加密提供程序“AES”通常将默认为较不安全的AES-ECB。 最佳做法是使用256位密钥和SecureRandom生成的随机IV指定AES-CBC或AES-GCM。 您还应该使用经过良好测试的PBKDF2(基于密码的密钥导出函数)来从密码导出密钥。

iOS

iOS中内置的数据保护API与复杂的密码短语相结合,可以提供额外的数据保护层,但不像实施其他第三方验证的加密技术那么安全。 为了利用这一点,文件必须被专门标记。(#### 参考最佳实践 6.1 谨慎使用keychain). 任何没有专门使用apple 的文件保护api加密的数据都是未加密存储的

参考

CWE/OWASP

详细描述

如果Cookie未标记为“Secure”,则无论与主机的会话是否安全,都可能通过不安全的连接进行传输。 换句话说,它可以通过HTTP连接来传输。

此外,由于Cookie无法通过客户端访问(例如,无法使用JavaScript代码段访问),因此在Cookie上设置“HTTPOnly”标志可防止跨站点脚本(XSS)等攻击。

建议

Set-Cookie头应该使用“Secure”和“HTTPOnly”设置。 这些设置应适用于本机和/或网络应用程序的所有Cookie。

CWE/OWASP

3.3 完全验证SSL / TLS

许多应用程序没有正确验证SSL / TLS证书,使他们容易受到中间人(MITM)攻击。 如果应用程序无法正确验证其与服务器的连接,则该应用程序容易受到特权网络攻击者的MITM攻击。 这种类型的攻击使罪犯能够捕获,查看和修改在应用程序和服务器之间发送和接收的流量。

详细描述

未正确验证其与服务器的连接的应用程序容易受到拥有网络管理权限的攻击者的中间人攻击。 这意味着攻击者将能够捕获,查看和修改在应用程序和服务器之间发送和接收的流量。

常见错误:接受自签名证书

开发人员可能会因为各种原因而停用应用中的证书验证。 一个例子是开发人员需要在生产服务器上测试代码,但没有测试环境的域证书。 在这种情况下,开发人员可以向网络库添加代码以接受所有有效的证书。 然而,接受所有证书为有效的,允许攻击者通过简单地使用自签名证书对应用程序执行MITM攻击。 这种开发应用程序的方法使SSL / TLS的效果无效,并且相对于未加密的纯文本连接(除了需要主动MITM攻击来查看和修改流量,而纯文本连接可以被动监控之外)没有实质上的安全提升。

以下是接受所有SSL / TLS证书的有效Android代码示例:

    TrustManager[] trustAllCerts = new TrustManager[] {
       new X509TrustManager() {
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
          }
          public void checkClientTrusted(X509Certificate[] certs, String authType) {  }

          public void checkServerTrusted(X509Certificate[] certs, String authType) {  }

       }
    };

    //Globally set the broken TrustManager
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    //Make the connection to the server
    URL url = new URL("https://paypal.com");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    InputStream ins = urlConnection.getInputStream();
    InputStreamReader isr = new InputStreamReader(ins);
    BufferedReader in = new BufferedReader(isr);

    String inputLine;
    in.close();

常见错误:设置允许的主机名验证器

在实施SSL / TLS时,另一个常见的开发人员错误是设置一个允许所有主机名的主机名验证器(hostname verifier)。 在这种情况下,应用程序将不接受自签名证书,证书仍然需要被验证。 但是如果应用程序“允许所有主机名”,任何有效的证书颁发机构(CA)为任何域名颁发的证书都可以被用来进行中间人攻击(MITM)和签名网络数据。

下面是一个易受攻击的Android代码示例,它设置了一个允许所有主机名的主机名验证器:

    URL url = new URL("https://paypal.com");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    InputStream ins = urlConnection.getInputStream();
    InputStreamReader isr = new InputStreamReader(ins);
    BufferedReader in = new BufferedReader(isr);

    String inputLine;
    in.close();

建议

一般建议

对于处理高度敏感数据的任何应用程序,请使用证书锁定以防止MITM攻击。 大多数应用程序都定义了它们连接的位置(它们的后端服务器),并固有地信任它们连接的基础设施,因此使用“私有”公钥基础结构(与公共证书分开)是可以接受的(通常更安全) 当局。 使用这种方法,攻击者需要来自服务器端的私钥对他们没有物理访问的设备执行MITM攻击。

如果无法对处理高度敏感数据的任何应用程序功能实施证书锁定,请实施正确的证书验证,该证书验证由两部分组成:

  1. Certificate validation: 呈现给应用程序的证书必须由应用程序完全验证,并由受信任的根CA进行签名。
  2. Hostname validation: 应用程序必须检查并验证从证书提取的主机名(公用名或CN)与应用程序要与之通信的主机的主机名相匹配。

Android建议

将证书固定到Android随附的默认Apache HTTP客户端包括获取所需主机的证书,转换.bks格式的证书,然后将证书固定到DefaultHttpClient实例。 BKS密钥库通常包含在应用程序的APK文件的assets / raw目录中。

以下示例代码演示了如何加载BKS密钥库:

    InputStream in = resources.openRawResource(certificateRawResource);

    keyStore = KeyStore.getInstance("BKS");
    keyStore.load(resourceStream, password);

httpClient实例可以被配置为仅允许接受存在于应用程序内存储的证书签名的证书。

以下示例代码说明了此方法:

    HttpParams httpParams = new BasicHttpParams();

    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("https", new SSLSocketFactory(keyStore), 443));

    ThreadSafeClientConnManager clientMan = new ThreadSafeClientConnManager(httpParams, schemeRegistry);

    httpClient = new DefaultHttpClient(clientMan, httpParams);

有关在Android中实现证书锁定的更多信息,请参阅OWASP Certificate and Public Key Pinning guide

此外,CWAC-NetSecurity 是一个将Android 7.0网络安全配置子系统返回到API级别17(Android 4.2)的库。它使得将应用程序绑定到特定的证书颁发机构或证书更容易。支持自签名证书,以及处理其他高级SSL证书方案。

For iOS

一个方案是使用NSURLSessionAFNetworking类在iOS中实现证书锁定。 此实现的其他详细信息可以在apple developer网站上HTTPS Server Trust Evaluation中找到。

此外,一个开源框架的TrustKit可以帮助开发人员更容易地在iOS中部署公钥锁定。

参考

CWE/OWASP

3.4 防止SSL降级攻击

详细描述

使用这种形式的中间人攻击,攻击者可以通过透明地劫持网络上的HTTP流量,监控HTTPS请求,然后消除SSL / TLS来绕过SSL / TLS,从而在客户端和服务器之间创建不安全的连接。 这种攻击在移动网络应用上尤其难以预防(移动网络应用本质上是一个看起来像应用的网页)。

建议

通过TLS提供所有流量,甚至非敏感流量。 这防止了任何可能的降级/剥离攻击,因为攻击者需要初始明文“入口点”来完成所述攻击。

验证SSL / TLS是否处于活动状态。 在完全本机应用程序中验证SSL / TLS是相对简单的。 移动网络应用可以通过JavaScript验证SSL / TLS,以便如果未检测到HTTPS连接,则客户端将重定向到HTTPS。 要求SSL / TLS的更可靠的手段是HTTP严格传输安全(HSTS)头。 HSTS标头强制与该域的所有后续连接使用TLS和原始证书。 浏览器只是开始实现HSTS头而移动浏览器总是滞后。

在应用程序中使用图标或语言,以确保用户在所述连接不依赖于经过验证的HTTPS会话时知道并进行安全连接。 教育用户是降低SSL / TLS降级攻击风险的重要组成部分。 在应用程式内使用弹出提示和文字提示,强化使用者使用HTTPS保护网路流量的重要性。

最近在Android和iOS中实施的另一种缓解措施是将非TLS /纯文本流量视为开发人员错误。Android最近添加 android:usesCleartextTraffic (Android M and the War on Cleartext Traffic,而iOS 9及更高版本要求您手动添加明文流量的例外。 替换Web协议HTTP / 2是另一个未来的解决方案,因为它仅使用TLS(并且包括其他功能).

参考

CWE/OWASP

3.5 限制使用UUID

详细描述

大多数移动设备具有在制造时为了识别目的而分配的唯一ID(也称为通用唯一标识符(UUID))。 例如,iOS设备分配了所谓的唯一设备标识符(UDID)。 唯一标识设备的能力对于获取,管理和保护数据通常很重要。 开发人员迅速采用UUID和UDID进行设备识别,从而使其成为许多系统的安全基础。

不幸的是,这种方法带来了几个隐私和安全问题。 首先,许多在线系统已将设备的UUID连接到单个用户,以便即使在用户未登录到应用时也能够跨应用进行跟踪。 这种跟踪用户的高级功能已经成为主要的隐私问题。

除此之外,通过UUID识别人员的应用程序有风险暴露设备的以前的所有者的数据到新的所有者。 在一个实例中,在重新设置iPhone之后,即使所有用户数据已被擦除,我们也可以访问先前用户的在线音乐服务的帐户。 这不仅是一个隐私问题,它的安全威胁,因为攻击者可以伪造一个UUID。

苹果已经认识到iOS的UDID的隐私和安全风险,并删除了开发者对它的访问。 在UDID不可用的情况下,一些开发人员应用涉及无线网络接口或OpenUDID的MAC地址的其他设备识别方法。 这些方法现在已在系统/ API级别被禁止,并且作为AppStore审查过程的一部分被标记和拒绝。

建议

我们#### 建议开发人员避免使用任何设备提供的标识符来标识设备,特别是如果它是实施设备身份验证的一部分。 相反,我们#### 建议在注册,安装或首次执行时创建应用程序唯一的“设备因子”。 然后可能需要将此应用程序唯一的设备因子与用户身份验证结合,以创建会话。 设备因子也可以用作加密例程中的附加因子。

由于它不依赖于可预测的,设备提供的数据,因此开发变得更加困难。 通过利用挑战 - 响应方法,服务器和设备可以在用户认证之前彼此认证。 要获得系统访问,攻击者必须利用这两个因素。 开发人员还可以实现在客户端或服务器端重置设备因素的功能,从而强制对用户和设备进行更严格的重新身份验证。

为了在保留广告功能的同时保护用户隐私,Apple#### 建议使用advertisingIdentifier - 在系统中所有应用程序之间共享的唯一标识符。用户可以随时在设置 - >隐私 - >广告菜单中重置其设备上的advertisingIdentifier。

参考

CWE/OWASP

3.6 谨慎处理地理位置数据

详细描述

Android和iOS可以使用GPS准确地确定位置。 处理此GPS数据是一个隐私问题,如果攻击者知道其当前或过去的位置,可能会使用户容易受到其他攻击。 本地蓝牙、NFC 、RFID标签的信息也可能泄漏地理位置信息。

此外,访问图库图片的应用程序也可以是用户的隐私问题,它可以通过检查时间戳并且假设图片是用户自己拍摄的图片来抓取存储在其中的GPS位置(如果有的话)。

建议

考虑使用和避免存储GPS数据的影响。 为了更好的隐私,尽量使用最粗粒度的位置服务。 除非必要,不要记录或存储GPS信息。 虽然在某些应用中使用GPS可能很有用,但很少需要记录和存储数据。 避免这种情况防止了许多隐私和安全问题。 GPS定位信息通常在iOS上的locationd缓存和Android上的各种缓存之间缓存一段时间。 一些应用程序自动使用GPS。 一个例子是通常对图像进行地理标记的相机。 如果这是一个问题,请确保从图像中剥离EXIF数据。

在需要安全的地点工作时,请记住,GPS数据可能会报告给Apple和Google服务器,以提高准确性。 Android和iOS设备都能捕获范围内附近接入点的信息,无论设备是否连接到它们。 不要在将在安全位置或其附近运行的应用程序中激活GPS,其坐标或无线网络拓扑不应报告给供应商。 除此之外,攻击者可以使用单个接入点的硬件地址的知识来模拟安全无线环境并从苹果或Google返回环境的GPS坐标。

参考

CWE/OWASP

3.7 本地会话超时机制

详细描述

移动设备经常丢失或被盗,攻击者可以利用活动的会话来访问敏感数据,执行事务或在设备所有者的帐户上执行侦察。 此外,如果没有适当的会话超时,应用可能容易受到中间人攻击的数据拦截。

建议

任何时间应用程序不使用超过5分钟,终止活动会话,将用户重定向到登录屏幕,确保没有应用程序数据可见,并要求用户重新输入登录凭据以访问 应用程序。

超时后,还要丢弃和清除与用户数据相关联的所有内存,包括用于解密数据的任何主密钥

此外,确保客户端和服务器端都发生会话超时,以减轻攻击者修改本地超时机制。

CWE/OWASP

3.8 使用增强/双因素身份验证

详细描述

弱或不存在的身份验证可以授予攻击者对应用程序的未经授权的访问权限。

建议

密码不应该是简单的。 最好要求(如果不是至少支持)复杂密码,包括至少六个字母数字字符的长度(更多字符总是更强)。 作为登录过程的一部分,要求选择一个秘密词或图标(不是用户自己创建的)可以帮助保护用户的帐户,以防他们重复使用密码,并防止密码被从其他数据猜出。

在某些情况下,用户名和密码不能为移动应用程序提供足够的安全性。 当涉及敏感数据或事务时,实施双因素身份验证。 在用户每次登录时都进行二次身份验证可能不大合适,但可以按时间间隔或在访问所选功能时使用。 可以考虑逐步认证方法以提供对非事务性区域的正常访问,但需要针对敏感功能进行第二层认证。

增强认证的选项包括:

  • 附加秘密词/图标
  • 通过短信或电子邮件提供的附加代码 - 但请注意,攻击者可能会在被盗设备上访问这两者
  • 密码加上其他用户已知的值,例如用户选择的个人因素
  • 安全问题和答案,由用户预先选择(例如在注册期间)

对于最高级别的安全性,使用一次性密码,要求用户不仅拥有正确的凭据,而且还要包括一次性密码的物理令牌。

CWE/OWASP

3.9 保护应用程序设置

详细描述

iOS开发人员经常将应用程序设置存储在plist文件中,在某些情况下可能会受到损害。 类似地,Android开发人员通常将设置存储在共享首选项XML文件或SQLite数据库中,这些数据库在默认情况下未加密,可以使用root权限读取或甚至修改,或使用备份。

建议

尽可能将设置编译为代码。 通过plist文件在iOS上配置应用程序没有什么好处,因为更改必须绑定并部署为新的应用程序。 相反,如果将配置包含在应用程序代码中,攻击者需要更多的时间和技能才能修改。 除非先加密不要在字典或其他文件中存储任何关键设置。 理想情况下,使用由用户提供的密码加密的主密钥或用户登录系统时远程提供的密钥加密所有配置文件。

CWE/OWASP

3.10 隐藏帐号并使用令牌

详细信息

许多app在各种UI显示中存储完整的帐号。

建议

鉴于移动应用在公共场所的广泛使用,显示部分号码(例如***9881)可以帮助确保此信息的最大隐私。 除非需要在设备上存储完整的数字,否则存储部分隐藏的数字。 通常,帐号用于查询服务器端帐户数据; 该数据可以容易地从存储器中被盗取,或者在某些情况下被操纵以处理用户不应该具有访问权限的帐户。 #### 建议将令牌(token)分配给每个帐户,并提供给客户端,而不是帐号。 这些令牌(不应由用户推断)具有到实际帐户的服务器端映射。 如果应用程序数据被盗,用户的帐号不会被暴露,攻击者必须首先确定映射回帐户的令牌,而不能直接查询帐号。

在iOS中,如果你意识到

> - (BOOL)textField:(UITextField *)textField 

> shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

作为文本字段的委托的一部分,您可以更改输入文本的可见性。

3.11 使用安全的网络传输方法传输敏感数据

详细描述

与网络浏览器不同,移动设备通常不会公开应用程序是否使用SSL / TLS来保护数据传输,因此应用程序用户只能相信应用程序的开发人员已实施网络加密。

多年来,SSL(后继者是TLS)一直是Web通信加密的标准,包括为移动应用程序提供支持的Web服务。 然而,违反认证机构如DigiNotar和Comodo暴露了许多用户伪造证书。 苹果 “goto fail” 错误进一步暴露了SSL / TLS的可靠性依赖于应用程序开发人员。

今天,最佳实践要求应用提供商有效地使用SSL / TLS来保护通过网络传输密码,登录ID和其他敏感数据,甚至进一步利用应用层加密来保护用户数据。

建议

使用SSL / TLS与标准信任验证,或为了增加安全性,实施证书锁定(#### 参考 OWASP “Pinning Cheat Sheet”).

为了防止通过受损的SSL / TLS连接拦截高度敏感的值(例如登录ID,密码,PIN,帐号等),请在传输过程中增加额外的加密。 使用密钥大小256为AES(也称为Rijndael)加密高度敏感的值。而对于hash,请使用诸如SHA-256或更高版本的算法。

在服务器端,请考虑仅接受强TLS密码和密钥,并禁用较低级别的加密,例如导出级40位加密

CWE/OWASP

3.12 验证来自客户端的输入

详细描述

即使数据是从您的应用程序生成的,这些数据也可能被拦截和操纵。 这可能包括导致应用程序崩溃(生成关键崩溃日志),缓冲区溢出,SQL注入和其他攻击的攻击。 通过实现UITextFieldDelegate中的方法并利用上面的#### 建议,这可以很容易地在iOS中执行。

建议

与正确的Web应用程序安全性一样,客户端的所有输入都必须被视为不受信任。 服务必须彻底过滤和验证应用程序和用户的输入。 适当的清理包括在发送之前和接收期间的所有用户输入。

References

CWE/OWASP

3.13 避免将应用程序数据存储在备份中

详细描述

在Android或iOS设备上执行数据备份还可以备份存储在应用程序的私人目录中的敏感信息。

建议

Android

默认情况下,Android应用程序的Manifest文件中的allowBackup的值为true。 这将产生一个Android备份文件(backup.ab),包括设备文件系统上的应用程序私有目录中包含的所有子目录和文件。 因此,显式声明allowBackup标志为false

iOS

在对安装了特定应用程序的设备执行iTunes备份时,备份将包括设备文件系统上包含在该应用程序的私人目录中的所有子目录(“Caches”子目录除外)和文件。 因此,请避免将任何敏感数据以明文存储在应用程序的私有目录或子目录中的任何文件或文件夹中。

CWE/OWASP

4 缓存与日志

4.1 避免缓存应用程序数据

详细描述

数据可以在很多地方被捕获,尽管许多是无意的。 开发人员经常忽略一些可以存储数据的方式,包括日志/调试文件,cookie,网络历史记录,网页缓存,属性列表,文件和SQLite数据库。 在移动设备上安全地存储数据需要适当的技术。 只要有可能“简单地不要存储或缓存数据” 是避免数据在设备上被获取最直接的方式。

建议

阻止HTTP高速缓存。 开发人员可以将iOS和Android配置为不缓存网络数据,特别是HTTPS流量。 在iOS中,查看实现NSURLConnection委托并禁用newCachedResponse。 此外,我们#### 建议采取步骤,以避免缓存任何Web进程(如注册)的URL历史记录和页面数据。 HTTP缓存头在此重要,并在Web服务器上配置。 HTTP协议在响应标头中支持“no-store”指令,指示浏览器它不得存储响应或引发它的请求的任何部分。 对于Web应用程序,HTML表单输入可以使用autocomplete = off设置指示浏览器不缓存值。 应用程序启动后,可以通过对设备数据进行读取检查来验证是否已经缓存了数据。

参考

CWE/OWASP

4.2 避免崩溃日志

详细描述

有很多用于跟踪用户使用情况并收集iOS和Android的崩溃日志的framework,这些framework都是开发的有用工具,但重要的是要在开发人员的足够调试信息和攻击者的信息减少之间找到平衡。

如果应用程序崩溃,生成的日志可以为攻击者提供有价值的信息。

建议

确保已发布的应用程序构建时没有任何警告,并经过彻底测试,以避免崩溃。 这肯定总是目标,值得一提的是由于崩溃日志的价值。 考虑禁用iOS的NSAssert。 如果断言失败,此设置将导致应用程序立即崩溃。 它更加优雅地处理失败的断言比崩溃和生成崩溃日志。 此外,避免通过网络以纯文本发送崩溃日志。

使用安全的开发工具,如clang-analyzer,coverity,ASAN和其他linting实用程序,以确定所有可能的操作,可以使应用程序崩溃或错误功能。

此外,如果应用程序被混淆和剥离,开发人员将需要保留一个地址到符号数据库,以便在崩溃日志中恢复有意义的回溯,使攻击者的生活更加困难,因为在函数中缺乏可理解的名称。

CWE/OWASP

4.3 限制缓存用户名

详细描述

在iOS上,当用户启用“保存此用户ID”功能时,用户名将缓存在CredentialsManager对象中。 在运行时,用户名在任何类型的身份验证之前加载到内存中,从而允许恶意进程拦截用户名。

建议

很难同时向用户提供保存的用户名的便利,以及避免通过不安全的存储或在运行时的潜在拦截来泄露用户名。 虽然不像密码那么敏感,但是用户名是应该保护的私人数据。 提供具有更高安全性的高速缓存用户名选项的一种潜在方法是存储掩蔽的用户名而不是实际的用户名,并且在认证中将用户名值替换为哈希值。 可以创建哈希值,包括在注册时存储的唯一设备令牌。 使用散列和设备令牌的进程的好处是,实际用户名不存储在本地或不加保护地加载到内存中,并且复制到另一个设备或在网络上使用的值将不够。 恶意用户必须发现更多信息才能成功窃取认证用户名。

参考

CWE/OWASP

4.4 谨慎管理调试日志

详细描述

调试日志通常设计为用于检测和纠正应用程序中的缺陷。 这些日志可能泄漏敏感信息,这可能有助于攻击者创建更强大的攻击。

建议

开发人员应考虑调试日志在生产环境中可能出现的风险。 一般来说,我们#### 建议他们在生产中禁用。

通常由应用程序用于输出调试消息的Android系统日志是存储在存储器中的几千字节的循环缓冲器。 在内核崩溃的情况下,还可以从文件系统恢复调试日志。 在设备重新启动时,它被清除,但在此之前,具有READ_LOGS权限的任何Android应用程序都可以查询日志。 在最新版本的Android中,日志文件已被更仔细地隔离,并且不需要请求系统级权限。

在Android中,还可以利用ProGuard或DexGuard完全删除发布版本中Log类的方法调用,从而取消对Log.d,Log.i,Log.v,Log.e方法的所有调用。

在 proguard.cfg 中,添加以下代码段:

> -assumenosideeffects class android.util.Log { 
        > public static *** d(...);
        > public static *** v(...);
        > public static *** i(...);
        > public static *** e(...);
> }

在iOS上禁用NSLog语句将删除可能被拦截的潜在敏感信息,因为额外的好处可能会稍微提高应用程序的性能。 例如,一种方法是在生产构建中定义NSLog:

> #define NSLog(s,...)

This macro effectively removes all NSLog statements and replaces it with empty text at compilation time.

> NSLog(@”Breakpoint here with data %@”,data.description);

becomes effectively a no-op.

>   ;

CWE/OWASP

4.5 注意键盘缓存

详细描述

iOS记录用户键入的内容,以便提供自定义自动更正和表单完成等功能,但敏感数据也可以存储。 几乎每个非数字字按照键入的顺序缓存在键盘缓存中; 缓存的内容超出了应用程序的管理权限,因此应用程序无法从缓存中删除数据。

建议

对任何敏感信息(而不仅仅是密码字段)禁用自动更正功能。 由于键盘缓存敏感信息,它可以恢复。 对于UITextField,查看将autocorrectionType属性设置为UITextAutocorrectionTypeNo以禁用缓存。 随着SDK更新,这些设置可能会随时间而变化,以确保其得到充分研究。 添加企业策略以定期清除键盘字典。 这可以由最终用户通过简单地去设置应用程序,通用>重置>重置键盘字典。

Android包含用户字典,用户输入的字词可以保存以供将来自动更正。 此用户字典可用于没有特殊权限的任何应用程序。 为了提高安全性,请考虑实施自定义键盘(以及可能的PIN输入),这可以禁用缓存,并提供针对恶意软件的额外保护。

CWE/OWASP

4.6 注意复制和粘贴

详细描述

iOS和Android都支持复制/粘贴。 敏感数据可以以明文形式存储,恢复或者可以从剪贴板修改,而不管数据的源是否最初被加密。 如果在用户复制它时它是纯文本,当其他应用程序访问剪贴板时,它将是明文。

例如,它遵循严格的规则,这意味着应用程序不能读取或写入剪贴板,并且使用它的唯一方式是通过用户交互,做长按来弹出剪贴板菜单。

建议

在适当情况下,禁用用于处理敏感数据的区域的复制/粘贴。 取消复制选项有助于避免数据暴露。 在Android上,剪贴板可以由任何应用程序访问,因此#### 建议使用经过适当配置的内容提供程序来传输复杂的敏感数据。 在iOS上,考虑用户是否需要在应用程序或系统范围内复制/粘贴数据,并选择适当类型的粘贴板。

此外,值得注意的是在取得内容后清除剪贴板,以避免其他应用程序读取它们并泄漏用户正在做什么。

CWE/OWASP

5 webviews

5.1 防止frame劫持和点击劫持

详细描述

Frame劫持涉及在iFrame中传送Web / WAP站点。 这种攻击可以使“包装”站点执行点击劫持攻击。 点击劫持是一个非常真实的威胁,已被利用高信息服务(例如Facebook)窃取信息或重定向用户到攻击者控制的网站。

Frame劫持的主要目的是诱骗用户点击不同的东西,他们的意图。 目标是通过链接漏洞(如跨站脚本)收集机密信息或控制受影响的计算机。 这种攻击通常采用嵌入在源代码中的脚本的形式,该脚本在用户不知道的情况下执行。 当用户单击看起来执行其他功能的按钮时,可以触发。

建议

在iOS中防止这种做法的最好方法是不使用WebViews。 非常非常小心的使用

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script

(click here for more info on the NSString Class Reference).

防止Frame劫持的一种机制利用客户端JavaScript。 大多数网站已经不能离开JavaScript,因此在JavaScript中实现安全措施(和禁用没有它的网站)是一个方案。 虽然客户端不是不能被篡改,但是这方案确实提高了攻击者的标准。 下面是一个JavaScript代码示例,它强制网站到“顶部”框架,从而“破坏”已加载网站的框架。

攻击者可以添加一些额外的代码来防止Frame劫持被破坏,例如在Frame unload时提醒用户不要退出。 更复杂的JavaScript可能能够对抗这样的技术。 但是包含Frame劫持破坏代码使得简单的Frame劫持变得困难。

X-FRAME-OPTIONS HEADER– 最近在一些浏览器中基于响应中的HTTP头实现了一种新的更好的反frame 劫持方案。 通过在Webserver级别配置此HTTP头,指示浏览器不在Frame或iFrame中显示响应内容。在代码示例中提供了Apache配置文件中的示例实现。

专门为WebView设计的API可能被滥用来危害WebView中指定的Web内容的安全性。 保护应用程序及其用户免受这个众所周知的漏洞的最佳方法是:

  • 防止X-Frame-Option HTTP响应头加载请求其他域名托管的内容的框架。 但是,这种方案不适用于处理受影响的主机。

  • 利用内部防御机制,确保所有UI元素在顶层框架中加载; 这样就避免了在较低的层次,通过不信任的Frame展示内容。

参考

基本Frame破解javascript:

if( self != top ) { 
  top.location = self.location ;
}

服务器端Apache配置文件的反iframe方案:

Header add X-FRAME-OPTIONS "DENY"

另一个选项是将此值设置为“SAMEORIGIN”,它将只允许来自相同域的Frame。 此标题已在各种浏览器上测试,包括iOS 4上的Safari,并确认可防止在iFrame中显示页面。 如果在iFrame中没有传送要求,#### 建议使用DENY。

CWE/OWASP

5.2 使用表单令牌保护CSRF

详细描述

CSRF (Cross-site Request Forgery) 依赖于已知或可预测的表单值和登录的浏览器会话。

建议

每个表单提交应包含一个令牌,该令牌在表单中或在用户会话的开头载入。 在接收POST请求时,在服务器上检查此令牌,以确保是用户启动它。 此功能已经被主流Web平台提供,仅需要少量定制表单开发。

参考

CWE/OWASP

6 IOS

6.1 谨慎使用keychain

详细描述

iOS提供了安全数据存储的钥匙串。 然而,在几种情况下,钥匙串可能被泄密并随后被解密。

在iOS的所有版本,包括iOS 7,如果攻击者可以访问加密的iTunes备份,钥匙串可能部分破解。 由于iOS在创建iTunes备份时重新加密钥匙串条目的机制,所以当iTunes备份可用且备份加密的密码已知时,钥匙串可以被部分解密。 但是,未加密的iTunes备份不允许解密钥匙串项。

如果越狱已应用于设备,则钥匙串访问控制将无效。 在越狱的情况下,在设备上运行的任何应用程序都可能读取每个其他应用程序的钥匙串项目。

最后,对于存在BootROM漏洞的旧设备(例如,iPhone 4),该钥匙串可能被具有对该设备的物理访问的攻击者获取。

建议

当在钥匙串中存储数据时,使用最严格的保护类(由kSecAttrAccessible属性定义)仍然允许您的应用程序正常工作。 例如,如果您的应用程序不是设计为在后台运行,请使用kSecAttrAccessibleWhenUnlockedkSecAttrAccessibleWhenUnlockedThisDeviceOnly

要防止通过iTunes备份暴露钥匙串项目,请在实用的情况下使用ThisDeviceOnly 选项。

最后,对于高度敏感的数据,考虑使用应用程序级加密来增强钥匙串提供的保护。 例如,依靠用户输入密码在应用程序内进行认证,然后使用该密码在将数据存储到密钥链之前对数据进行加密。

参考

CWE/OWASP

6.2 避免缓存应用程序快照

详细描述

为了提供界面中的视觉过渡,iOS会将屏幕截图作为图像存储在设备NAND闪存的文件系统部分中。 当应用程序暂停(而不是终止)时,当按下Home按钮或电话呼叫或其他事件暂时挂起应用程序时,会发生这种情况。 这些图像通常可以包含用户和应用程序数据。 在一个已发布的案例中,它们包含用户的全名,DOB,地址,雇主和信用评分。

建议

要保护敏感数据,请使用API配置或代码阻止应用程序快照的缓存。

当applicationDidEnterBackground:方法运行结束时,iOS将采用应用程序用户界面的快照,并且它用于转换动画并存储在文件系统中。 应该重载此方法,并且在返回之前应删除用户界面中的所有敏感信息。 这样快照就不会包含它们。

参考

CWE/OWASP

6.3 保护针对缓冲区溢出的攻击

详细描述

这个最佳实践涵盖了三个iOS代码实现,帮助开发人员减轻缓冲区溢出攻击他们的应用程序的风险:自动引用计数(ARC),地址空间布局随机化(ASLR)和堆栈崩溃保护。

自动引用计数(ARC)

自动引用计数(ARC)是一种内存管理系统,在编译时自动处理对象的引用计数,而不是将此任务留给开发人员。 此功能是与iOS 5一起引入的,但它可以反向运行到以前的版本,因为操作是在编译时执行的。

  • 编译器将自动插入释放和保留调用,使开发人员的生活更轻松,并降低引入与对象的内存生命周期相关的漏洞的风险。
  • 因为该过程在编译时发生,所以它不引入任何运行时开销,例如与垃圾收集器不同。 因此,切换到ARC没有明显的缺点。

地址空间布局随机化(ASLR)

ASLR(地址空间布局随机化)是iOS 4.3中引入的一种安全功能,它随机化应用在内存中的加载和维护。 ASLR对应用程序中使用的地址空间进行随机化,使得在没有首先导致应用程序崩溃的情况下执行恶意代码变得很困难。 它还使转储分配的应用程序的内存的过程变得复杂。 此测试检查应用程序二进制文件是否使用-PIE(位置无关可执行文件)标志进行编译。

堆栈崩溃保护

当应用程序使用堆栈崩溃保护编译时,已知的值或“canary”被放置在堆栈上直接在局部变量之前,以保护保存的基指针,保存的指令指针和函数参数。 在函数返回时检查"canary"的值,以查看它是否已被覆盖。 编译器使用启发式方法来智能地将堆栈崩溃保护应用于函数(通常是使用字符数组的函数)。

建议

启用 ARC - 在Xcode项目中启用ARC,或者使用Xcode中的重构工具将现有项目迁移到ARC。

ASLR - 编译支持PIE的应用程序。 当通过命令行使用选项-PIE(在iOS 4.3或更高版本)上编译时,可以启用PIE。

堆栈崩溃保护 - 使用-fstack-protector-all编译器标志编译应用程序,以保护应用程序免受缓冲区溢出攻击。

参考

  • Transitioning to ARC Release Notes - https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

  • Address Space Layout Randomization - https://developer.apple.com/library/prerelease/content/documentation/Security/Conceptual/SecureCodingGuide/Articles/BufferOverflows.html#//apple_ref/doc/uid/TP40002577-SW22

  • Other Compiler Flags That Affect Security - https://developer.apple.com/library/content/documentation/Security/Conceptual/SecureCodingGuide/Articles/BufferOverflows.html#//apple_ref/doc/uid/TP40002577-SW26

CWE/OWASP

6.4 避免缓存HTTP(S)请求/响应

详细描述

默认情况下,iOS的NSURLRequest将响应缓存在Cache.db文件中。 为了防止这种不安全的行为,开发人员必须明确禁用缓存。

建议

开发人员可以设置NSURLRequestcachePolicy属性来禁用HTTP(S)请求和响应的缓存。 禁用缓存的许多方法之一如下面的代码片段所示 (从Stack Overflow转载 NSURLConnection Delegate Returns Null :

 (NSCachedURLResponse)connection:(NSURLConnection)connection
            willCacheResponse:(NSCachedURLResponse *)cachedResponse {
        return nil;
        }

开发人员还可以在下面引用的Apple Developer文章“Understanding cache access”中找到禁用缓存HTTP(S)请求和响应的其他方法。

参考

  • Understanding cache access - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/Concepts/CachePolicies.html

CWE/OWASP

6.5 应用ATS(App Transport Security)

详细描述

iOS 9中的新功能,ATS(App Transport Security)有助于确保应用程序和任何后端服务器之间的安全连接。 默认情况下,当应用程序与iOS 9.0 SDK或更高版本链接时,它会启用。 启用ATS后,HTTP连接将强制使用HTTPS(TLS v1.2),并且使用不安全HTTP连接的任何尝试都将失败。

应用ATS包括几个方案:

  • 开发人员可以 启用 全局ATS(通过链接到iOS 9.0或更高版本的SDK),然后选择使用例外减少特定服务器上的ATS限制
  • 开发人员可以 禁用 全局ATS(通过将NSAllowsArbitraryLoads键设置为YES),然后使用例外来增加特定服务器上的ATS限制

建议

对于在iOS 9.0或更高版本上运行的应用程序,最佳做法是通过链接到iOS 9.0或更高版本的SDK来启用ATS,并且不要NSAllowsArbitraryLoads键设置为“Yes”或“True”。 苹果目前允许开发人员为任何不能实施TLS的域设置为例外。 可以使用NSExceptionAllowsInsecureHTTPLoadsNSThirdPartyExceptionAllowsInsecureHTTPLoads键来设置例外。 重要的是要注意,从2017年1月开始,苹果将要求开发人员对应用程序中声明的任何异常(在App Store审核期间)提供合理的理由。 否则,所有通信必须使用ATS。

参考

CWE/OWASP

6.6 正确应用Touch ID

详细描述

Touch ID通常被用来允许用户在不输入密码的情况下对其设备进行认证和解锁。 一些开发人员还使用Touch ID,允许用户使用存储的设备指纹对其应用进行身份验证。

当开发人员在其应用中应用Touch ID时,通常会采用以下两种方式之一:

  1. 仅使用本地认证框架来认证用户
  2. 这种方法使认证机制很容易被绕过
  3. 攻击者可以在运行时修改本地检查,或者通过修补二进制。 这可以通过覆盖LAContextevaluatePolicy:localizedReason:reply方法来实现
  4. 使用钥匙串访问控制列表(ACL)

建议

当使用Touch ID进行身份验证时,将应用程序的密钥存储在钥匙串中,并将ACL分配给该项目。 使用此方法,iOS将在读取和将Keychain项目返回到应用程序之前执行用户存在检查。 开发人员可以在Apple网站上找到示例代码: https://developer.apple.com/library/ios/samplecode/KeychainTouchID/Listings/KeychainTouchID_AAPLKeychainTestsViewController_m.html.

参考

CWE/OWASP

7 Android

7.1 谨慎配置 File Permissions

详细描述

所有人可读的文件可以作为程序的一个向量,泄漏敏感信息。 所有人可写文件可能会暴露您的应用程序,让攻击者通过覆盖您的应用从存储读取的数据影响其行为。 示例包括设置文件和存储的登录信息。

建议

不要创建具有MODE_WORLD_READABLE或MODE_WORLD_WRITABLE权限的文件,除非它是必需的,因为任何应用程序都能读取或写入该文件,即使它可能存储在应用程序的私人数据目录中。

注意: 这些常量在Android API级别17中已弃用。#### 参考: http://developer.android.com/reference/android/content/Context.html

不要使用chmod二进制或系统调用接受文件模式(chmod,fchmod,creat等)的模式,例如0666,0777和0664,

CWE/OWASP

7.2 谨慎实现 Intents

详细描述

Intents用于组件间信令,而且可以被用来做以下事情:

  • 启动Activity,通常为应用程序打开用户界面
  • 作为广播以通知系统和应用程序的变化
  • 开始,停止和与后台服务进行通信
  • 通过ContentProvider访问数据
  • 作为回调处理事件

不正确的实现可能导致数据泄露,受限功能被调用和程序流被操纵。

建议

  • 通过Intents访问的组件可以是公共的或私有的。 默认值取决于Intents过滤器,很容易错误地允许组件被公开。 可以在应用程序的Manifest中将组件设置为android:exported = false,以防止出现这种情况。

  • 默认情况下,在清单中声明的公共组件是开放的,所以任何应用程序都可以访问它们。 如果组件不需要被所有其他应用程序访问,请考虑设置对清单中声明的组件的权限。

  • 公共组件接收的数据不可信任,必须仔细检查。

CWE/OWASP

7.3 检查 Activities

通常在Android应用程序中,Activity是应用程序中的“屏幕”。

详细描述

任何应用程序都可以调用exported 和 enabled的Activity . 这可能允许攻击者以开发者可能不想要的方式加载UI元素,例如跳过密码锁定屏幕访问数据或功能。 默认情况下,不会导出Activity,但是,如果为Activity定义了Intent过滤器,系统将导出Activity。

建议

Activities 可以通过检查内部应用程序状态来验证它们是否可以加载来确保正确的行为。 例如,首先看看应用程序是否处于“未锁定”状态,如果没有,则跳回到锁定屏幕。 无论定义什么Intent过滤器,exported/enabled都可以直接使用未定义的数据调用Activity,因此当对不受信任的源提供的数据进行操作时,#### 建议使用输入验证。

传递Intent额外ID的示例代码,而不是整个对象。

//bad passing the whole paracable object
public static Intent getStartingIntent(Context context,
        User user) {
    Intent i = new Intent(context, UserDetailsActivity.class);
    i.putExtra(EXTRA_USER, user);
    return i;
}

//better to pass just the ID to lookup the user details
public static Intent getStartingIntent(Context context,
        String userId) {
    Intent i = new Intent(context, UserDetailsActivity.class);
    i.putExtra(EXTRA_USER_ID, userId);
  return i;
}

Avoid intent filters on Activities if they are private, instead use explicit intent. 避免对私有的Activities添加intent过滤器,而是使用显式intent。

<activity
    android:name="com.app.YourActivity"
    android:label="@string/app_name"
    android:excludeFromRecents="true"
    android:exported="false" >
</activity>

参考

CWE/OWASP

7.4 谨慎使用 Broadcasts

详细描述

如果在发送广播Intent时未设置权限,则任何非特权应用程序都可以接收Intent,除非它有明确的目标。

攻击者可以通过以下方式利用没有任何设置权限的Intent:

  • 创建包含与合法组件具有相同名称的组件的恶意应用程序
  • 只要该名称(或命名空间)尚未使用,恶意应用程序将安装在目标设备上
  • 从发送到该组件名称的广播Intent提取敏感数据

建议

使用权限来保护应用程序中的Intents。 请记住,当通过广播Intent向第三方组件发送信息时,该组件可能已被恶意安装替换。

参考

CWE/OWASP

7.5 谨慎实现 PendingIntents

PendingIntent允许应用程序将Intent传递给第二个应用程序,然后可以执行该Intent,就像它是原始应用程序一样(即具有相同的权限)。

详细描述

使用PendingIntent,应用程序可以将Intent传递给第二个应用程序,然后可以执行该Intent,就像它是原始应用程序一样(即具有相同的权限)。 这允许其他应用程序回调到原始应用程序的私有组件。 外部应用程序,如果是恶意的,可能会尝试影响目标 和/或 数据/完整性。

建议

使用PendingIntents作为对私有BroadcastReceivers或broadcast activities的延迟回调,并在基本Intent中显式指定组件名称。

参考

CWE/OWASP

7.6 保护应用程序服务

详细描述

服务通常用于后台处理。 与BroadcastReceivers和应用程序 activities 一样,应用程序服务可以由外部应用程序调用,因此应该由权限和导出标志保护。

建议

服务可以具有可以从外部调用者调用的多于一个方法。 可以为每个方法定义任意权限,并通过使用checkPermission()检查调用包是否具有相应的权限。 或者,可以通过使用AndroidManifest中定义的权限来定义单独的服务和访问权限。

当调用具有敏感数据的服务时,验证正在调用正确的服务,而不是恶意服务。 如果您知道要连接的组件的确切名称,请在用于连接的意图中指定该名称。 另一种方法是再次使用checkPermission()来验证调用包是否具有接收所需Intent所需的权限。 用户在安装期间向应用程序授予权限。

下面是一个例子,声明并需要在访问com.example.MyService.时使用自定义权限

<permission android:name="com.example.mypermission" 
android:label="my_permission" android:protectionLevel="dangerous"></permission>`
<service

    android:name="com.example.MyService"

    android:permission="com.example.mypermission">

    <intent-filter>

        <action android:name="com.example.MY_ACTION" />

    </intent-filter>

</service>

CWE/OWASP

7.7 避免 Intent 嗅探

当activity由另一个使用广播Intent的应用程序启动时,Intent中传递的数据可以由恶意应用程序读取。

详细描述

当另一个应用程序通过发送广播Intent启动activity时,恶意应用程序可以读取Intent中包含的数据。 恶意应用程序还可以读取应用程序的最近Intent列表。 例如,如果应用程序调用并传递URL到Android Web浏览器,攻击者可以嗅探该URL。

建议

不要在使用广播Intent的应用之间传递敏感数据。 而使用显式Intent。

CWE/OWASP

7.8 谨慎使用content providers

详细描述

Content providers 允许应用程序使用URI寻址方案和关系数据库模型共享数据。 它们也可以用于通过URI方案访问文件。

建议

Content providers 可以声明权限和单独的读写访问。 除非绝对必要,否则不要给内容提供者写访问权限。 除非必需,请确保设置权限,以便非特权应用程序无法读取ContentProvider实例。

将访问限制为操作所需的最低限度。 例如,要与向某个联系人发送该邮件的其他应用程序共享即时消息,只共享该单个邮件,而不是所有即时消息。 内容提供商中的记录级委派功能允许共享特定记录或文件,而不共享整个数据库。 一旦外部应用程序返回到原始应用程序,代表结束。

将传递给内容提供者的参数视为不受信任的输入,并且不在没有防护的情况下直接在SQL查询中使用它们。 没有防护的SQL代码可以通过内容提供者请求发送。 如果SQL代码包含在查询中,它可以返回数据或向攻击者提供控制权。

基于文件名传递给提供者的文件的内容提供者应该确保路径遍历被过滤掉。 例如,如果攻击者在请求中包含`../../../ file',它可能导致程序读取并返回攻击者在其他情况下不能访问的文件中的数据。 应用程序。 此外,请注意,攻击者创建的以下符号链接可能具有类似的结果。

CWE/OWASP

7.9 遵循WebView最佳实践

详细描述

WebView可以引入一些安全问题,应该小心地使用。 目前已经发现了使用addJavscriptInterface API所产生的许多可利用的漏洞。

建议

如果不需要JavaScript和Plugin支持,请禁用。 虽然默认情况下都禁用它们,但最佳做法需要明确将其设置为禁用。 禁用本地文件访问。 这限制了对应用程序的资源和资产目录的访问,并减轻了来自网页的攻击,该网页试图获得对其他本地可访问文件的访问。

禁止从第三方主机加载内容。 这可能很难在应用程序内实现。 但是,开发人员可以覆盖shouldOverrideUrlLoading和shouldInterceptRequest来拦截,检查和验证从WebView中发起的大多数请求。 开发者还可以考虑通过使用URI类来检查URI的组件以确保其匹配已批准资源的列表中的条目来实现白名单方案。

示例代码 https://gist.github.com/scottyab/6f51bbd82a0ffb08ac7a

参考

CWE/OWASP

7.10 避免存储缓存的摄像机图像

远程支票存款应用程序允许一个人用手机的相机拍摄支票的图片,然后将图像发送到他们的金融机构存入他们的帐户。

详细描述

使用远程支票存款应用程序,一个人可以用手机的相机拍照支票,然后将图像发送到他们的金融机构存入他们的帐户。 许多这些应用程序将保留检查图像(或其一部分)在移动设备的NAND内存中,即使它被删除。

建议

不要在设备上使用非易失性存储器传输支票图像,支票图像可能会残留。 一个可能的替代方案是:

  1. 创建一个SurfaceView,显示相机预览或实时预览相机传感器所看到的内容
  2. 插入并编程一个按钮,当按下时,将相机预览作为像素阵列返回
  3. 最后,将像素阵列转换为位图,将其压缩为.jpg,将其编码为Base64,并将其提交到远程位置

此方法将只维护易失性内存中的图像,并防止支票图像在非易失性存储器中的缓存。

特别是使用Android Camera类,可以使用方法takePicture指定当使用Camera.PictureCallback接口生成.jpg时的回调。 特别是,我们对方法“public void onPictureTaken(byte [] bytes,Camera camera)感兴趣。

使用这种技术,可以使用“bytes”数组内容,其中包含RAM中的照片。

CWE/OWASP

7.11 避免GUI对象缓存

远程支票存款应用程序允许人们使用他们的设备拍摄支票的照片,并将其发送到其金融机构以存入帐户。

详细描述

Android将应用程序屏幕保留在内存中,并且多任务可以导致将整个应用程序保留在内存中(即使用户注销其帐户)。 这允许发现或窃取设备的攻击者直接导航到保留的屏幕,其可以包括作为GUI的一部分的敏感用户数据。 例如,如果用户退出银行应用,但不退出或关闭应用,则可以保留显示交易活动的屏幕,并且攻击者可以看到该屏幕。

建议

为了解决这个问题,开发人员有三个常见的选择:

  1. 当用户注销时,完全退出应用程序。 虽然违反Android设计原则退出您自己的应用程序,它是更安全,因为退出应用程序将销毁任何保留的GUI屏幕。

  2. 任何时间启动活动或访问屏幕时,请执行检查以确定用户是否处于已登录状态。 如果用户未登录,则显示登录屏幕。

  3. 在离开屏幕或注销之前,在GUI屏幕上清除数据。

CWE/OWASP

7.12 签名 Android APKs

详细描述

APK应使用未过期的证书正确签名。

建议

  • 使用生产证书(而不是调试证书)签署生产应用程序
  • 确保证书包含足够的有效期(即,不会在应用的预期有效期内过期)
  • Google#### 建议您的证书至少使用2048位加密
  • 确保包含签名密钥的密钥库被正确保护
  • 此外,将密钥库的访问权限限制为只有那些绝对需要它的人

下面是一个生成私钥的Keytool命令的示例:

$ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

参考

CWE/OWASP

8 Servers

8.1 实施正确的Web服务器配置

详细描述

Web服务器上的某些设置可以提高安全性。 Web服务器上通常被忽视的漏洞是信息泄露。 信息泄露可能导致严重的问题,因为每一个信息攻击者都可以从服务器获得使攻击更容易。

建议

减少信息泄露的一种简单方法是禁用详细错误。 详细错误在开发环境中很有用,但在生产环境中可能会泄露关键信息,例如Web框架信息和版本。 攻击者可以使用此信息来定位旨在利用实施特定缺陷的攻击。

减少信息泄露的另一个简单方法是返回服务器响应中的最少量的信息。 默认情况下,Apache将返回其版本号,运行它的操作系统和插件运行。 通过更改配置文件中的单个行,可以减少到只公开服务器正在运行Apache而不影响功能。

可以大大提高安全性的服务器中的一个配置更改是更改任何默认目录。 攻击者经常在Internet上搜索具有“low-hanging fruit”的网站,例如默认登录,容易猜到的管理界面,以及简单的“隐藏”目录命名方案。 这是一个好的策略,模糊处理需要网络访问的服务器上的任何敏感网页的位置。

管理或其他限制区域不应公开网络访问,除非绝对必要,并且必须抵抗暴力攻击。 HTTP认证或没有锁定保护的表单认证可以(并且将)被暴力攻击。

CWE/OWASP

8.2正确配置服务器端SSL

详细描述

许多Web服务器允许较低的加密设置,例如非常弱的导出级40位加密。 实施强密码套件以保护用于创建共享密钥,在客户端和服务器之间加密消息,以及生成消息哈希和签名以确保这些消息的完整性的信息。 还要确保禁用弱协议。

建议

确保正确安装和配置SSL证书以实现最高的加密。 如果可能,仅启用强密码(128位及以上)。

TLSv1已超过10年,并且在2009年被发现很容易受到“重新协商攻击”。

  • 大多数使用TLSv1的服务器已修补以关闭此漏洞,但您应针对相关服务器验证此漏洞。
  • TLSv1协议已更新,当前更多的TLSv1.2提供了最新的技术和最强的加密密码。 更新到较新版本的TLS应该更加坚固和面向未来的应用程序。

避免使用弱cipher,例如:

  • NULL cipher suite
  • 匿名Diffie-Hellman
  • DES 和 RC4 (因为它们容易受到加密分析攻击)

避免弱协议,例如:

如需获取有关如何安全地设计和配置应用程序的传输层安全性的更多信息。#### 参考 OWASP Transport Layer Protection Cheat Sheet for more information about how to securely design and configure transport layer security for an app.

参考

CWE/OWASP

8.3 使用合适的会话管理

详细描述

大多数应用程序都通过可能容易受到攻击的Cookie来维护用户的会话。

Remediation

Web语言(例如Java,.NET)提供会话管理,这是经过良好开发和安全测试的。 使服务器软件保持最新的安全补丁。 开发您自己的会话管理更有风险,只有在适当的专业知识。 确保会话cookie的大小足够。 短的或可预测的会话cookie使攻击者可能预测,高压或对会话执行其他攻击。 在会话配置中使用高安全性设置。

CWE/OWASP

8.4 保护和执行Web服务的渗透测试

详细描述

已经被攻破的服务器有可能拦截用户凭据并对应用用户发起其他攻击。

建议

一般来说,生产Web服务器必须经过彻底测试和防御恶意攻击。 生产服务器软件应更新到最新版本,并加固以防止有关服务器软件和接口的信息泄露。

身份验证表单不应反映用户名是否存在。 如果攻击者有一个方法来确定有效的用户名,他们有一个起点的暴力攻击和网络钓鱼攻击。 通过向客户端提供“无效的用户/密码组合”和“没有找到此类用户名”事件的相同响应,阻止用户名收集。 所有的交换敏感数据的登录表单和表单/页面都应该实现并需要HTTPS。 Web服务器不应允许没有SSL的客户端连接用于此类资源。 关闭详细错误,删除任何遗留的不必要的站点或页面,并持续强化Web资源以防潜在的攻击。

参考

CWE/OWASP

8.5 保护内部资源

详细描述

用于内部使用的资源(例如管理员登录表单)经常使用可能被暴力破解的身份验证。 例如无锁定的HTTP或表单认证。 管理或其他内部资源的泄露可能导致广泛的数据丢失和其他损害。

建议

这种资源应该被阻止外部访问。 任何不需要公共互联网访问的资源都应该使用防火墙规则和网络分段进行限制。 如果登录页面,管理区域或其他资源是外部可访问的,它就会被恶意用户发现和暴力攻击。

CWE/OWASP

参考文档:https://github.com/nowsecure/secure-mobile-development/

 

08.产品设计开发最佳实践

待定

09. 常见协议安全

本文档通过介绍常见开发过程中因协议配置错误或代码漏洞而导致安全的问题去避免类似现象的发生。 常见开发过程中的协议:

  • HTTPS
  • WebSocket
  • JWT
  • OAuth
  • Json
  • XML

WebSocket 安全

  • 输入未做校验
  • 帧数大小未做限制
  • 最大连接数未做限制,既可以耗尽客户端也可以耗尽服务端
  • 持久链接过多未自动关闭
  • Origin头部未做验证
  • 未采用HTTPS
  • 访问策略未做限制,存在越权现象(授权需由服务端限制)

OAuth 安全

  • 回调域名需要白名单限制访问
  • OAuth Token泄露
  • Refer消息头泄露Authorization Code

JWT安全

Json, XML安全

引用

10. MySQL安全配置

数据库作为数据管理的平台,它的安全性首先由系统的内部安全和网络安全两部分来决定。对于系统管理员来说,首先要保证系统本身的安全,在安装MySQL数据库时,需要对基础环境进行较好的配置。

修改root用户口令,删除空口令

缺省安装的MySQL的root用户是空密码的,为了安全起见,必须修改为强密码,所谓的强密码,至少8位,由字母、数字和符号组成的不规律密码。使用MySQL自带的命令mysaladmin修改root密码,同时也可以登陆数据库,修改数据库mysql下的user表的字段内容,修改方法如下所示:

# /usr/local/mysql/bin/mysqladmin -u root password “upassword” //使用mysqladmin
#mysql> use mysql;
#mysql> update user set password=password('upassword') where user='root';
#mysql> flush privileges; //强制刷新内存授权表,否则用的还是在内存缓冲的口令

删除默认数据库和数据库用户

一般情况下,MySQL数据库安装在本地,并且也只需要本地的php脚本对mysql进行读取,所以很多用户不需要,尤其是默认安装的用户。MySQL初始化后会自动生成空用户和test库,进行安装的测试,这会对数据库的安全构成威胁,有必要全部删除,最后的状态只保留单个root即可,当然以后根据需要增加用户和数据库。

#mysql> show databases;
#mysql> drop database test; //删除数据库test
#use mysql;
#delete from db; //删除存放数据库的表信息,因为还没有数据库信息。
#mysql> delete from user where not (user='root') ; // 删除初始非root的用户
#mysql> delete from user where user='root' and password=''; //删除空密码的root,尽量重复操作
Query OK, 2 rows affected (0.00 sec)
#mysql> flush privileges; //强制刷新内存授权表。

改变默认mysql管理员帐号

系统mysql的管理员名称是root,而一般情况下,数据库管理员都没进行修改,这一定程度上对系统用户穷举的恶意行为提供了便利,此时修改为复杂的用户名,请不要在设定为admin或者administraror的形式,因为它们也在易猜的用户字典中。

mysql> update user set user="newroot" where user="root"; //改成不易被猜测的用户名mysql> flush privileges;

关于密码的管理

密码是数据库安全管理的一个很重要因素,不要将纯文本密码保存到数据库中。如果你的计算机有安全危险,入侵者可以获得所有的密码并使用它们。相反,应使用MD5()、SHA1()或单向哈希函数。也不要从词典中选择密码,有专门的程序可以破解它们,请选用至少八位,由字母、数字和符号组成的强密码。在存取密码时,使用mysql的内置函数password()的sql语句,对密码进行加密后存储。例如以下方式在users表中加入新用户。

#mysql> insert into users values (1,password(1234),'test');

使用独立用户运行msyql

绝对不要作为使用root用户运行MySQL服务器。这样做非常危险,因为任何具有FILE权限的用户能够用root创建文件(例如,~root/.bashrc)。mysqld拒绝使用root运行,除非使用–user=root选项明显指定。应该用普通非特权用户运行mysqld。正如前面的安装过程一样,为数据库建立独立的linux中的mysql账户,该账户用来只用于管理和运行MySQL。

要想用其它Unix用户启动mysqld,增加user选项指定/etc/my.cnf选项文件或服务器数据目录的my.cnf选项文件中的[mysqld]组的用户名。

#vim /etc/my.cnf
[mysqld]
user=mysql

该命令使服务器用指定的用户来启动,无论你手动启动或通过mysqld_safemysql.server启动,都能确保使用mysql的身份。也可以在启动数据库是,加上user参数。

# /usr/local/mysql/bin/mysqld_safe --user=mysql &

作为其它linux用户而不用root运行mysqld,你不需要更改user表中的root用户名,因为MySQL账户的用户名与linux账户的用户名无关。确保mysqld运行时,只使用对数据库目录具有读或写权限的linux用户来运行。

禁止远程连接数据库

在命令行netstat -ant下看到,默认的3306端口是打开的,此时打开了mysqld的网络监听,允许用户远程通过帐号密码连接数本地据库,默认情况是允许远程连接数据的。为了禁止该功能,启动skip-networking,不监听sql的任何TCP/IP的连接,切断远程访问的权利,保证安全性。假如需要远程管理数据库,可通过安装PhpMyadmin来实现。假如确实需要远程连接数据库,至少修改默认的监听端口,同时添加防火墙规则,只允许可信任的网络的mysql监听端口的数据通过。

# vim /etc/my.cf
将#skip-networking注释去掉。
# /usr/local/mysql/bin/mysqladmin -u root -p shutdown //停止数据库
#/usr/local/mysql/bin/mysqld_safe --user=mysql & //后台用mysql用户启动mysql

限制连接用户的数量

数据库的某用户多次远程连接,会导致性能的下降和影响其他用户的操作,有必要对其进行限制。可以通过限制单个账户允许的连接数量来实现,设置my.cnf文件的mysqld中的max_user_connections变量来完成。GRANT语句也可以支持 资源控制选项来限制服务器对一个账户允许的使用范围。

#vim /etc/my.cnf
[mysqld]
max_user_connections 2

用户目录权限限制

默认的mysql是安装在/usr/local/mysql,而对应的数据库文件在/usr/local/mysql/var目录下,因此,必须保证该目录不能让未经授权的用户访问后把数据库打包拷贝走了,所以要限制对该目录的访问。确保mysqld运行时,只使用对数据库目录具有读或写权限的linux用户来运行。

# chown -R root  /usr/local/mysql/  //mysql主目录给root
# chown -R mysql.mysql /usr/local/mysql/var //确保数据库目录权限所属mysql用户

命令历史记录保护

数据库相关的shell操作命令都会分别记录在.bash_history,如果这些文件不慎被读取,会导致数据库密码和数据库结构等信息泄露,而登陆数据库后的操作将记录在.mysql_history文件中,如果使用update表信息来修改数据库用户密码的话,也会被读取密码,因此需要删除这两个文件,同时在进行登陆或备份数据库等与密码相关操作时,应该使用-p参数加入提示输入密码后,隐式输入密码,建议将以上文件置空。

# rm .bash_history .mysql_history  //删除历史记录
# ln -s /dev/null .bash_history   //将shell记录文件置空
# ln -s /dev/null .mysql_history  //将mysql记录文件置空

禁止MySQL对本地文件存取

在mysql中,提供对本地文件的读取,使用的是load data local infile命令,默认在5.0版本中,该选项是默认打开的,该操作令会利用MySQL把本地文件读到数据库中,然后用户就可以非法获取敏感信息了,假如你不需要读取本地文件,请务必关闭。

测试:首先在测试数据库下建立sqlfile.txt文件,用逗号隔开各个字段

# vi sqlfile.txt
1,sszng,111
2,sman,222
#mysql> load data local infile 'sqlfile.txt' into table users fields terminated by ','; //读入数据
#mysql> select * from users;

+--------+------------+----------+
| userid  | username   | password |
+--------+------------+----------+
|      1 | sszng       | 111      |
|      2 | sman        | 222      |
+--------+------------+----------+

成功的将本地数据插入数据中,此时应该禁止MySQL中用“LOAD DATA LOCAL INFILE”命令。网络上流传的一些攻击方法中就有用它LOAD DATA LOCAL INFILE的,同时它也是很多新发现的SQL Injection攻击利用的手段!黑客还能通过使用LOAD DATALOCAL INFILE装载“/etc/passwd”进一个数据库表,然后能用SELECT显示它,这个操作对服务器的安全来说,是致命的。可以在my.cnf中添加local-infile=0,或者加参数local-infile=0启动mysql。

#/usr/local/mysql/bin/mysqld_safe --user=mysql --local-infile=0 &
#mysql> load data local infile 'sqlfile.txt' into table users fields terminated by ',';
#ERROR 1148 (42000): The used command is not allowed with this MySQL version

--local-infile=0选项启动mysqld从服务器端禁用所有LOAD DATA LOCAL命令,假如需要获取本地文件,需要打开,但是建议关闭。

 

MySQL服务器权限控制 MySQL权限系统的主要功能是证实连接到一台给定主机的用户,并且赋予该用户在数据库上的SELECT、INSERT、UPDATE和DELETE等权限(详见user超级用户表)。它的附加的功能包括有匿名的用户并对于MySQL特定的功能例如LOAD DATA INFILE进行授权及管理操作的能力。

管理员可以对user,db,host等表进行配置,来控制用户的访问权限,而user表权限是超级用户权限。只把user表的权限授予超级用户如服务器或数据库主管是明智的。对其他用户,你应该把在user表中的权限设成’N’并且仅在特定数据库的基础上授权。你可以为特定的数据库、表或列授权,FILE权限给予你用LOAD DATA INFILE和SELECT … INTO OUTFILE语句读和写服务器上的文件,任何被授予FILE权限的用户都能读或写MySQL服务器能读或写的任何文件。(说明用户可以读任何数据库目录下的文件,因为服务器可以访问这些文件)。 FILE权限允许用户在MySQL服务器具有写权限的目录下创建新文件,但不能覆盖已有文件在user表的File_priv设置Y或N。,所以当你不需要对服务器文件读取时,请关闭该权限。

#mysql> load data infile 'sqlfile.txt' into table loadfile.users fields terminated by ',';
Query OK, 4 rows affected (0.00 sec) //读取本地信息sqlfile.txt'
Records: 4  Deleted: 0  Skipped: 0  Warnings: 0
#mysql> update user set File_priv='N' where user='root'; //禁止读取权限
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> flush privileges; //刷新授权表
Query OK, 0 rows affected (0.00 sec)
#mysql> load data infile 'sqlfile.txt' into table users fields terminated by ','; //重登陆读取文件
#ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) //失败
# mysql> select * from loadfile.users into outfile 'test.txt' fields terminated by ',';
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

为了安全起见,随时使用SHOW GRANTS语句检查查看谁已经访问了什么。然后使用REVOKE语句删除不再需要的权限。

使用chroot方式来控制MySQL的运行目录

Chroot是linux中的一种系统高级保护手段,它的建立会将其与主系统几乎完全隔离,也就是说,一旦遭到什么问题,也不会危及到正在运行的主系统。这是一个非常有效的办法,特别是在配置网络服务程序的时候。

关闭对Web访问的支持

如果不打算让Web访问使用MySQL数据库,没有提供诸如PHP这样的Web语言的时候,重新设置或编译你的PHP,取消它们对MySQL的默认支持。假如服务器中使用php等web程序,试试用Web形式非法的请求,如果得到任何形式的MySQL错误,立即分析原因,及时修改Web程序,堵住漏洞,防止MySQL暴露在web面前。

对于Web的安全检查,在MySQL官方文档中这么建议,对于web应用,至少检查以下清单:

  • 试试用Web形式输入单引号和双引号(‘’’和‘”’)。如果得到任何形式的MySQL错误,立即分析原因。

  • 试试修改动态URL,可以在其中添加%22(‘”’)、%23(‘#’)和%27(‘’’)。

  • 试试在动态URL中修改数据类型,使用前面示例中的字符,包括数字和字符类型。你的应用程序应足够安全,可以防范此类修改和类似攻击。

  • 试试输入字符、空格和特殊符号,不要输入数值字段的数字。你的应用程序应在将它们传递到MySQL之前将它们删除或生成错误。将未经过检查的值传递给MySQL是很危险的!

  • 将数据传给MySQL之前先检查其大小。

  • 用管理账户之外的用户名将应用程序连接到数据库。不要给应用程序任何不需要的访问权限。

数据库备份策略

一般可采用本地备份和网络备份的形式,可采用MySQL本身自带的mysqldump的方式和直接复制备份形式, 直接拷贝数据文件最为直接、快速、方便,但缺点是基本上不能实现增量备份。为了保证数据的一致性,需要在备份文件前,执行以下 SQL 语句:FLUSH TABLES WITH READ LOCK;也就是把内存中的数据都刷新到磁盘中,同时锁定数据表,以保证拷贝过程中不会有新的数据写入。这种方法备份出来的数据恢复也很简单,直接拷贝回原来的数据库目录下即可。

使用mysqldump可以把整个数据库装载到一个单独的文本文件中。这个文件包含有所有重建您的数据库所需要的SQL命令。这个命令取得所有的模式(Schema,后面有解释)并且将其转换成DDL语法(CREATE语句,即数据库定义语句),取得所有的数据,并且从这些数据中创建INSERT语句。这个工具将您的数据库中所有的设计倒转。因为所有的东西都被包含到了一个文本文件中。这个文本文件可以用一个简单的批处理和一个合适SQL语句导回到MySQL中。

使用 mysqldump进行备份非常简单,如果要备份数据库” nagios_db_backup ”,使用命令,同时使用管道gzip命令对备份文件进行压缩,建议使用异地备份的形式,可以采用Rsync等方式,将备份服务器的目录挂载到数据库服务器,将数据库文件备份打包在,通过crontab定时备份数据:

#!/bin/sh
time=`date +"("%F")"%R`
$/usr/local/mysql/bin/mysqldump -u nagios -pnagios nagios | gzip >/home/sszheng/nfs58/nagiosbackup/nagios_backup.$time.gz
# crontab -l
# m h  dom mon dow   command
00 00 * * * /home/sszheng/shnagios/backup.sh

恢复数据使用命令:
gzip -d nagios_backup.\(2008-01-24\)00\:00.gz
nagios_backup.(2008-01-24)00:00
#mysql –u root -p nagios  <  /home/sszheng/nfs58/nagiosbackup/nagios_backup.\(2008-01-24\)12\:00

Mysqld安全相关启动选项

下列mysqld选项影响安全:

  • --allow-suspicious-udfs

    • 该选项控制是否可以载入主函数只有xxx符的用户定义函数。默认情况下,该选项被关闭,并且只能载入至少有辅助符的UDF。这样可以防止从未包含合法UDF的共享对象文件载入函数。
  • --local-infile[={0|1}]

    • 如果用–local-infile=0启动服务器,则客户端不能使用LOCAL in LOAD DATA语句。
  • --old-passwords

    • 强制服务器为新密码生成短(pre-4.1)密码哈希。当服务器必须支持旧版本客户端程序时,为了保证兼容性这很有用。
  • (OBSOLETE) --safe-show-database

    • 在以前版本的MySQL中,该选项使SHOW DATABASES语句只显示用户具有部分权限的数据库名。在MySQL 5.1中,该选项不再作为现在的 默认行为使用,有一个SHOW DATABASES权限可以用来控制每个账户对数据库名的访问。
  • --safe-user-create

    • 如果启用,用户不能用GRANT语句创建新用户,除非用户有mysql.user表的INSERT权限。如果你想让用户具有授权权限来创建新用户,你应给用户授予下面的权限:
    • mysql> GRANT INSERT(user) ON mysql.user TO ‘user_name’@’host_name’;
    • 这样确保用户不能直接更改权限列,必须使用GRANT语句给其它用户授予该权限。
  • --secure-auth

    • 不允许鉴定有旧(pre-4.1)密码的账户。
  • --skip-grant-tables

    • 这个选项导致服务器根本不使用权限系统。这给每个人以完全访问所有的数据库的权力!(通过执行mysqladmin flush-privileges或mysqladmin eload命令,或执行FLUSH PRIVILEGES语句,你能告诉一个正在运行的服务器再次开始使用授权表。)
  • --skip-name-resolve

    • 主机名不被解析。所有在授权表的Host的列值必须是IP号或localhost。
  • --skip-networking

    • 在网络上不允许TCP/IP连接。所有到mysqld的连接必须经由Unix套接字进行。
  • --skip-show-database

  • 使用该选项,只允许有SHOW DATABASES权限的用户执行SHOW DATABASES语句,该语句显示所有数据库名。不使用该选项,允许所有用户执行SHOW DATABASES,但只显示用户有SHOW DATABASES权限或部分数据库权限的数据库名。请注意全局权限指数据库的权限

 

转自:https://www.securitypaper.org

posted @ 2021-11-09 23:06  穷到底  阅读(493)  评论(0编辑  收藏  举报