0.前言
用户登入注册的流程其实很简单(不考虑安全需求的流程如下图),但作为用户安全的第一道防线,需要严格的安全措施。市面上常用的攻击手段有sql注入,csrf,xss等。
一.避免sql注入攻击
SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
1.1从url注入:
请求 URL:
POST
http://localhost:8080/login?username=张三&password=123456
1.2构建不合法请求
select * from user where username='张三' and password='' or 1=1 #’
1..3避免方法
、
使用参数化查询:避免直接将用户输入嵌入 SQL 查询中,而是使用预处理语句或参数化查询。 ORM(对象关系映射)功能,能够自动处理 SQL 查询并防止注入。
在java开发中就严格遵循这总,DAO(数据操纵层)-----JDBC-------数据源。DAO 层封装数据库操作,可以确保所有 SQL 查询都使用预处理语句,从而有效防止 SQL 注入攻击
编写数据类
public class Attribute {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String value;
}
数据接口类--数据库增删改查的函数已经配置好了
@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
void flush();
<S extends T> S saveAndFlush(S entity);
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
/** @deprecated */
@Deprecated
default void deleteInBatch(Iterable<T> entities) {
this.deleteAllInBatch(entities);
}
void deleteAllInBatch(Iterable<T> entities);
void deleteAllByIdInBatch(Iterable<ID> ids);
void deleteAllInBatch();
/** @deprecated */
@Deprecated
T getOne(ID id);
/** @deprecated */
@Deprecated
T getById(ID id);
T getReferenceById(ID id);
<S extends T> List<S> findAll(Example<S> example);
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
二.CSRF攻击
攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己以前认证过的站点并运行一些操作(如发邮件,发消息,甚至财产操作(如转账和购买商品))。因为浏览器之前认证过,所以被访问的站点会觉得这是真正的用户操作而去运行。
2.1攻击流程
① 用户C登录正常网站A,浏览器 保存cookie
② 诱导用户C未关闭浏览器的情况 下,访问恶意/漏洞网站B
③ 恶意/漏洞B网站B获取cookie
④ 恶意/漏洞B以用户C权限向正常 网站A发送请求
2.2验证请求来源、使用验证码、token(令牌)
csrf是基于cookie,所以不使用cookie的方式验证
下面用令牌token举例
token流程,用户登入成功后会通过账号密码生成token(注意这个token存在时间限制),以后发每次访问使用token放入请求头然后才访问。这种方式被称为jwt(json web token)
给出jwtfilter代码
// JWT工具类
package com.example.userlogin.util;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key";
//生成token方法
//Jwts.builder(): 创建一个新的 JWT 构建器。
//.setSubject(username): 设置 JWT 的主题部分为提供的用户名。
//.setExpiration(new Date(System.currentTimeMillis() + 86400000)): 设置 JWT 的过期时间为当前时间加上 24 小时(86400000 毫秒)。
//.signWith(SignatureAlgorithm.HS256, SECRET_KEY): 使用 HS256 签名算法和 SECRET_KEY 对 JWT 进行签名,以保证其完整性和真实性。
//.compact(): 构建并返回最终的 JWT 字符串。
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}
public static String getUsername(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
}
}
三.爆破攻击
爆破攻击要从两个角度防护,首先用户密码本身不能太简单,否则容易直接被尝试。其次在系统会遭受彩虹表攻击。
密码通常不会以明文形式存储在数据库中,而是将其哈希后的值存储在数据库中。哈希是一种单向函数,将输入数据转换为固定长度的哈希值。例如,常用的哈希函数如MD5、SHA-1、SHA-256等。因此,当用户登录时,系统会将其提供的密码进行哈希,然后将其与数据库中存储的哈希值进行比较。
彩虹表攻击的基本原理如下:
预计算阶段:攻击者使用密码哈希函数和彩虹表生成算法,预先计算大量的哈希值和对应的明文密码,并将结果存储在一个庞大的彩虹表中。这个表可以包含数亿条数据项。
破解阶段:当攻击者获得了存储在目标系统中的密码哈希值后,他们可以在彩虹表中查找匹配项。如果哈希值在彩虹表中找到了匹配,攻击者就可以通过查找对应的明文密码来获取用户的原始密码。
3.1密码校验
不允许太简单的用户密码,给出校验代码
public static boolean validatePassword(String password) {
// 验证密码长度
if (password.length() < 8) {
return false;
}
// 验证字符组合
boolean hasUppercase = false;
boolean hasLowercase = false;
boolean hasNumber = false;
boolean hasSpecialChar = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) {
hasUppercase = true;
} else if (Character.isLowerCase(c)) {
hasLowercase = true;
} else if (Character.isDigit(c)) {
hasNumber = true;
} else {
hasSpecialChar = true;
}
}
// 验证密码是否满足要求
return hasUppercase && hasLowercase && hasNumber && hasSpecialChar;
}
3.2加盐hash
通过加入随机数盐salt,与密码结合后,使得很多彩虹表攻击失效
public static String generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
public static String hashPassword(String password, String salt) {
try {
byte[] saltBytes = Base64.getDecoder().decode(salt);
byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
byte[] saltedPasswordBytes = new byte[passwordBytes.length + saltBytes.length];
System.arraycopy(passwordBytes, 0, saltedPasswordBytes, 0, passwordBytes.length);
System.arraycopy(saltBytes, 0, saltedPasswordBytes, passwordBytes.length, saltBytes.length);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashedBytes = md.digest(saltedPasswordBytes);
return Base64.getEncoder().encodeToString(hashedBytes);
四.xss攻击
虽然xss常用来攻击javascript
如注入到html的填入框
<form action=“”method=“”> <input type=“file”/><input type=“submit”value=“”/> </form>
但是有些比如传入图片等这些还是会进入到后端
xssfilter
public class XssValidator implements ConstraintValidator<Xss, String>
{
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
{
if (StringUtils.isBlank(value))
{
return true;
}
return !containsHtml(value);
}
public static boolean containsHtml(String value)
{
StringBuilder sHtml = new StringBuilder();
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
while (matcher.find())
{
sHtml.append(matcher.group());
}
return pattern.matcher(sHtml).matches();
}
}