校验跨信任边界传递的不可信数据(策略检查数据合法性,含白名单机制等)格式化字符串时,依然要检验用户输入的合法性,避免可形成系统信息泄露或者拒绝服务javascript
禁止向Java Runtime.exec()方法传递不可信、未净化的数据(当参数中包含空格,双引号,以-或者/符号开头表示一个参数开关时,可能会致使参数注入漏洞),建议若是能够禁止JVM执行外部命令,未知漏洞的危害性会大大下降,能够大大提升JVM的安全性。php
验证路径以前应该先将其标准化为实际路径(特殊的文件名,好比“..”,symbolic links、hard links、shortcuts)css
从ZipInputStream提取文件,若是不在程序预期计划的目录以内时,应拒绝将其提取出来,或者将其提取到一个安全的位置html
从ZipInputStream提取文件,若解压以后的文件大小超过必定的限制时,必须拒绝将其解压前端
在处理之前,验证全部来自客户端的数据,包括:全部参数、URL、HTTP头信息(好比:cookie名字和数据值),肯定包括了来自 JavaScript、Flash 或其余嵌入代码的post 返回信息java
若是任何潜在的危险字符必须被做为输入,请确保您执行了额外的安全控制,好比:输入转义、输出编码、特定的安全 API等。部分常见的危险字符,包含但不限于: < > " ' % ( ) & + \ \' \"node
若是您使用的标准验证规则没法验证下面的输入,那么它们须要被单独验证,好比验证空字节 (%00); 验证换行符 (%0d, %0a, \r, \n); 验证路径替代字符“点-点-斜杠”(../或 ..\);若是支持 UTF-8 扩展字符集编码,验证替代字符: %c0%ae%c0%ae/ (使用规范化验证双编码或其余类型的编码)python
严格验证来自重定向输入的数据(一个攻击者可能向重定向的目标直接提交恶意代码,从而避开应用程序逻辑以及在重定向前执行的任何验证)mysql
验证数据类型linux
验证数据范围
验证数据长度
为每一种输出编码方法采用一个标准的、已经过测试的规则
经过语义输出编码方式,对全部从服务端返回到客户端的数据进行编码。好比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或者使用存储过程进行数据查询存取
永远不要使用管理员权限进行数据库链接,为每一个应用使用单独的非特权权限,且配置有限的数据库链接数
不要把敏感信息明文存放,采用加密或者哈希、混淆等方式对敏感信息进行脱敏存储
应用的异常信息应不带有敏感信息,给出尽量少的提示;建议使用自定义的错误信息对原始错误信息进行包装,可把异常信息存放在独立的数据库表中
XML注入- 不要使用字符串/StringBuffer/StringBuilder/StringFormat组装XML
建议对XML元素属性或者内容进行转义
对输入的数据进行过滤和转义,包含但不限于< >" ' % ( ) & + \ \' \"等危险特殊字符
数据添加到html元素属性或者内容中时,对数据进行HTML转义
数据添加到script脚本中时,对数据进行script转义
数据添加到style中时,对数据进行css转义
建议在每一个关键表单中引入了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身份认证,加密传输,摘要签名安全保障
采用token验证访问控制,防止消费者绕过注册中心访问提供者;在注册中心控制权限以决定要不要下发令牌给消费者
采用filter IP白名单访问控制,同时也可预防生产系统和测试系统之间Dubbo混乱调用问题
在必要状况下(如敏感信息操做),链接注册中心Dubbo时要进行用户名和密码校验
应启用客户端IP访问控制验证功能
应启用客户端身份验证功能
敏感信息不要明文存储于Redis
为了提高公司各业务线产品的安全质量,下降安全风险,进一步提高公司总体安全水平,结合公司现有产品设计、开发、测试及上线流程,特制定此《XX产品安全开发流程》。
此流程适用于 XX 全部业务线产品的安全设计、开发、测试及上线。
SDL 的全称是 Security Development Lifecycle,即:安全开发生命周期。它是由微软最先提出的,是一种帮助解决软件安全问题的方法。SDL 中的方法,试图从安全漏洞产生的根源上解决问题。SDL 是一个安全保证的过程,经过对软件工程的控制,从而保证产品的安全性。它可以帮助企业以最小的成本提升产品的安全性,对企业安全的发展来讲,能够起到事半功倍的效果。
实施 SDL 的核心目的只有一个,那就是经过在开发过程当中加入安全控制的方法,提高各业务线项目、系统的安全性。避免因项目上线后存在安全漏洞被攻击者恶意利用而形成没必要要的安全风险和损失。
要求:须要保证全部项目在立项时都通知到安全团队。 通知方式:邮件+企业微信、立项会议
要求:全部需求必须经过安全评审,由安全团队来评估需求是否存在安全风险或是否须要设计安全功能;若是安全评审未经过,不容许进入下一步研发流程
安全评审流程:
安全评审成果:安全评审报告 安全评审沟通方式:邮件+企业微信、jira安全评审提交
在设计阶段,由产品和研发团队自查,确认是否有违反《XX开发安全红线准则》的行为。
在编码实现阶段,研发工程师应遵循 《XX安全开发手册》。
在开发工做完成后,应与功能测试同步进行安全测试,安全测试过程当中发现的安全漏洞经过“jira XX安全团队漏洞提交”管理。
安全测试需求提交方式:邮件+企业微信、项目安全提测系统
在业务验收时,应同步进行安全验收,完成后由安全团队发布《XX项目安全验收报告》,如 果在安全验收过程当中发现存在未修复的高危及严重级别安全漏洞,不容许进入上线发布环节。
php.ini基本安全配置
cgi.force_redirect在php.ini中修改,默认是开启的,它能够防止当PHP运行的CGI脚本未经验证的访问。在IIS,OmniHTTPD和Xitami上是禁用的,但在全部其余状况下它应该打开。
; php.ini cgi.force_redirect=1 ;
该指令仅对Apache模块版本的PHP有效。你能够针对每一个虚拟机或每一个目录开启或关闭dl()动态加载PHP模块。关闭动态加载的主要缘由是为了安全。经过动态加载,有可能忽略全部open_basedir限制。默认容许动态加载,除了使用安全模式。在安全模式,老是没法使用dl()。
; php.ini enable_dl=0 ;
file_uploads默认是开启的,容许将文件上传到您的站点。由于来自陌生人的文件本质上是不可信甚至危险的,除非您的网站绝对须要,不然应禁用此功能。若是开启请进行相应的限制,参考upload_max_filesize, upload_tmp_dir,和post_max_size。
; php.ini file_uploads = 0 ;
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" ;
默认为 0(禁用)。当禁用cookie时,若是它开启,PHP会自动将用户的会话ID附加到URL。基于 URL 的会话管理比基于 cookie 的会话管理有更多安全风险,从表面上看,这彷佛是让那些禁用cookie的用户正常使用您的网站的好方法。实际上,它使那些用户容易被任何人劫持他们的会话。例如用户有可能经过 email 将一个包含有效的会话 ID 的 URL 发给他的朋友,或者用户老是有可能在收藏夹中存有一个包含会话 ID 的 URL 来以一样的会话 ID 去访问站点。也能够从浏览器历史记录和服务器日志中检索URL获取会话ID。
; php.ini session.use_trans_sid = 0 ;
没有固定生命周期或到期日期的Cookie被称为非持久性或“会话”cookie,这意味着它们只会持续与浏览器会话同样长,而且在浏览器关闭时会消失。具备到期日期的Cookie叫作“持久性”Cookie,他们将被存储/保留到这些生存日期。
管理网站上的登陆会话应用非持久性cookie。要使cookie非持久化,只需省略该 expires属性便可。也能够使用session.cookie_lifetime实现。
allow_url_fopen和allow_url_include默认是开启的,他们容许代码从URL中读入脚本。从站点外部吸入可执行代码的能力,加上不完美的输入清理可能会使站点裸露给攻击者。即便该站点的输入过滤在今天是完美的,但不能保证之后也是。
; php.ini allow_url_fopen = 0 allow_url_include = 0
php安全编码建议
sleep()有时用于经过限制响应率来防止拒绝服务(DoS)攻击。可是由于它占用了一个线程,每一个请求须要更长的时间来服务,这会使应用程序更容易受到DoS攻击,而不是减小风险。
if (is_bad_ip($requester)) { sleep(5); // 不合规的用法 }
eval()函数是一种在运行时运行任意代码的方法。 函数eval()语言结构是很是危险的,由于它容许执行任意 PHP 代码。所以不鼓励使用它。若是您仔细的确认过,除了使用此结构之外别无方法,请多加注意,不要容许传入任何由用户提供的、未经完整验证过的数据。
eval($code_to_be_dynamically_executed) // 不合规的用法
由于从编译的应用程序中提取字符串很容易,因此永远不该对凭证进行硬编码。对于分发的应用程序尤为如此。 凭据应存储在受强保护的加密配置文件或数据库中的代码以外。
// 合规的用法 $uname = getEncryptedUser(); $password = getEncryptedPass(); connect($uname, $password);
// 不合规的用法 $uname = "steve"; $password = "blue"; connect($uname, $password);
有时候,咱们不但愿执行包括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了。
程序接受数据可能来源于未经验证的用户,网络链接和其余不受信任的来源,若是未对程序接受数据进行校验,则可能会引起安全问题。
使用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();
经过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();
对产生跨站的参数进行严格过滤,禁止传入<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 { // ... }
例:
错误的写法:
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); } }
忽略方法的放回值可能会致使没法预料的结果。
错误的写法:
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 } }
当一个变量指向一个NULL值,使用这个变量的时候又没有检查,这时会致使。NullPointerException。
在使用变量前必定要作是否为NULL值的校验。
数组没有覆盖的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 }
使用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(); } }
要避免由于分母为零而致使除法和取模运算出现异常。
if (num2 == 0) { // handle error } else { result1= num1 /num2; result2= num1 % num2; }
攻击者能够用意想不到的方式操纵public或protected的数据成员,因此须要将数据成员为private,对外提供可控的包装方法访问数据成员。
包含私人的,机密或其余敏感数据的类是不容许被复制的,解决的方法有两种:
一、类声明为final
final class SensitiveClass { // ... }
二、Clone 方法抛出CloneNotSupportedException异常
class SensitiveClass { // ... public final SensitiveClass clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }
若是由同一个类装载器装载,它们具备相同的彻底限定名称,则它们是两个相同的类。 不正确写法:
// 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) { // ... }
硬编码的敏感信息,如密码,服务器IP地址和加密密钥,可能会泄露给攻击者。
敏感信息均必须存在在配置文件或数据库中。
验证方法的参数,可确保操做方法的参数产生有效的结果。不验证方法的参数可能会致使不正确的计算,运行时异常,违反类的不变量,对象的状态不一致。 对于跨信任边界接收参数的方法,必须进行参数合法性校验
private Object myState = null; //对于修改myState 方法的入参,进行非空和合法性校验 void setState(Object state) { if (state == null) { // Handle null state } if (isInvalidState(state)) { // Handle invalid state } myState = state; }
在程序代码中使用过期的、陈旧的或低效的类或方法可能会致使错误的行为。
某个方法返回一个对敏感对象的内部数组的引用,假定该方法的调用程序不改变这些对象。即便数组对象自己是不可改变的,也能够在数组对象之外操做数组的内容,这种操做将反映在返回该数组的对象中。若是该方法返回可改变的对象,外部实体能够改变在那个类中声明的 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; } }
垃圾收集器只收集不可达的对象,所以,存在未使用的可到达的对象,仍然表示内存管理不善。过分的内存泄漏可能会致使内存耗尽,拒绝服务(DoS)。
对于捕获的异常要进行相应的处理,不能忽略已捕获的异常
不正确写法:
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 } } }
没有过滤敏感信息的异常堆栈每每会致使信息泄漏,
不正确的写法:
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; } } }
不正确的写法:
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 { //... }
不正确的写法:
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])); }
对于共享变量,要确保一个线程对它的改动对其余线程是可见的。 线程可能会看到一个陈旧的共享变量的值。为了共享变量是最新的,能够将变量声明为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; } }
除了要确保共享变量的更新对其余线程可见的,还须要确保对共享变量的操做是原子的,这时将共享变量声明为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(); } } }
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(); } }
有限线程池指定能够同时执行在线程池中的线程数量的上限。程序不得使用有限线程池线程执行相互依赖的任务。可能会致使线程饥饿死锁,全部的线程池执行的任务正在等待一个可用的线程中执行一个内部队列阻塞
Java的文件操做方法每每有一个返回值,而不是抛出一个异常,表示失败。所以,忽略返回值文件操做的程序,每每没法检测到这些操做是否失败。Java程序必须检查执行文件I / O方法的返回值。
不正确的写法:
File file = new File(args[0]); file.delete(); 正确的写法: File file = new File("file"); if (!file.delete()) { log.error("Deletion failed"); }
垃圾收集器没法释放非内存资源,如打开的文件描述符与数据库的链接。所以,不释放资源,可能致使资源耗尽攻击。
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 }
序列化容许一个对象的状态被保存为一个字节序列,而后从新在稍后的时间恢复,它没有提供任何机制来保护序列化的数据。敏感的数据不该该被序列化的例子包括加密密钥,数字证书。 解决方法:
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(); } }
不正确的写法:
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(); } } } }
(完成)
自己要注意的有,一些危险函数,危险模块的调用,主要是系统调用。这个若是调用必定要对输入输出作好过滤,如下是代码中各类致使进行系统调用的方式。尽可能避免。
避免各类状况致使系统调用
谨慎使用Eval
数据序列化
对应Web编程中安全概念在python web框架中的实现。url跳转,目录遍历,任意文件读取也须要考虑在内。针对不一样的框架也须要。
可参考phithon的博客,有较多相关资料。
安装使用方式较为简单,因此不作介绍。 - AST-based static Analyzer: Bandit - Static Analyzer: PYT
待定
移动安全带来了许多Web安全的挑战 - 普遍的受众,快速开发和持续的网络链接 - 加上更多传统胖客户端应用程序(例如缓冲区管理,本地加密和恶意软件)的常见风险。 移动环境的一个独特功能是来自未知开发人员的已安装应用程序的流行,这些应用程序应被视为“不受信任的”。
以下所示,移动攻击能够涉及设备层,网络层,数据中心或这些的组合。 固有平台漏洞和社会工程继续为网络盗贼提供重要机会,从而为那些寻求保护用户数据的人带来重大挑战。
在移动技术链中有三个点,恶意方可能利用漏洞来发起恶意攻击:
移动设备对敏感的公司信息(SCI)构成重大风险; 主要风险包括数据丢失和安全性受损。 不管是iPhone,Android或其余智能手机,针对设备自己的攻击者能够使用各类入口点:
基于浏览器的攻击点能够包括:
网络钓鱼 – 涉及经过电子邮件欺骗来假装为受信任实体来获取我的信息,例如用户名,密码和信用卡详细信息。 研究代表,移动用户比网络用户提交我的信息的可能性是钓鱼网站的三倍。 这在必定程度上多是因为移动浏览器运行的缩小的环境,因为有限的屏幕不动产,有限的警告对话,缩小的安全锁图标,而且放弃了许多用户界面指示器,所述环境只显示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手机上的一些预加载应用程序被发现包含安全漏洞,可用于擦除手机,窃取数据,甚至窃听电话。
零日漏洞 – 攻击常常发生在漏洞被首次利用的时间和软件开发人员可以发布解决问题的版本之间的窗口期间。
基于网络的攻击点能够包括:
Wi-Fi (弱加密/无加密) – 当在Wi-Fi网络上使用没法实施加密的应用程序时,存在恶意攻击者窃听无线链接而窃取数据的风险。 许多应用程序使用SSL / TLS,这提供了必定程度的保护; 然而一些针对SSL / TLS的攻击也被证实能够将关键用户数据暴露给攻击者。
恶意接入点 – 涉及物理安装受权各方访问安全网络的未受权无线接入点。
数据包嗅探 – 容许恶意入侵者捕获和分析网络流量,这一般包括以明文传输的用户名和密码信息。
中间人攻击 (MITM) – 涉及窃听现有网络链接,侵入该链接,拦截消息和修改选择数据。
SSLStrip – 一种中间人攻击的形式,利用了网站上的SSL / TLS实现的弱点,这能够依赖于用户验证HTTPS链接是否存在。 攻击将链接降级到HTTP,无需加密,用户很难在移动浏览器中检测到。
会话劫持 – 涉及会话密钥的利用以得到对用户和网络信息的未受权访问。
DNS 劫持 – 利用网络DNS能够将网站的用户引导到攻击者选择的另外一个网站。 在某些状况下,攻击还能够经过应用程序注入内容。
假SSL证书 – 另外一个中间人攻击涉及发出假SSL证书,容许恶意用户拦截假定安全的HTTPS链接上的流量。
针对数据中心的攻击者使用两个主要入口点:
基于Web服务器的攻击和漏洞包括:
平台漏洞 – 操做系统,服务器软件或Web服务器上运行的应用程序模块的漏洞可能被攻击者利用。 有时能够经过监视移动设备和web服务器之间的通讯来发现脆弱性,以发现协议或访问控制中的弱点。
服务器错误配置 – 配置不良的Web服务器可能容许对一般受保护的资源的未受权访问。
跨站点脚本 (XSS) – 跨站点脚本是一种涉及将恶意JavaScript代码注入网站的攻击。 容易受到此类攻击的页面会将用户输入返回浏览器,而不会正确地对其进行整理。 此攻击一般用于在用户访问页面时自动运行代码,从而控制用户的浏览器。 在已经创建对浏览器的控制以后,攻击者能够将该控制用于各类攻击,诸如内容注入或恶意软件传播。
跨站点请求伪造 (CSRF) – 跨站点请求伪造涉及攻击者根据特定Web应用程序的功能知识建立HTTP(Web)请求,并诱骗用户或浏览器提交这些请求。 若是Web应用程序易受攻击,攻击能够执行彷佛来自用户的事务或提交。 CSRF一般在攻击者已经经过XSS,社交工程或其余方法得到对用户会话的控制以后使用。
弱输入验证 – 许多Web服务过分信任来自移动应用程序的输入,依靠应用程序来验证最终用户提供的数据。 可是,攻击者能够伪造本身与Web服务器的通讯,或彻底绕过应用程序的逻辑检查,容许他们利用服务器上缺乏的验证逻辑来执行未经受权的操做。
暴力攻击 – 暴力攻击只是试图猜想对字段的有效输入,一般使用高速率的尝试和可能值的字典。 暴力攻击的最多见用法是身份验证,但也可用于在Web应用程序中发现其余有效值。
数据库攻击和漏洞包括:
SQL 注入 – 没有正确验证用户输入的接口可能致使SQL被注入到其余无害的应用程序查询中,致使数据库暴露或以其余方式操纵一般应该限制用户或应用程序的数据。
执行操做系统级命令 – 与SQL注入相似,某些数据库系统提供了执行操做系统级命令的方法。 攻击者能够将这些命令插入到查询中,使数据库在服务器上执行这些命令,从而为攻击者提供额外的权限,直至并包括根级别系统访问。
权限提高 – 当攻击利用一些漏洞得到更大的访问时,会发生这种状况。 在数据库上,这可能致使敏感数据被盗。 数据转储 - 攻击者致使数据库转储数据库中的一些或全部数据,暴露敏感记录。
移动应用一般分为三个操做类别:
Web – 经过通用网络浏览器操做的应用。 有时被称为WAP或移动网站,这些是移动等同于在过去十年中激增的功能性网络应用程序,提供许多功能,如网上银行和购物。 虽然常规网站可用于移动网络浏览器,但许多公司如今建立单独的移动网络应用以优化移动属性,例如更小的屏幕尺寸,基于触摸的导航和GPS位置的可用性。
Native – 安装的应用程序,其操做本机移动设备操做系统,为特定的移动平台编译并利用其API。 这些一般(但不老是)经过应用程序市场下载和安装。
Wrapper – 经过在专用本机应用程序包装器(有时也称为“shell应用程序”或“混合应用程序”)中利用网页操做的应用程序。虽然对最终用户显示为本机应用程序,但基于Web的功能可能致使不一样的漏洞 比在彻底本地编码的应用程序中发现。
反向工程应用程序能够提供有价值的洞察您的应用程序的工做原理。 使您的应用程序在内部更复杂,攻击者更难以看到应用程序如何操做,这能够减小攻击面的数量。
反向工程一个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应用程序中使用的加密可能会被熟练的攻击者剥离。 攻击者经过转储在运行时从设备的内存中直接加载应用程序的内存来实现这一点。
代码中的简单逻辑测试更容易受到攻击。 例如:
if sessionIsTrusted == 1
这是一个简单的逻辑测试,若是攻击者能够改变这个值,他们能够绕过安全控制。 苹果iOS被攻击使用这种类型的弱点和Android应用程序已经他们的Dalvik二进制补丁绕过各类保护机制。 这些逻辑测试很容易在许多级别上回避。 在组装级别上,攻击者能够仅使用调试器来攻击iOS应用程序,以找到正确的CBZ(零比较和分支)或CBNZ(比较和非零分支)指令并将其反转。 这能够在运行时执行,只需遍历对象的内存地址,并在应用程序运行时更改其实例变量。 在Android上,应用程序能够反编译为SMALI,并在从新编译以前修补分支条件。
考虑一个更好的编程范例,当会话不受信任时,由服务器强制执行权限,或者经过防止某些数据被解密或以其余方式可用,直到应用程序能够使用质询/响应,OTP或其余 形式的认证。 此外,#### 建议将全部健全检查功能声明为静态内联。 使用这种方法,它们被内联编译,使得更难以修补(即攻击者不能简单地重写一个函数或修补一个函数)。 这种技术将须要攻击者从应用程序中寻找并修补检查的每一个实例,增长了攻击所需的复杂性。 对于高度敏感的应用程序,创建在安全编码原则中的更复杂的方法可能值得进一步调查。 集成技术(如加密,定时回调和基于流的编程)可能增长攻击者的复杂性。
一样,存储在对象中的简单逻辑变量能够容易地被攻击者操纵。 例如:
session.trusted = TRUE
这样的值能够由应用程序当前使用的类的实例内的攻击者读取和写入。 在iOS上,经过操做Objective-C运行时,能够操纵这些变量,以便下次应用程序引用这些变量时,将读取任何操做的值。
开发人员很是依赖第三方库。 在测试代码时,完全探查和测试这一点很重要。 第三方库能够包含漏洞和弱点。 许多开发人员认为第三方库已经完善并通过测试,然而,问题可能并且确实存在于他们的代码中。
安全审计必须完全测试第三方库和功能。 这应该包括核心的iOS和Android代码/库。 升级到新版本的第三方库(或操做系统版本)应视为您的应用程序的一部分。 更新的第三方库(或新操做系统版本)可能包含新的漏洞或暴露您的代码中的问题。 应该测试它们,就像为应用程序测试新代码同样。 在iOS上,静态编译第三方库以免LD_PRELOAD攻击; 在这种攻击中,一个库及其功能能够被替换为具备被恶意代码替换的攻击者库。
攻击者能够在应用上篡改或安装后门,从新签名并将恶意版本发布到第三方应用市场。 这种攻击一般针对流行的应用程序和金融应用程序。
采用防篡改和篡改检测技术来防止非法应用程序执行。
使用校验和,数字签名和其余验证机制来帮助检测文件篡改。 当攻击者试图操纵应用程序时,不会保留正确的校验和,而且这能够检测和防止非法执行。 注意,这样的技术不是万无一失的,而且能够被充分动机的攻击者绕过。 校验和,数字签名和其余验证技术增长了攻击者必须花费的时间和精力才能成功地破坏应用程序。 应用程序能够在检测到篡改的状况下以静默方式擦除其用户数据,密钥或其余重要数据,以进一步挑战攻击者。 检测到篡改的应用程序还能够通知管理员。
在Android上,用于签名应用程序的公钥能够从应用程序的证书中读取,并用于验证应用程序是否已使用开发人员的私钥签名。 使用PackageManager类,能够检索应用程序的签名,而后将它们与正确的值进行比较。 若是有人篡改或从新签名了应用程序,比较将失败,致使检测到篡改应用程序。
一般,iOS开发人员会将应用程序设置存储在plist文件中,在某些状况下这可能会受到影响。
当应用程序正在使用时,用户或应用程序特定的数据能够存储在RAM中,而且在用户注销或会话超时时不会正确清除。 由于Android将应用程序存储在内存中(即便在使用后),直到内存被回收,加密密钥可能会保留在内存中。 发现或窃取设备的攻击者能够附加调试器并从应用程序转储内存,或者加载内核模块以转储内存中的所有内容。
当管理密码和其余敏感信息时,应用程序会将该信息保存在内存中,即便缓冲区释放了一段时间。 若是应用程序容易出现缓冲区溢出,格式化字符串,数据泄露和其余漏洞,这多是一个安全问题,这可能容许攻击者转储进程的内存以恢复敏感信息。
不要将内存中保存敏感数据(例如加密密钥)长于所需的时间。 清除使用后保存密钥的任何变量。 避免对敏感的密钥或密码使用不可变对象,如在Androidjava.lang.String
中,而是使用char数组。 即便对不可变对象的引用被删除或清空,它们可能保留在内存中,直到垃圾回收发生(这不能被应用程序强制)。
这只能经过低级语言完成,由于若是优化例程检测到缓冲区在覆盖后再也不使用,编译器和即时虚拟机将会因性能缘由而忽略这些操做。
有一些工具能够来绕过编译器优化以清除这些缓冲区,但他们都依赖工具链,特定的语言和平台。
在Android上,调用file.delete()将不会安全地擦除目标文件,而且只要它没有被覆盖,它能够从设备的物理图像刻出。 因为对NAND闪存的积极管理,擦除文件的传统方法一般不在移动设备上工做。
在假设任何写入设备的数据均可以恢复的状况下操做。 在某些状况下,加密可能会增长额外的保护层。
对于大多数应用程序不推荐使用如下方法,但可能删除文件并用大文件覆盖全部可用空间(这将迫使NAND闪存擦除全部未分配的空间)。 这种技术的缺点包括耗尽NAND闪存,致使应用和整个设备响应缓慢,并显着的功耗。
尽量避免在设备上存储敏感数据。
加密存储在文件中的敏感数据,在删除以前重写文件的内容和同步能够帮助,可是如上所述,它们不是彻底可靠的解决方案。
一个主要的突破口是执行一个简单的通过修改过的查询字符串。查询字符串参数是可见的,而且可能常常意外地缓存(从网络历史记录,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.
在移动设备上安全地存储数据须要适当的技术。 只要有可能“简单地不要存储或缓存数据” 是避免数据在设备上被获取最直接的方式。
尽可能不要存储敏感数据。 减小用户信息存储的方法包括:
若是在设备上存储敏感数据是应用程序需求,则应向数据添加额外的通过验证的第三方加密(例如,SQLCipher)。
经过添加另外一层加密,您能够更好地控制实施和攻击,主要集中在主要的操做系统加密类。 例如,对iOS数据保护类(如今已被入侵)的攻击将没法直接危害您的应用程序。 这种方法具备更复杂的缺点,而且若是实施得很差,实际上会减小安全系数。 若是您不肯定是否包括通过验证的第三方加密库,Apple和Android的通用加密库提供了许多标准加密功能,若是使用得当,能够提供至关安全的加密实现。
下面是一些方法:
每当您加密用户数据时,旨在使用随机生成的主密钥对其进行加密,该主密钥在用户访问数据时也使用用户提供的密码加密。 这将防止数据被容易地恢复,若是攻击者从设备中提取主密钥。 因为Apple的数据保护API和钥匙串中的漏洞数量以及大多数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中内置的数据保护API与复杂的密码短语相结合,能够提供额外的数据保护层,但不像实施其余第三方验证的加密技术那么安全。 为了利用这一点,文件必须被专门标记。(#### 参考最佳实践 6.1 谨慎使用keychain). 任何没有专门使用apple 的文件保护api加密的数据都是未加密存储的
若是Cookie未标记为“Secure”,则不管与主机的会话是否安全,均可能经过不安全的链接进行传输。 换句话说,它能够经过HTTP链接来传输。
此外,因为Cookie没法经过客户端访问(例如,没法使用JavaScript代码段访问),所以在Cookie上设置“HTTPOnly”标志可防止跨站点脚本(XSS)等攻击。
Set-Cookie头应该使用“Secure”和“HTTPOnly”设置。 这些设置应适用于本机和/或网络应用程序的全部Cookie。
许多应用程序没有正确验证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攻击。
若是没法对处理高度敏感数据的任何应用程序功能实施证书锁定,请实施正确的证书验证,该证书验证由两部分组成:
将证书固定到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证书方案。
一个方案是使用NSURLSession
或AFNetworking
类在iOS中实现证书锁定。 此实现的其余详细信息能够在apple developer网站上HTTPS Server Trust Evaluation中找到。
此外,一个开源框架的TrustKit能够帮助开发人员更容易地在iOS中部署公钥锁定。
使用这种形式的中间人攻击,攻击者能够经过透明地劫持网络上的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(而且包括其余功能).
大多数移动设备具备在制造时为了识别目的而分配的惟一ID(也称为通用惟一标识符(UUID))。 例如,iOS设备分配了所谓的惟一设备标识符(UDID)。 惟一标识设备的能力对于获取,管理和保护数据一般很重要。 开发人员迅速采用UUID和UDID进行设备识别,从而使其成为许多系统的安全基础。
不幸的是,这种方法带来了几个隐私和安全问题。 首先,许多在线系统已将设备的UUID链接到单个用户,以便即便在用户未登陆到应用时也可以跨应用进行跟踪。 这种跟踪用户的高级功能已经成为主要的隐私问题。
除此以外,经过UUID识别人员的应用程序有风险暴露设备的之前的全部者的数据到新的全部者。 在一个实例中,在从新设置iPhone以后,即便全部用户数据已被擦除,咱们也能够访问先前用户的在线音乐服务的账户。 这不只是一个隐私问题,它的安全威胁,由于攻击者能够伪造一个UUID。
苹果已经认识到iOS的UDID的隐私和安全风险,并删除了开发者对它的访问。 在UDID不可用的状况下,一些开发人员应用涉及无线网络接口或OpenUDID的MAC地址的其余设备识别方法。 这些方法如今已在系统/ API级别被禁止,而且做为AppStore审查过程的一部分被标记和拒绝。
咱们#### 建议开发人员避免使用任何设备提供的标识符来标识设备,特别是若是它是实施设备身份验证的一部分。 相反,咱们#### 建议在注册,安装或首次执行时建立应用程序惟一的“设备因子”。 而后可能须要将此应用程序惟一的设备因子与用户身份验证结合,以建立会话。 设备因子也能够用做加密例程中的附加因子。
因为它不依赖于可预测的,设备提供的数据,所以开发变得更加困难。 经过利用挑战 - 响应方法,服务器和设备能够在用户认证以前彼此认证。 要得到系统访问,攻击者必须利用这两个因素。 开发人员还能够实如今客户端或服务器端重置设备因素的功能,从而强制对用户和设备进行更严格的从新身份验证。
为了在保留广告功能的同时保护用户隐私,Apple#### 建议使用advertisingIdentifier - 在系统中全部应用程序之间共享的惟一标识符。用户能够随时在设置 - >隐私 - >广告菜单中重置其设备上的advertisingIdentifier。
Android和iOS能够使用GPS准确地肯定位置。 处理此GPS数据是一个隐私问题,若是攻击者知道其当前或过去的位置,可能会使用户容易受到其余攻击。 本地蓝牙、NFC 、RFID标签的信息也可能泄漏地理位置信息。
此外,访问图库图片的应用程序也能够是用户的隐私问题,它能够经过检查时间戳而且假设图片是用户本身拍摄的图片来抓取存储在其中的GPS位置(若是有的话)。
考虑使用和避免存储GPS数据的影响。 为了更好的隐私,尽可能使用最粗粒度的位置服务。 除非必要,不要记录或存储GPS信息。 虽然在某些应用中使用GPS可能颇有用,但不多须要记录和存储数据。 避免这种状况防止了许多隐私和安全问题。 GPS定位信息一般在iOS上的locationd缓存和Android上的各类缓存之间缓存一段时间。 一些应用程序自动使用GPS。 一个例子是一般对图像进行地理标记的相机。 若是这是一个问题,请确保从图像中剥离EXIF数据。
在须要安全的地点工做时,请记住,GPS数据可能会报告给Apple和Google服务器,以提升准确性。 Android和iOS设备都能捕获范围内附近接入点的信息,不管设备是否链接到它们。 不要在将在安全位置或其附近运行的应用程序中激活GPS,其坐标或无线网络拓扑不该报告给供应商。 除此以外,攻击者能够使用单个接入点的硬件地址的知识来模拟安全无线环境并从苹果或Google返回环境的GPS坐标。
移动设备常常丢失或被盗,攻击者能够利用活动的会话来访问敏感数据,执行事务或在设备全部者的账户上执行侦察。 此外,若是没有适当的会话超时,应用可能容易受到中间人攻击的数据拦截。
任什么时候间应用程序不使用超过5分钟,终止活动会话,将用户重定向到登陆屏幕,确保没有应用程序数据可见,并要求用户从新输入登陆凭据以访问 应用程序。
超时后,还要丢弃和清除与用户数据相关联的全部内存,包括用于解密数据的任何主密钥
此外,确保客户端和服务器端都发生会话超时,以减轻攻击者修改本地超时机制。
弱或不存在的身份验证能够授予攻击者对应用程序的未经受权的访问权限。
密码不该该是简单的。 最好要求(若是不是至少支持)复杂密码,包括至少六个字母数字字符的长度(更多字符老是更强)。 做为登陆过程的一部分,要求选择一个秘密词或图标(不是用户本身建立的)能够帮助保护用户的账户,以防他们重复使用密码,并防止密码被从其余数据猜出。
在某些状况下,用户名和密码不能为移动应用程序提供足够的安全性。 当涉及敏感数据或事务时,实施双因素身份验证。 在用户每次登陆时都进行二次身份验证可能不大合适,但能够按时间间隔或在访问所选功能时使用。 能够考虑逐步认证方法以提供对非事务性区域的正常访问,但须要针对敏感功能进行第二层认证。
加强认证的选项包括:
对于最高级别的安全性,使用一次性密码,要求用户不只拥有正确的凭据,并且还要包括一次性密码的物理令牌。
iOS开发人员常常将应用程序设置存储在plist文件中,在某些状况下可能会受到损害。 相似地,Android开发人员一般将设置存储在共享首选项XML文件或SQLite数据库中,这些数据库在默认状况下未加密,能够使用root权限读取或甚至修改,或使用备份。
尽量将设置编译为代码。 经过plist文件在iOS上配置应用程序没有什么好处,由于更改必须绑定并部署为新的应用程序。 相反,若是将配置包含在应用程序代码中,攻击者须要更多的时间和技能才能修改。 除非先加密不要在字典或其余文件中存储任何关键设置。 理想状况下,使用由用户提供的密码加密的主密钥或用户登陆系统时远程提供的密钥加密全部配置文件。
许多app在各类UI显示中存储完整的账号。
鉴于移动应用在公共场所的普遍使用,显示部分号码(例如***9881)能够帮助确保此信息的最大隐私。 除非须要在设备上存储完整的数字,不然存储部分隐藏的数字。 一般,账号用于查询服务器端账户数据; 该数据能够容易地从存储器中被盗取,或者在某些状况下被操纵以处理用户不该该具备访问权限的账户。 #### 建议将令牌(token)分配给每一个账户,并提供给客户端,而不是账号。 这些令牌(不该由用户推断)具备到实际账户的服务器端映射。 若是应用程序数据被盗,用户的账号不会被暴露,攻击者必须首先肯定映射回账户的令牌,而不能直接查询账号。
在iOS中,若是你意识到
> - (BOOL)textField:(UITextField *)textField > shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
做为文本字段的委托的一部分,您能够更改输入文本的可见性。
与网络浏览器不一样,移动设备一般不会公开应用程序是否使用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位加密
即便数据是从您的应用程序生成的,这些数据也可能被拦截和操纵。 这可能包括致使应用程序崩溃(生成关键崩溃日志),缓冲区溢出,SQL注入和其余攻击的攻击。 经过实现UITextFieldDelegate中的方法并利用上面的#### 建议,这能够很容易地在iOS中执行。
与正确的Web应用程序安全性同样,客户端的全部输入都必须被视为不受信任。 服务必须完全过滤和验证应用程序和用户的输入。 适当的清理包括在发送以前和接收期间的全部用户输入。
在Android或iOS设备上执行数据备份还能够备份存储在应用程序的私人目录中的敏感信息。
默认状况下,Android应用程序的Manifest文件中的allowBackup
的值为true
。 这将产生一个Android备份文件(backup.ab),包括设备文件系统上的应用程序私有目录中包含的全部子目录和文件。 所以,显式声明allowBackup
标志为false
。
在对安装了特定应用程序的设备执行iTunes备份时,备份将包括设备文件系统上包含在该应用程序的私人目录中的全部子目录(“Caches”子目录除外)和文件。 所以,请避免将任何敏感数据以明文存储在应用程序的私有目录或子目录中的任何文件或文件夹中。
数据能够在不少地方被捕获,尽管许可能是无心的。 开发人员常常忽略一些能够存储数据的方式,包括日志/调试文件,cookie,网络历史记录,网页缓存,属性列表,文件和SQLite数据库。 在移动设备上安全地存储数据须要适当的技术。 只要有可能“简单地不要存储或缓存数据” 是避免数据在设备上被获取最直接的方式。
阻止HTTP高速缓存。 开发人员能够将iOS和Android配置为不缓存网络数据,特别是HTTPS流量。 在iOS中,查看实现NSURLConnection委托并禁用newCachedResponse。 此外,咱们#### 建议采起步骤,以免缓存任何Web进程(如注册)的URL历史记录和页面数据。 HTTP缓存头在此重要,并在Web服务器上配置。 HTTP协议在响应标头中支持“no-store”指令,指示浏览器它不得存储响应或引起它的请求的任何部分。 对于Web应用程序,HTML表单输入能够使用autocomplete = off设置指示浏览器不缓存值。 应用程序启动后,能够经过对设备数据进行读取检查来验证是否已经缓存了数据。
有不少用于跟踪用户使用状况并收集iOS和Android的崩溃日志的framework,这些framework都是开发的有用工具,但重要的是要在开发人员的足够调试信息和攻击者的信息减小之间找到平衡。
若是应用程序崩溃,生成的日志能够为攻击者提供有价值的信息。
确保已发布的应用程序构建时没有任何警告,并通过完全测试,以免崩溃。 这确定老是目标,值得一提的是因为崩溃日志的价值。 考虑禁用iOS的NSAssert。 若是断言失败,此设置将致使应用程序当即崩溃。 它更加优雅地处理失败的断言比崩溃和生成崩溃日志。 此外,避免经过网络以纯文本发送崩溃日志。
使用安全的开发工具,如clang-analyzer,coverity,ASAN和其余linting实用程序,以肯定全部可能的操做,能够使应用程序崩溃或错误功能。
此外,若是应用程序被混淆和剥离,开发人员将须要保留一个地址到符号数据库,以便在崩溃日志中恢复有意义的回溯,使攻击者的生活更加困难,由于在函数中缺少可理解的名称。
在iOS上,当用户启用“保存此用户ID”功能时,用户名将缓存在CredentialsManager对象中。 在运行时,用户名在任何类型的身份验证以前加载到内存中,从而容许恶意进程拦截用户名。
很难同时向用户提供保存的用户名的便利,以及避免经过不安全的存储或在运行时的潜在拦截来泄露用户名。 虽然不像密码那么敏感,可是用户名是应该保护的私人数据。 提供具备更高安全性的高速缓存用户名选项的一种潜在方法是存储掩蔽的用户名而不是实际的用户名,而且在认证中将用户名值替换为哈希值。 能够建立哈希值,包括在注册时存储的惟一设备令牌。 使用散列和设备令牌的进程的好处是,实际用户名不存储在本地或不加保护地加载到内存中,而且复制到另外一个设备或在网络上使用的值将不够。 恶意用户必须发现更多信息才能成功窃取认证用户名。
调试日志一般设计为用于检测和纠正应用程序中的缺陷。 这些日志可能泄漏敏感信息,这可能有助于攻击者建立更强大的攻击。
开发人员应考虑调试日志在生产环境中可能出现的风险。 通常来讲,咱们#### 建议他们在生产中禁用。
一般由应用程序用于输出调试消息的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. > ;
iOS记录用户键入的内容,以便提供自定义自动更正和表单完成等功能,但敏感数据也能够存储。 几乎每一个非数字字按照键入的顺序缓存在键盘缓存中; 缓存的内容超出了应用程序的管理权限,所以应用程序没法从缓存中删除数据。
对任何敏感信息(而不只仅是密码字段)禁用自动更正功能。 因为键盘缓存敏感信息,它能够恢复。 对于UITextField,查看将autocorrectionType属性设置为UITextAutocorrectionTypeNo以禁用缓存。 随着SDK更新,这些设置可能会随时间而变化,以确保其获得充分研究。 添加企业策略以按期清除键盘字典。 这能够由最终用户经过简单地去设置应用程序,通用>重置>重置键盘字典。
Android包含用户字典,用户输入的字词能够保存以供未来自动更正。 此用户字典可用于没有特殊权限的任何应用程序。 为了提升安全性,请考虑实施自定义键盘(以及可能的PIN输入),这能够禁用缓存,并提供针对恶意软件的额外保护。
iOS和Android都支持复制/粘贴。 敏感数据能够以明文形式存储,恢复或者能够从剪贴板修改,而无论数据的源是否最初被加密。 若是在用户复制它时它是纯文本,当其余应用程序访问剪贴板时,它将是明文。
例如,它遵循严格的规则,这意味着应用程序不能读取或写入剪贴板,而且使用它的惟一方式是经过用户交互,作长按来弹出剪贴板菜单。
在适当状况下,禁用用于处理敏感数据的区域的复制/粘贴。 取消复制选项有助于避免数据暴露。 在Android上,剪贴板能够由任何应用程序访问,所以#### 建议使用通过适当配置的内容提供程序来传输复杂的敏感数据。 在iOS上,考虑用户是否须要在应用程序或系统范围内复制/粘贴数据,并选择适当类型的粘贴板。
此外,值得注意的是在取得内容后清除剪贴板,以免其余应用程序读取它们并泄漏用户正在作什么。
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。
CSRF (Cross-site Request Forgery) 依赖于已知或可预测的表单值和登陆的浏览器会话。
每一个表单提交应包含一个令牌,该令牌在表单中或在用户会话的开头载入。 在接收POST请求时,在服务器上检查此令牌,以确保是用户启动它。 此功能已经被主流Web平台提供,仅须要少许定制表单开发。
iOS提供了安全数据存储的钥匙串。 然而,在几种状况下,钥匙串可能被泄密并随后被解密。
在iOS的全部版本,包括iOS 7,若是攻击者能够访问加密的iTunes备份,钥匙串可能部分破解。 因为iOS在建立iTunes备份时从新加密钥匙串条目的机制,因此当iTunes备份可用且备份加密的密码已知时,钥匙串能够被部分解密。 可是,未加密的iTunes备份不容许解密钥匙串项。
若是越狱已应用于设备,则钥匙串访问控制将无效。 在越狱的状况下,在设备上运行的任何应用程序均可能读取每一个其余应用程序的钥匙串项目。
最后,对于存在BootROM漏洞的旧设备(例如,iPhone 4),该钥匙串可能被具备对该设备的物理访问的攻击者获取。
当在钥匙串中存储数据时,使用最严格的保护类(由kSecAttrAccessible
属性定义)仍然容许您的应用程序正常工做。 例如,若是您的应用程序不是设计为在后台运行,请使用kSecAttrAccessibleWhenUnlocked
或kSecAttrAccessibleWhenUnlockedThisDeviceOnly
。
要防止经过iTunes备份暴露钥匙串项目,请在实用的状况下使用ThisDeviceOnly 选项。
最后,对于高度敏感的数据,考虑使用应用程序级加密来加强钥匙串提供的保护。 例如,依靠用户输入密码在应用程序内进行认证,而后使用该密码在将数据存储到密钥链以前对数据进行加密。
为了提供界面中的视觉过渡,iOS会将屏幕截图做为图像存储在设备NAND闪存的文件系统部分中。 当应用程序暂停(而不是终止)时,当按下Home按钮或电话呼叫或其余事件暂时挂起应用程序时,会发生这种状况。 这些图像一般能够包含用户和应用程序数据。 在一个已发布的案例中,它们包含用户的全名,DOB,地址,雇主和信用评分。
要保护敏感数据,请使用API配置或代码阻止应用程序快照的缓存。
当applicationDidEnterBackground:方法运行结束时,iOS将采用应用程序用户界面的快照,而且它用于转换动画并存储在文件系统中。 应该重载此方法,而且在返回以前应删除用户界面中的全部敏感信息。 这样快照就不会包含它们。
这个最佳实践涵盖了三个iOS代码实现,帮助开发人员减轻缓冲区溢出攻击他们的应用程序的风险:自动引用计数(ARC),地址空间布局随机化(ASLR)和堆栈崩溃保护。
自动引用计数(ARC)是一种内存管理系统,在编译时自动处理对象的引用计数,而不是将此任务留给开发人员。 此功能是与iOS 5一块儿引入的,但它能够反向运行到之前的版本,由于操做是在编译时执行的。
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
默认状况下,iOS的NSURLRequest
将响应缓存在Cache.db文件中。 为了防止这种不安全的行为,开发人员必须明确禁用缓存。
开发人员能够设置NSURLRequest
的cachePolicy
属性来禁用HTTP(S)请求和响应的缓存。 禁用缓存的许多方法之一以下面的代码片断所示 (从Stack Overflow转载 NSURLConnection Delegate Returns Null :
(NSCachedURLResponse)connection:(NSURLConnection)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { return nil; }
开发人员还能够在下面引用的Apple Developer文章“Understanding cache access”中找到禁用缓存HTTP(S)请求和响应的其余方法。
iOS 9中的新功能,ATS(App Transport Security)有助于确保应用程序和任何后端服务器之间的安全链接。 默认状况下,当应用程序与iOS 9.0 SDK或更高版本连接时,它会启用。 启用ATS后,HTTP链接将强制使用HTTPS(TLS v1.2),而且使用不安全HTTP链接的任未尝试都将失败。
应用ATS包括几个方案:
对于在iOS 9.0或更高版本上运行的应用程序,最佳作法是经过连接到iOS 9.0或更高版本的SDK来启用ATS,而且不要将NSAllowsArbitraryLoads
键设置为“Yes”或“True”。 苹果目前容许开发人员为任何不能实施TLS的域设置为例外。 能够使用NSExceptionAllowsInsecureHTTPLoads
或NSThirdPartyExceptionAllowsInsecureHTTPLoads
键来设置例外。 重要的是要注意,从2017年1月开始,苹果将要求开发人员对应用程序中声明的任何异常(在App Store审核期间)提供合理的理由。 不然,全部通讯必须使用ATS。
Touch ID一般被用来容许用户在不输入密码的状况下对其设备进行认证和解锁。 一些开发人员还使用Touch ID,容许用户使用存储的设备指纹对其应用进行身份验证。
当开发人员在其应用中应用Touch ID时,一般会采用如下两种方式之一:
LAContextevaluatePolicy:localizedReason:reply
方法来实现当使用Touch ID进行身份验证时,将应用程序的密钥存储在钥匙串中,并将ACL分配给该项目。 使用此方法,iOS将在读取和将Keychain项目返回到应用程序以前执行用户存在检查。 开发人员能够在Apple网站上找到示例代码: https://developer.apple.com/library/ios/samplecode/KeychainTouchID/Listings/KeychainTouchID_AAPLKeychainTestsViewController_m.html.
全部人可读的文件能够做为程序的一个向量,泄漏敏感信息。 全部人可写文件可能会暴露您的应用程序,让攻击者经过覆盖您的应用从存储读取的数据影响其行为。 示例包括设置文件和存储的登陆信息。
不要建立具备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,
Intents用于组件间信令,并且能够被用来作如下事情:
不正确的实现可能致使数据泄露,受限功能被调用和程序流被操纵。
经过Intents访问的组件能够是公共的或私有的。 默认值取决于Intents过滤器,很容易错误地容许组件被公开。 能够在应用程序的Manifest中将组件设置为android:exported = false
,以防止出现这种状况。
默认状况下,在清单中声明的公共组件是开放的,因此任何应用程序均可以访问它们。 若是组件不须要被全部其余应用程序访问,请考虑设置对清单中声明的组件的权限。
公共组件接收的数据不可信任,必须仔细检查。
一般在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>
若是在发送广播Intent时未设置权限,则任何非特权应用程序均可以接收Intent,除非它有明确的目标。
攻击者能够经过如下方式利用没有任何设置权限的Intent:
使用权限来保护应用程序中的Intents。 请记住,当经过广播Intent向第三方组件发送信息时,该组件可能已被恶意安装替换。
PendingIntent容许应用程序将Intent传递给第二个应用程序,而后能够执行该Intent,就像它是原始应用程序同样(即具备相同的权限)。
使用PendingIntent,应用程序能够将Intent传递给第二个应用程序,而后能够执行该Intent,就像它是原始应用程序同样(即具备相同的权限)。 这容许其余应用程序回调到原始应用程序的私有组件。 外部应用程序,若是是恶意的,可能会尝试影响目标 和/或 数据/完整性。
使用PendingIntents做为对私有BroadcastReceivers或broadcast activities的延迟回调,并在基本Intent中显式指定组件名称。
服务一般用于后台处理。 与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>
当activity由另外一个使用广播Intent的应用程序启动时,Intent中传递的数据能够由恶意应用程序读取。
当另外一个应用程序经过发送广播Intent启动activity时,恶意应用程序能够读取Intent中包含的数据。 恶意应用程序还能够读取应用程序的最近Intent列表。 例如,若是应用程序调用并传递URL到Android Web浏览器,攻击者能够嗅探该URL。
不要在使用广播Intent的应用之间传递敏感数据。 而使用显式Intent。
Content providers 容许应用程序使用URI寻址方案和关系数据库模型共享数据。 它们也能够用于经过URI方案访问文件。
Content providers 能够声明权限和单独的读写访问。 除非绝对必要,不然不要给内容提供者写访问权限。 除非必需,请确保设置权限,以便非特权应用程序没法读取ContentProvider
实例。
将访问限制为操做所需的最低限度。 例如,要与向某个联系人发送该邮件的其余应用程序共享即时消息,只共享该单个邮件,而不是全部即时消息。 内容提供商中的记录级委派功能容许共享特定记录或文件,而不共享整个数据库。 一旦外部应用程序返回到原始应用程序,表明结束。
将传递给内容提供者的参数视为不受信任的输入,而且不在没有防御的状况下直接在SQL查询中使用它们。 没有防御的SQL代码能够经过内容提供者请求发送。 若是SQL代码包含在查询中,它能够返回数据或向攻击者提供控制权。
基于文件名传递给提供者的文件的内容提供者应该确保路径遍历被过滤掉。 例如,若是攻击者在请求中包含`../../../ file',它可能致使程序读取并返回攻击者在其余状况下不能访问的文件中的数据。 应用程序。 此外,请注意,攻击者建立的如下符号连接可能具备相似的结果。
WebView能够引入一些安全问题,应该当心地使用。 目前已经发现了使用addJavscriptInterface API所产生的许多可利用的漏洞。
若是不须要JavaScript和Plugin支持,请禁用。 虽然默认状况下都禁用它们,但最佳作法须要明确将其设置为禁用。 禁用本地文件访问。 这限制了对应用程序的资源和资产目录的访问,并减轻了来自网页的攻击,该网页试图得到对其余本地可访问文件的访问。
禁止从第三方主机加载内容。 这可能很难在应用程序内实现。 可是,开发人员能够覆盖shouldOverrideUrlLoading和shouldInterceptRequest来拦截,检查和验证从WebView中发起的大多数请求。 开发者还能够考虑经过使用URI类来检查URI的组件以确保其匹配已批准资源的列表中的条目来实现白名单方案。
示例代码 https://gist.github.com/scottyab/6f51bbd82a0ffb08ac7a
远程支票存款应用程序容许一我的用手机的相机拍摄支票的图片,而后将图像发送到他们的金融机构存入他们的账户。
使用远程支票存款应用程序,一我的能够用手机的相机拍照支票,而后将图像发送到他们的金融机构存入他们的账户。 许多这些应用程序将保留检查图像(或其一部分)在移动设备的NAND内存中,即便它被删除。
不要在设备上使用非易失性存储器传输支票图像,支票图像可能会残留。 一个可能的替代方案是:
此方法将只维护易失性内存中的图像,并防止支票图像在非易失性存储器中的缓存。
特别是使用Android Camera类,能够使用方法takePicture指定当使用Camera.PictureCallback
接口生成.jpg时的回调。 特别是,咱们对方法“public void onPictureTaken(byte [] bytes,Camera camera)感兴趣。
使用这种技术,能够使用“bytes”数组内容,其中包含RAM中的照片。
远程支票存款应用程序容许人们使用他们的设备拍摄支票的照片,并将其发送到其金融机构以存入账户。
Android将应用程序屏幕保留在内存中,而且多任务能够致使将整个应用程序保留在内存中(即便用户注销其账户)。 这容许发现或窃取设备的攻击者直接导航到保留的屏幕,其能够包括做为GUI的一部分的敏感用户数据。 例如,若是用户退出银行应用,但不退出或关闭应用,则能够保留显示交易活动的屏幕,而且攻击者能够看到该屏幕。
为了解决这个问题,开发人员有三个常见的选择:
当用户注销时,彻底退出应用程序。 虽然违反Android设计原则退出您本身的应用程序,它是更安全,由于退出应用程序将销毁任何保留的GUI屏幕。
任什么时候间启动活动或访问屏幕时,请执行检查以肯定用户是否处于已登陆状态。 若是用户未登陆,则显示登陆屏幕。
在离开屏幕或注销以前,在GUI屏幕上清除数据。
APK应使用未过时的证书正确签名。
下面是一个生成私钥的Keytool命令的示例:
$ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
Web服务器上的某些设置能够提升安全性。 Web服务器上一般被忽视的漏洞是信息泄露。 信息泄露可能致使严重的问题,由于每个信息攻击者均可以从服务器得到使攻击更容易。
减小信息泄露的一种简单方法是禁用详细错误。 详细错误在开发环境中颇有用,但在生产环境中可能会泄露关键信息,例如Web框架信息和版本。 攻击者能够使用此信息来定位旨在利用实施特定缺陷的攻击。
减小信息泄露的另外一个简单方法是返回服务器响应中的最少许的信息。 默认状况下,Apache将返回其版本号,运行它的操做系统和插件运行。 经过更改配置文件中的单个行,能够减小到只公开服务器正在运行Apache而不影响功能。
能够大大提升安全性的服务器中的一个配置更改是更改任何默认目录。 攻击者常常在Internet上搜索具备“low-hanging fruit”的网站,例如默认登陆,容易猜到的管理界面,以及简单的“隐藏”目录命名方案。 这是一个好的策略,模糊处理须要网络访问的服务器上的任何敏感网页的位置。
管理或其余限制区域不该公开网络访问,除非绝对必要,而且必须抵抗暴力攻击。 HTTP认证或没有锁定保护的表单认证能够(而且将)被暴力攻击。
许多Web服务器容许较低的加密设置,例如很是弱的导出级40位加密。 实施强密码套件以保护用于建立共享密钥,在客户端和服务器之间加密消息,以及生成消息哈希和签名以确保这些消息的完整性的信息。 还要确保禁用弱协议。
确保正确安装和配置SSL证书以实现最高的加密。 若是可能,仅启用强密码(128位及以上)。
TLSv1已超过10年,而且在2009年被发现很容易受到“从新协商攻击”。
避免使用弱cipher,例如:
避免弱协议,例如:
如需获取有关如何安全地设计和配置应用程序的传输层安全性的更多信息。#### 参考 OWASP Transport Layer Protection Cheat Sheet for more information about how to securely design and configure transport layer security for an app.
大多数应用程序都经过可能容易受到攻击的Cookie来维护用户的会话。
Web语言(例如Java,.NET)提供会话管理,这是通过良好开发和安全测试的。 使服务器软件保持最新的安全补丁。 开发您本身的会话管理更有风险,只有在适当的专业知识。 确保会话cookie的大小足够。 短的或可预测的会话cookie使攻击者可能预测,高压或对会话执行其余攻击。 在会话配置中使用高安全性设置。
已经被攻破的服务器有可能拦截用户凭据并对应用用户发起其余攻击。
通常来讲,生产Web服务器必须通过完全测试和防护恶意攻击。 生产服务器软件应更新到最新版本,并加固以防止有关服务器软件和接口的信息泄露。
身份验证表单不该反映用户名是否存在。 若是攻击者有一个方法来肯定有效的用户名,他们有一个起点的暴力攻击和网络钓鱼攻击。 经过向客户端提供“无效的用户/密码组合”和“没有找到此类用户名”事件的相同响应,阻止用户名收集。 全部的交换敏感数据的登陆表单和表单/页面都应该实现并须要HTTPS。 Web服务器不该容许没有SSL的客户端链接用于此类资源。 关闭详细错误,删除任何遗留的没必要要的站点或页面,并持续强化Web资源以防潜在的攻击。
用于内部使用的资源(例如管理员登陆表单)常用可能被暴力破解的身份验证。 例如无锁定的HTTP或表单认证。 管理或其余内部资源的泄露可能致使普遍的数据丢失和其余损害。
这种资源应该被阻止外部访问。 任何不须要公共互联网访问的资源都应该使用防火墙规则和网络分段进行限制。 若是登陆页面,管理区域或其余资源是外部可访问的,它就会被恶意用户发现和暴力攻击。
参考文档:https://github.com/nowsecure/secure-mobile-development/
待定
本文档经过介绍常见开发过程当中因协议配置错误或代码漏洞而致使安全的问题去避免相似现象的发生。 常见开发过程当中的协议:
数据库做为数据管理的平台,它的安全性首先由系统的内部安全和网络安全两部分来决定。对于系统管理员来讲,首先要保证系统自己的安全,在安装MySQL数据库时,须要对基础环境进行较好的配置。
缺省安装的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的管理员名称是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');
绝对不要做为使用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_safe
或mysql.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中,提供对本地文件的读取,使用的是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是linux中的一种系统高级保护手段,它的创建会将其与主系统几乎彻底隔离,也就是说,一旦遭到什么问题,也不会危及到正在运行的主系统。这是一个很是有效的办法,特别是在配置网络服务程序的时候。
若是不打算让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选项影响安全:
--allow-suspicious-udfs
--local-infile[={0|1}]
--old-passwords
(OBSOLETE) --safe-show-database
--safe-user-create
--secure-auth
--skip-grant-tables
--skip-name-resolve
--skip-networking
--skip-show-database
转载:https://www.securitypaper.org