Shiro 重放攻击登录验证(密码加密加盐)

1、加盐:

        简单来说就是一组安全随机数。它在特定的时候,加入到密码中(一般来说是加密后的密码)。从而使密码变得更有味道(从单一简单化到复杂化),更安全。

2、对密码进行加密

        盐值是一个随机数。当用户注册一个简单密码时,系统会同时生成这样一个salt,与该用户对应,保存到数据库中。

加密操作步骤:

(1)注册、修改密码时,前台将 888888加密后的pwd1,传入后台

(2)后台拿到pwd1以后,生成一个相应的随机数 salt。将pwd1与salt拼接并再次加密,生成pwd2

(3)后台将pwd2和salt 一并存储到数据库中。

登录验证步骤:

(1)当用户每次输入用户名密码后,将密码加密生成pwd1'后,传入后台。

(2)后台拿到pwd1'后,根据用户名id拿到对应的盐值。与盐值拼接加密后,生成pwd2'。

(3)然后判断pwd2'与数据库中的pwd2是否一致即可。

这里有两点需要注意:

(1)密码在前后台的加密方式可以采用不同的形式

(2)盐值的拼接不一定非要拼接到最后,也可以放在前边、插在中间、甚至拆开或者倒序拼接。

3、重放攻击

        重放攻击 (Replay Attacks) 又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。重放攻击可以由发起者,也可以由拦截并重发该数据的敌方进行。攻击者利用网络监听或者其他方式盗取认证凭据,之后再把它重新发给认证服务器。重放攻击在任何网络通过程中都可能发生,是计算机世界黑客常用的攻击方式之一。

解决思路之一(加随机数):

(1)每次登陆时,可以生成一个随机数(一个动态生成的salt),这个salt在前后台各自保存一份。

(2)当用户名输入完密码pwd后。前台会进行 f1(pwd)加密,然后用动态生成的salt加密的密钥,然后再次加密。即: pwd1=f2(f1(pwd)+salt)。之后前台就把这个pwd1发送到后台。(注意由于动态salt每次都会改变,所以pwd1每次也会改变)

(3)后台拿到数据后,在用动态salt(即f2()使用可逆的机密算法)解密,然后再拼接固化salt接着再次加密,最后与数据库对比即可

 

一、重放攻击测试

 实现密码加密使用: HashedCredentialsMatcher

    <!-- 配置自己域realm实现  -->
<!--     <bean id="shiroRealm" class="cn.jq.ssm.service.shiro.ShiroRealm"></bean> -->
    <bean id="shiroRealm" class="cn.jq.ssm.service.shiro.ShiroRealm">
    	<property name="credentialsMatcher">
    		<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    			<property name="hashAlgorithmName" value="MD5"></property>
    			<property name="hashIterations" value="1024"></property>
    			<property name="storedCredentialsHexEncoded" value="true"></property>
    		</bean>
    	</property>
    </bean>

1、访问项目路径到登录页面

	@GetMapping(value= {"/","/login"})
	public String login(Model model, HttpSession session) {
		//生成一组16位随机数
		int hashCodeValue = UUID.randomUUID().hashCode();
		if(hashCodeValue < 0) hashCodeValue = -hashCodeValue;
		String uuidSalt = String.format("%016d",hashCodeValue);//左边补0,16位,进制(d,x)
		
		//把uuid盐值,同时保存在前后端
		model.addAttribute("uuidSalt", uuidSalt);
		session.setAttribute("uuidSalt", uuidSalt);
		return "login";
	}
	<h4>登录页面</h4>		
	<input type="hidden" id="uuidSalt" value="${uuidSalt }"><br>
	<br>
	<form action="${pageContext.request.contextPath }/login" method="post" id="loginForm" >
		用户名:<input type="text" name="username"><br>
		密    码:<input type="password" name="pazzword" id="pazzword"><br>
		<button type="button" onclick="checkForm()">提交</button>
	</form> 
	
<script src="${pageContext.request.contextPath }/static/jquery/jquery-1.12.4.js"></script>
<script src="${pageContext.request.contextPath }/static/md5/md5.js"></script>
<script src="${pageContext.request.contextPath }/static/aes/aes.js"></script>
<script src="${pageContext.request.contextPath }/static/aes/pad-zeropadding-min.js"></script>
<script type="text/javascript">
	function checkForm(){
		var uuidSalt = $("#uuidSalt").val();
		var pazzword = $("#pazzword").val();
		
		pazzword = hex_md5(pazzword);		
		//加密后的密码进行第二次加密(可解密)
		pazzword = encrypt(pazzword,uuidSalt,uuidSalt);
		$("#pazzword").val(pazzword);
		
		//alert(pazzword.length);
		if(pazzword.length == 44){
			$("#loginForm").submit();
		} 
	}
	
    function encrypt(data,key,iv) { //key,iv:16位的字符串
    	var key1  = CryptoJS.enc.Latin1.parse(key);
        var iv1   = CryptoJS.enc.Latin1.parse(iv);
        return CryptoJS.AES.encrypt(data, key1, {iv:iv1,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding}).toString();
    }
</script>

2、输入账号和密码进行登录

   用户密码通过 MD5 加密,然后在进行 JQSalt 盐值加盐后保存到数据库 。所以 ShiroRealm 验证也是用相同盐值。

  1)action 登录方法

	@PostMapping("/login")
	public String login(User user, HttpSession session) {
		//使用 shiro 登录验证
		//1 认证的核心组件:获取 Subject 对象
		Subject subject = SecurityUtils.getSubject();
		
		//将密码进行 aes 解密
		String key = (String) session.getAttribute("uuidSalt");
		String iv = (String) session.getAttribute("uuidSalt");
		try {
			user.setPazzword(AesEncryptUtil.desEncrypt(user.getPazzword(), key, iv));
			//密码解码成功后盐值失效
			session.removeAttribute("uuidSalt");
		} catch (Exception e1) {
			e1.printStackTrace();
			return "loginError";
		}
		
		//2 将登陆表单封装成 token 对象
		UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPazzword());
		try {
			//3 让 shiro 框架进行登录验证:
			subject.login(token);
		} catch (Exception e) {
			e.printStackTrace();
			return "loginError";
		}
		return "redirect:/admin/index";
	}

2)自定义 Realm 的实现验证

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import cn.jq.ssm.dao.UserMapper;
import cn.jq.ssm.model.User;

public class ShiroRealm extends AuthenticatingRealm{

	@Autowired
	private UserMapper userMapper;

	/**
	 *  登录的验证实现方法
	 * @param token
	 * @return
	 * @throws AuthenticationException
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken token2 = (UsernamePasswordToken) token;
		String username = token2.getUsername();
		User user = userMapper.getUserByUsername(username);
		if(user == null) {
			throw new UnknownAccountException("用户名或密码有误!");
		}
		if(user.getStatus() == 0) {
			throw new UnknownAccountException("用户名已被禁用,请联系系统管理员!");
		}
		
		/**
		 * principals: 可以使用户名,或d登录用户的对象
		 * hashedCredentials: 从数据库中获取的密码
		 * credentialsSalt:密码加密的盐值
		 * RealmName:  类名(ShiroRealm)
		 */
		ByteSource credentialsSalt = ByteSource.Util.bytes("JQSalt");
		AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPazzword(), credentialsSalt, getName());
		return info;
	}	

}

简单 重放攻击实现 end ~

密码加密加盐

密码从明文变成密文,可以通过 SimpleHash 这个类来实现

	@PostMapping("addUser")
	public String addUser(User user) {
		//获取加密工具
		ByteSource pzwd1 = ByteSource.Util.bytes(user.getPazzword());
		String saltValue = "JQSalt";
		/**
		 * algorithmName: 加密方式
		 * source: 要加密的内容
		 * salt:盐值
		 * hashIterations: 加密次数
		 */
		Object pzwd2 = new SimpleHash("MD5", pzwd1, saltValue, 1024);
		user.setPazzword(pzwd2.toString());
		userService.add(user);
		return "userlist";
	}

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/89401473