目录
第一章 安全性考虑
1、安全哲学,三个安全性目标
- 保护敏感数据:常见的敏感数据有信用卡号,账户密码,医疗历史记录等。计算机安全的一个规则就是:如果破解系统的代价比系统本身的内容价值高,那么系统是安全的
- 控制对资源的访问:访问存储好的敏感数据时必须处于严格的控制之下,一般来说可以通过用户名和口令控制,但是还可以采取数字认证的方法。访问控制的用途是抵制拒绝服务(DOS)的攻击
- 记录日志:日志能够帮助查明危害的来源,某些情况下甚至能恢复丢失的数据,不过在一些系统中,任务日志也被视为敏感数据被保护
2、安全策略
安全策略指的是在一个组织里,允许什么行为,禁止什么行为的一个表述,它可以对应用程序中应该保护的内容做一个界定,也可以帮助我们识别哪些是系统中的重要资源。
定义:可接受的公共资源的许可的使用方式、远程访问的策略以及用户特权。
3、安全需求:
两个方面:风险评估和数据公开,任何系统都是建立在实践的基础上,做出一些妥协不可避免
4、风险评估
风险评估即对你的应用系统的安全风险进行描述。让你明白哪些资源需要被保护,它们的价值有多大?
明确几个问题:
- 数据被破坏,损失有多大
- 数据对其他人而言,价值有多大
- 数据被损坏,对其他人的影响有多大
- 其他人通过什么途径了解到我的应用程序
5、数据公开
仔细检查,想想哪些地方的数据最容易被窃取或者故意篡改,对组织中最薄弱点进行彻底和客观的评估
一件比较重要的事情是:把系统分成多个子系统,并且把数据访问权限只分配给需要访问的人(角色)
划分好角色后,就需要保护系统中潜在的安全弱点,例如
- 直接连接到互联网的地方
- 物理位置处于公共或半公共的场所
- 存储敏感数据或宝贵数据的装置,比如数据库
对外公开的任何机器必须使用最新版本的软件和应用程序。程序中任何时候都可能存在安全漏洞,如果不及时给系统打补丁,系统就会留下一个安全漏洞
6、在安全需求和可用性之间做平衡
在对系统完整性保护和用户体验之间,我们需要做出平衡,一个好的系统,甚至可以让用户完全感受不到安全系统的存在
防止数据被篡改或破坏的最常用的应急方法是定期地对数据进行备份,其次就是通过日志系统,通过访问的日志,找出哪些数据被篡改。
7、操作系统
如果你的操作系统有漏洞,攻击者可以改变组成应用程序的类文件,从而替换应用程序
需要经常给操作系统打上最新的补丁,不要运行无关的网络服务,如FTP和telnet
8、IP安全
IPV4不支持加密
IPV6有个部分叫IPsec,它支持对IP层网络通讯的保护,IPsec对应用程序来说是完全透明的,根据配置的不同,通讯可以自动加密和认证,性能也比SSL优越
9、虚拟专用网
虚拟专用网简称VPN,是一种在无安全的通讯媒介上提供安全通讯通道的方法
典型用法就是透明地连接两个不同的网络,代理服务器能够透明底完成加密和解密的要求,除了中间有个额外的网络安全层意外,终端用户感受不到任何区别
10、防火墙
防火墙对两点之间的网络流量进行限制,主要用于把内部网络和Internet隔离开来
目前的防火墙主要有以下几种形式
- 源/目的型防火墙——基于数据包的源、目的来过滤数据包
- 网关型防火墙——通常用于双网卡系统,一块连接国际互联网,一块用于内部网,主要作用是限制国际互联网对内部网的访问
- 客户端防火墙——主要用于限制员工访问特定网站,同时也保护公司不受攻击
- 服务器端防火墙——有时也称为禁区(DMZ),创建一块安全区域,保护不受国际网和内部网的攻击
11、入侵检测工具
最常用的是数据包嗅探器,他可以浏览网上的所有的数据流量,通过他可以看到是否有异常行为发生。
另外一种方式是检测是否有人未经许可就修改你机器上的文件,这类工具应用最广泛的就是Tripwire
12、Java安全
Java的Applet在默认情况下是在沙袋中运行的,仅仅允许CPU和某个代码片访问,沙袋能够阻止有恶意的Apple从硬盘读取你的口令。
Java没有指针,不能够对内存进行直接访问
13、字节验证器
通过运行前对字节码进行校验,虚拟机可以为确保正在运行的代码不做某些危险的操作
14、类装入器
类装入器主要是往虚拟机装入指定的类,与安全管理器和访问控制器一起工作
15、Java和密码学
Java核心库和标准扩展中提供了简易的方法来使用密码
第二章 安全的Java代码
1、可访问性
访问修饰符:
- public:公有的——任何类都可以访问这个实体
- protected:保护——类本身可以访问,他的子类,以及在同一个包内部所有其它类都可以访问
- package-private:包私有——如果没有声明任何可访问性,这就是默认的可访问性。类本身,同一个包内部的其他类可以访问
- private:私有的——只有类本身可以访问
如果方法需要在某个类内部用到,那应该把他设置为私有方法
如果一个变量只需要在方法内部用到且不需要永久保存,那就应该声明局部变量
局部变量尽可能为私有的,然后通过Get,Set方法提供访问
2、可访问性和安全性
Java中,运行Applet时,字节码验证器默认打开,但在运行应用程序时则没有打开,如果需要打开,则在实例化虚拟机时加-verify选项
java -verify Perpetrator
默认情况下,一个对象可以访问其他对象的私有成员变量,如果希望代码遵循安全模式,你应该一直把-verify打开,以便强制保证Java中的可访问性规则有效
3、Final类、方法和变量
Final关键字用来把一个类、方法或变量标识为常量。一个fianal的实体永远保持不变
定义为final的类不能派生子类,fianal的方法不能重载,final的变量不能重新赋值,但是可以进行修改,例如final修饰的hash表可以调用put()方法
4、静态字段
静态变量属于某个类,一个给定的静态变量只有一个实例
如果一个类的静态变量被声明为公有,则应该定义为final,从而使别的类不能修改他
如果你需要一个被外部访问,同时可以修改的静态变量,那就提供get(),set()方法进行访问
5、清除敏感信息
如果对象保存了敏感信息,但是又不需要使用,那就需要对这个对象进行处理,一般情况是时通过碎片收集程序进行回收,以备不时之需,但是也可能会被内存清除掉,也有可能在你退出程序后读取到你的内存。所以在处理完敏感信息后,最好将他清除
6、常量类
Java有很多类时不变的,因为不可变,所以不能被清除,尽量不要用String和BigInteger存储需要清理为0的数据。需要把口令和密钥存储在数组中,并在数组的类中提供清除方法
7、保存和返回对象和数组
在方法有返回值/对象时,我们应该给数组做一个拷贝,即使返回的数组被修改了,原始对象中内部状态不会被修改掉
数组可以使用System.arraycopy(arry1,开始的位置,arry2,开始的位置,复制的长度)
如果保存的不是数组,则可以用clone()方法,如果没有clone()方法,则需要创建一个新的对象并通过get()和set()方法从原始对象中把数值拷贝过去
如果存储的是String这种常量,则不需要担心被修改
8、序列化
序列化用于存储传输和传送对象的状态,主要用于在不通虚拟机之间传送对象或者用于在同一个虚拟机的不同对象实例之间保存对象状态,一旦对象被序列化,他就处于Java安全体系外
默认状态下,序列化可以把所有内部变量保存到他提供的任何输出流,子类可以实现Serizlizable方法进行序列化
9、Transient关键字
Transient关键字用于指定一个变量的值对不同的实例各不相同,如果把变量申明为transient,那么对象将不能被序列化
10、验证合法性、
反序列化时,有必要验证对象字段合法性。在序列化和反序列化时,流将调用readObject()和writeObject()方法,通过这两个方法验证合法性
11、加密
先把对象序列化,然后对序列化后的对象加密
使用java.crypto.SealedObject方法加密
对称加密和非对称加密的区别:https://www.jianshu.com/p/b078282653b3
12、对包的保护
默认情况下,可以把用恶意垒加入到已有包中,从而获得包中的权限。如何避免这种情况:
- 封装JAR文件
- 在java.security中加入package.definition项
- 限制包的访问权限
13、封装jar文件
封装jar指在JVM中,所有jar包中定义的类都必须来自这个jar中。不能将外部类加入到jar包中
实现方法:在MANIF-EST.MF文件中对每个想密封的包加入一个Name和Sealed项
14、使用Package.definition项
在java.security中加入package.definition项,限制在包中创建新的类
java.security文件定义了虚拟机的安全默认权限
15、限制包的访问权限
可以对访问一个包的类进行限制
限制:在java.security中加入:package.access=包名称
授权:在java.policy中加:RuntimePermission("accessClassInPackage.包名");
16、特权代码
特权代码可以放你对以前没有权限调用的一些小代码片段赋予临时的权限。
注意事项:
- 传递参数要小心:如果特权代码有输入参数,要注意它们往往造成你不想要的结果。
- 特权代码尽可能短:代码越短,可被攻击的路径越短,也更容易保证代码不被分析
- 尽可能不使用特权代码:如果不是必要,尽量不去使用它
17、本地方法
从Java内部调用的本地方法要受Java安全管理器的限制,但是一旦本地方法执行,他就可以做底层操作系统支持的任何事情。本地方法可以访问和修改Java对象,不受安全管理器检查
第三章:密码和Java中的加密服务概述
3.1、加密
加密的意思是把信息隐藏起来,除了适当的人以外其他人不能阅读的实用方法
3.1.1、对称加密算法
分组密码(block cipher):一次对一个数据块进行加密,一般是64位加密,有些是128位。分组密码可以创建流密码,适合单条信息加密
流密码(stream cipher):针对数据流进行加密,一次一位或一个字节。适合稳定的信息流,如socket
密钥长度越长,对称加密的破解难度越大。密钥长度一般在40比特到448比特不等,想要获得密钥,只能通过枚举一个一个试
密钥空间:加密算法使用的所有可能的密钥的集合
常用的对称加密算法:
- 数据加密标准DES和TripleDES:DES密钥长度为56位,但是相对安全长度还有点短,TripleDES也因此诞生了;TripleDES实际上是用不同的密钥把DES使用三遍,从加密→解密→加密,密钥可能有两到三个,总的密钥位数可能在56到112或168位,因为长度增加,所以可能更安全
- Blowfish:是一个分组密码,是目前最快最安全的算法,密钥长度为448位
- RAC4:是一个流算法,大部分SSL都是由它实现,以保证TCP/IP连接安全,密钥长度为40到128位
- AES:高级加密标准,密钥长度位128、192和256位,分组大小位128、195和256位
3.1.2、对称加密的局限
加密算法都采用了:在加密和解密消息时使用相同的密钥。若果想要发送一段加密信息,双方都必须知道密钥
秘密密钥加密问题:密钥本身也要秘密保存,丢失了密钥将危及任何密文的安全
3.1.3、非对称加密
非对称密钥加密也称为公钥加密,它解决了发送信息前先建立约定好的密钥问题,非对称加密拥有一个公钥一个私钥
如果你想使用非对称加密,那么只需要把私钥保护好,公钥可以给任何人加密,但只有拥有私钥的人才能解密
3.1.4、非对称加密算法
- RSA:RSA是最通用和最有名的非对称加密算法,它的特点是公钥和私钥都可以加密数据,用其中一个就能解密另一个数据
- EIGamal:免费的公钥算法
3.1.5、密钥长度
一个1024位的非对称加密的强度和128位对称加密的强度相当
3.1.6、公钥的弱点
对于某些形式的攻击,获取来加密的密钥任然有些问题
比起对称加密,非对称加密的速度很慢,对称加密的速度比非对称加密快1000倍
3.1.7、会话密钥加密
会话密钥加密是把对称加密和非对称加密一起使用,它可以使用对称密钥对消息进行加密,使用公钥对密钥本身进行加密
不用非对称的方法对整个消息加密所节约的CPU时间将对程序可用性非常关键
会话密钥加密的协议有:
- PGP:PGP全称"Pretty Good Privacy",它的保密性能非常好隐含它的效率很高。主要用于Email加密,组合了RSA、TripleDES等算法
- S/MIME:全称Secure/Multipurpose Internet Mail Extension 即安全/多目的互联网邮件扩展,由RSA发明,用于保护电子邮件的安全
- SSL和TLS:Secure Socket Layer 和 Transport Layer Security ,主要目的是保护TCP/IP的安全,也可以用于Internet通信安全服务
3.1.8密钥协定
密钥协定是另一种不需要交换密钥而发送安全消息的方法,也依赖于一个公钥一个私钥。但密钥是用来创建共享秘密的,共享秘密用来创建会话密钥(会话密钥永远不会发出去)
3.1.9、Diffie-Hellman密钥协定算法
它是第一个拥有独立的公钥-私钥对算法。只有公钥加密才能够在预先不定义一些共享秘密的情况下交换消息,公钥加密的方法使人们可以公开地交换密钥
3.1.10、小结
加密算法的强度取决于两个方面:
- 使用什么算法
- 密钥的长度
一般来说,128位密钥能够满足对称算法的大部分需要了、1024位密钥能够满足非对称算法的大部分需要了
3.2、其他数据隐藏的方法
3.2.1、Steganography
Steganography指把消息隐藏在其他消息里面的方法,例如把每段话的第一个字母拼接成一条消息。虽然他不是加密算法,但是它可以传送秘密信息
大结尾是一种格式,他把最有意义的位放在第一个字节——最左边,如果最有意义的位放在最右边则称为little-endian
3.2.2、编码
一个编码是一个单词或词组,代表了另一个单词或词组,且不能通过上下文理解含义
编码也有对称加密那样的依赖性:事先必须交换一些秘密信息,如密码字的含义
3.2.3、一次性填充
这是最安全的加密方式,它要求创建一个与要传送的消息长度完全一样的复杂的随机密码。且密钥只被使用一次,并且是完全随机的,否则就有可能被破解
一次填充的问题:发送消息前,密钥必须完全秘密地传送,必须实现建立秘密通讯;一次填充需要的密钥长度比对称算法长得多。且密钥永远不重用
3.2.4、椭圆曲线加密(ECC)
椭圆曲线加密是公钥加密法的变种。它比RSA更快更安全,ECC是基于椭圆曲线的对数问题,没有因式分解的弱点
3.3、认证
认证就是确定一条消息或一个用户的可靠进程。它可以用来验证用户的身份,或者确定一条消息没有被篡改
3.3.1、消息摘要
消息摘要也被称为杂凑(hash),用于证明数据没有被更改或损坏
有些杂凑算法允许字母进行移位,因此杂凑算法比较复杂,也更难被破解
把相同的消息输入一个给定的算法,应该是总是产生相同的杂凑
消息摘要同在只有16到20个字节,消息摘要也可以检查一份文档和另一份文档是否相同,因为两份文档产生相同的消息摘要的情况几乎等于零,同理也可以验证下载的文件是否出现损坏
消息摘要的另一个作用就是密钥认证:操作系统的密码都是以散杂凑版本的形式存储,而不是密码原本。登陆时,密码先进性散杂凑处理,然后与存储的杂数据进行比较
常用的消息摘要算法:
- MD5:MD5是常用的128位的消息摘要,大量用于口令存储机制,包括许多现代UNIX系统
- SHA和SHA-1:SHA指的是安全杂凑算法,SHA-1是SHA的修改版,他是一个160位的杂凑算法,某种意义上比MD5更安全
3.3.2、消息认证码
消息认证码(MAC)指的是带有密钥的消息摘要。由于双方都拥有密钥,接收者可以验证消息是否正确
同样的消息和密钥产生相同的消息认证码
MAC通常用于以及建立了安全连接的场合,如果数据在传输过程中有损坏或者被修改,检查MAC就可以发现问题
实际中最常见的MAC是杂凑消息验证码(HMACs),MAC的算法有:
- HmacMD5:使用了带密钥的MD5摘要算法的消息认证码
- HmcaSHA1:类似于HmacMD5,只是使用的算法是SHA-1
3.3.3、数字签名
主要功能是:对数据源的保证和证明数据没有被修改
数字签名采用一个人的私钥计算出来,然后公钥去校验。产生数字签名需要一个公钥私钥对,公钥可以随意发部,私钥必须秘密保存
数字签名算法(DSA),它是NIST的数字签名标准(DSS)的一部分,DSS提供了没有加密的数字签名,DSS不用私钥对消息摘要进行加密,而是采用用一种不同的数学算法从消息摘要和私钥中产生签名。校验过程从技术上讲也不是解密,但从数字签名上来说,DSA和RSA的功能基本一样
3.3.4、数字证书
数字证书主要指的是一个团体签署的证明,证明另一个团体的公钥属于他们颁发的
证书主要是指一个人的公钥和一些由权威机构的私钥校验过的鉴定信息
证书也可以包含除公钥和数字签名外的其他信息,证书也用来签署和加密电子邮件,这样的证书包含了用户的电子邮件地址。SSL证书包含了证书主任的主机名称
用处示例:一个浏览器连接到服务器上,发现它的URL与认证书中的主机名不匹配,就会警告用户有问题
3.3.5、证书链
证书链指的是用另一个把一个证书作为公钥的的私钥签名一个证书的方法
它可以让一个证书授权机构去为另一个证书授权机构做认证
证书链中的顶级证书必须是自我签名的,任何人都能够自签自己的证书,从而成一个认证权威
Versign之所以被承认是因为它已经应用于商业中了,IE在安装时就包含了Versign证书,这样在建立SSL连接时就能自动接收这些证书
3.3.6、密码分析学
密码分析学是分析和破解密码的学问,由于加密的强度取决于密钥的强度,而不同的算法有不同的密钥长度,因此对门对于密码分析的抵抗力是不一样的
3.3.7、除穷举法以外的其他方法
除了对密钥用穷举法进行破解外,一个常见的攻击方式是针对密钥产生方式进行分析。
理想的随机数产生源需要特殊的硬件。如果没有某种随机信息作为种子,任何算法都不能得到完全的随机性
除了穷举法和分析随机数产生方法以外,还有其他密码分析法。虽然这些方法各不相同,但根据他们收集与密钥相关的信息的方式,可以把他们分为不同种类
加密算法对密码分析的抵抗力也很重要
3.3.8、密钥管理
密钥管理是一个安全系统主要考虑的问题,一般使用智能卡解决密钥管理问题,智能卡存储了私钥和证书,用来对信息进行加密解密,但是密钥永远不会被公开或者拷贝出来,它具有防修改功能,通常是一个快速清除函数
3.3.9、协议
协议,指的就是如何决定每个算法的使用顺序和使用方式,以得到最大的安全性
密码学并不能被简单地认为是一个涉及加密和认证的数学算法问题,必须把它放在整个组织去考虑
要阻止协议攻击,最好的方法就是使用广为使用的协议,例如SSL和S/MIME,它们都是由专家设计,同时也是经过其他专家评估和测试过的
3.4、Java密码架构和Java密码扩展
Java密码架构(JCA)和Java密码扩展(JCE)的设计目的就是为Java提供与实现无关的加密函数API,JCA是java2的运行环境一部分,JCE是对没包含在JDK中的JCA的扩展,JCE为JCA增加了简单的加密和解密API
3.4.1、Java密码架构
Java密码架构是由java.security包和子包中的一系列类组成的,这些类提供了像消息摘要和数字签名这样的API函数
几个比较重要的类:
- MessageDigest
- Signature
- KeyPareGenerator
- KeyFactory
- CertificateFactory
- KeyStore
- CertificateFactory
- KeyStroe
- AlgorithmParametes
- AlgorithmParameterGenerator
- SecureRandom
每个类的使用方式都相似,都要通过工厂模式。在调用JCA中的类时,new关键字很少用到
3.4.2、加密服务提供者
加密服务提供者通常简称为提供者(provider),指的是许多不同算法实现的集合
它包含如下算法的实现
- MD5消息摘要
- SHA-1消息摘要
- DSA数字签名的签署和证书
- DSA密钥对的产生
- DSA密钥的转换
- X.509认证的创建
- DSA算法参数
- DSA算法参数的产生
- 所有者随机数产生
- 所有者密钥存储实现
JDK1.3给出了RSJCA提供者,类名是com.sun.rsajca.Provider,它提供了如下算法:
- RSA密钥对产生
- RSA密钥转换
- 使用了SHA-1或MD5消息摘要的RSA签名
获取JCA类的例程的时候,可以自己指定提供者,或者让虚拟机自己根据底层引擎决定使用哪个提供者,然后JCA将把对类的调用委托给内部引擎,引擎中对这个类提供了服务提供者接口(SPI)。这种设计基于册罗模式,它通常用于面向对象编程
3.4.3、Java密码扩展(JCE)
JCE在设计上和JCA相似,都是用工厂模式创建类的例程
JCE本身指的是javax.crypto包和它包中的子类,它包含如下接口
- Ciper
- KeyAreement
- KeyGeneratro
- Mac
- SecretKey
- SecretKeyFactory
要在Java中实际使用加密,必须有执行这些函数的适当的类。所以你的虚拟机必须安装了JCE和提供者
3.4.4、安装JCE
现在已经有很多JCE的实现,他们通常都带有一个实现了不同算法的提供者,如列表所示
提供者 | 证书 | 注释 |
Bouncy Castle | Open Source Apache-style | 最完全,可以从JCE提供者免费获得 |
BeeJCE | Open Source LPGL | 一个好的JCE实现,但不支持RSA |
OpenJCE | Open Source Apache-style | 没有应用前景,但支持JCE 1.2 API |
SunJCE | Java license | 提供参考实现,但却不能与其他JCE协调工作。也不支持RSA |
推荐使用Bouncy Castle,支持的算法如下:
- Blowfish
- DES
- DESede
- IDEA
- RC2
- RC4
- RC6
- RC5
- Rjndael
- Skipjack
- Twofish
- RSA
- MD5
- MD5
- RipeMD160
- SHA-1
- Diffle-Hellman
- DSA
SunJCE支持的算法有
- DES
- TripleDES
- Blowfish
- Diffie-Hellman
不推荐下载和使用SunJCE,因为一旦安装了它的代码,其他的提供者就不能使用
安装JCE和他的提供者的步骤
- 下载提供者
- 把JAR文件拷贝到适当位置
- 配置java.security文件
- 测试安装好的程序