1 ESAPI使用
OWASP ESAPI (OWASP企业级安全API)是一个自由开源的web程序安全控制库,它可以让程序员利用此安全API规避很多安全风险。目前已经发布的有java版本及其他语言如C、C++、.Net、PHP等,ESAPI对常见安全漏洞提供了对应的安全控制实现方法。如下表:
http://www.cnblogs.com/fishou/p/4177491.html
注意事项:这个组件使用不是直接引入jar就ok,初始化时要读两个配置文件ESAPI.properties和validation.properties,这两个配置文件不一定能在安装指南所示的目录中找到,不过你解压搜索一下dist目录,应该是可以找到的,把这两个文件扔进src目录中就Ok了,还需要加入log4j-1.2.15.jar或者其他版本的jar
2 SQL预编译
1、JAVA基础之 PreparedStatement
预编译的sql语句存储在PreparedStatement对象中,所以PreparedStatement的执行效率要高于Statement 使用占位符(?)的方式,使得重复的结构重复的语句不用重复的编写
例如:Statement下如果我想插入两条记录
stmt.addBatch("insert into t_student values ('11','小明','男')");
stmt.addBatch("insert into t_student values ('22','小明2','男')");
PreparedStatement下 使用占位符,只需要录入占位符的数据即可
con.prepareStatement("insert into t_student values (?,?,?)"); 具体见例子
2、这样的好处:
1、防止重复编写多个结构类似的sql语句
2、没有拼接字符串的烦恼
3、防止sql注入(拼接字符串 会带来sql注入问题)
4、sql语句预编译在PreparedStatement对象中,性能好
//executeUpdate 执行插入语句
//executeQuery执行查询语句,返回结果集
3 链接参数防篡改方案
1、RSA算法应用防篡改
非对称加密算法是一种密码体制,其加密算法和解密算法使用不同的密钥:一个是公钥,一个是私钥。
RSA算法:
以下方案采用数字签名方式对链接参数进行加密和校验,具体描述如下:
1、签名算法:RSA
2、签名哈希算法:SHA1
原理:
利用RSA非对称算法可进行数字签名的特性,发送方将参数按一定规则拼接后作为明文,使用私钥对其进行签名,连同对应公钥的模和指数发送给发送方,由发送方生成公钥进行签名验证。因为RSA为非对称加密,即加密与解密不能使用同一个密钥,因此,即使第三方得到公钥的模和指数并生成了公钥,也无法生成有效的数字签名,从而保证参数的可靠性。
使用URL参数中的模和指数生成公钥进行验证会存在以下问题,因此公钥的模和指数不再通过URL参数进行传递,改为其他方式交给签名验证方(文件、邮件或其他方式),并由验证方根据情况选择保存公钥的模和指数或保存由模和指数生成的公钥。发送方需要保存私钥,并在私钥发生变更时,及时与验证方联系并提供新的公钥指数和模。
特殊情况:
假设:
篡改者知道原文的拼接规则以及签名的哈希算法:
当使用URL参数传递公钥的模和指数时,如果篡改者自行生成私钥并对篡改后的参数进行签名,此时验证方仍可通过验证,但数据已被篡改。
具体实施方案:
1、将链接中包含的参数按字母顺序排序后拼接作为签名原文,如:?voter=xxx&a=v1&z=v2&y=v3,
则按照参数字母升序排序后为[a, voter, y, z]
签名原文 = v1xxxv3v2
2、查询系统生产RSA密钥对,并使用私钥对签名原文进行签名
3、查询系统
4、查询系统将签名生成的密文(sign)作为附加参数拼接在原始链接参数中发送给爱调研,其中密文为url safe base64编码
5、爱调研方获取密文,并对其他参数排序拼接后,进行签名验证。
关于Python公钥生成及验签的建议及注意项:
Python可使用PyCrypto库根据公钥的模和指数生成公钥;
Python对BASE64编码的签名密文解码时,由于是url safe变种,需要先对原串补全缺失的等号(=);
Python验证签名时,需要计算原文的哈希值,此处应使用SHA1;
以下为Python验证签名的Demo,已通过测试:
(注:例程中公钥的模和指数只为程序能够运行,并不是实际使用的指数和模)
#!-*- coding:utf-8 -*-
from Crypto.Hash import SHA
import base64
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as pk
'''
公钥的模、指数、原文、签名后的密文均由Java生成,本例程模拟的为Python接收参数后进行公钥生成、签名验证的步骤,仅供参考
'''
#公钥的模
n = long("94635777235927625703157301553538346231276514986752610066658700719609707671971889796774318957359510214646122527678003718279843100950934353410986842648664484164907852661775024451148638430565141735377213753212139322555486609834922571859496777261504160615423932182870284059318995343895605146945291557397942294833",10)
#公钥指数
e = long("65537",10)
#原文
data = "abcd1234"
#签名后的密文,
sign = "aOKzW34jU0A-VpTsN9F0-C578HPP-1KVLG3CB5XkDzcs2UAO25XQdx9WwYYGMB8dQIDf2hXgzBxc3A8vaFx_hxV4nor_104stEzaObDKsBiUS7vmLzSDigEfL_MTJMRZYsfhbr6MmsuUPdBoHr4z8-C2d8Rl1Sis5Qp1gdq-8ao"
#根据模和指数生成公钥
pubKey = RSA.construct(tup=(n, e)).publickey()
#Python Base64的bug,需要将urlsafe的base64串补全等号,否则无法解析
paddingCount = 4 - len(sign) % 4
if paddingCount > 0:
sign += b'='*paddingCount
signn = base64.urlsafe_b64decode(sign)
#用公钥创建签名验证器
verifier = pk.new(pubKey)
#验证签名
if verifier.verify(SHA.new(data), signn):
print "The signature is authentic."
else:
print "The signature is not authentic."
2、MD5算法应用防篡改
为了提高传输过程参数的防篡改性,必须使用签名参数sig。
签名参数sig生成的步骤:
第1步: 把需要包含在签名中的参数按key升序排序。 具体需要包含哪些参数,见各接口参数说明中关于sig的描述。
第2步: 把排序后的key和它对应的value拼接成一个字符串。
第3步: 把分配给商家的appkey拼接在第2步得到的字符串后面。
第4步: 计算第3步字符串的md5值,使用md5值的16进制字符串作为sig的内容。
示例
原始请求信息
应用ID:appid=600
商户appkey=4dd1af55f7f140ac8827518472af3d87
原始请求:
http://openapi.pengyou.qq.com/v2/r/qzone/qz_get_balance?
appid=600&
appkey=HWAffC6MK1DQ5ztm&
appname=app600&
device=0&
openid=00000000000000000000000000000009&
openkey=1111111111446414117133E71111111111C50AE4A7111111&
ts=1300444184&
userip=112.90.139.30
查询时需要包含在签名里的参数:appid,appkey,appname,openid,openkey,ts。
下面开始生成sig:
第1步: 将参数按key升序排序得到:
appid,appkey,appname,openid,openkey,ts
第2步: 把所有键值对字符串拼接成一个字符串得到:
appid600appkeyHWAffC6MK1DQ5ztmappnameapp600openid00000000000000000000000000000009
openkey1111111111446414117133E71111111111C50AE4A7111111ts1300444184
第3步: 把分配给商家的appkey拼接在第2步骤得到的字符串后面得到:
appid600appkeyHWAffC6MK1DQ5ztmappnameapp600openid00000000000000000000000000000009
openkey1111111111446414117133E71111111111C50AE4A7111111ts1300444184
HWAffC6MK1DQ5ztm
第4步: 计算第3步骤字符串的md5值
md5
(appid600appkeyHWAffC6MK1DQ5ztmappnameapp600openid00000000000000000000000000000009
openkey1111111111446414117133E71111111111C50AE4A7111111ts1300444184
HWAffC6MK1DQ5ztm)
得到:
sig=eddf71eaa362748beda2cca96a4786ff
加签名后的请求串
http://openapi.pengyou.qq.com/v2/r/qzone/qz_get_balance?
appid=600&
appkey=HWAffC6MK1DQ5ztm&
appname=app600&
device=0&
openid=00000000000000000000000000000009&
openkey=1111111111446414117133E71111111111C50AE4A7111111&
ts=1300444184&
userip=112.90.139.30&
sig=4d7c026abf32d84cc8afb9f36d365e28
4 统一错误页面
java.lang.Exception类是Java中所有异常的直接或间接父类。即Exception类是所有异常的根类
<error-page>
<--指定异常类型>
<exception-type>java.lang.Exception</exception-type>
<location>/errorPage.jsp</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/error/400.jsp</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/error/403.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error/error.jsp</location>
</error-page>
<error-page>
//空指针异常
<exception-type>java.lang.NullPointerException</exception-type>
<location>/error/error.jsp</location>
</error-page>
<error-page>
//servlet异常
<exception-type>javax.servlet.ServletException</exception-type>
<location>/error/error.jsp</location>
</error-page>