目录:
1、生成安全随机数
2、密码安全存储
3、文件上传
4、SQL注入
一、生成安全随机数
用于生成会话sessionid、防CSRF时的token、以及其他场景下的veritycode。
如下代码:生成安全随机数
package net.xdclass.demo; import java.security.SecureRandom; /** * 使用安全随机数生成器生成24byte的随机数 */ public class OtherTest { public static StringBuilder createToken(){ SecureRandom random = new SecureRandom(); byte bytes[] = new byte[24]; random.nextBytes(bytes); StringBuilder token = new StringBuilder(); String hv; for (int i=0;i<bytes.length;i++){ int j = bytes[i] & 0xFF; hv = Integer.toHexString(j); if (hv.length()==1){ hv = "0"+hv; } token.append(hv); } return token; } public static void main(String[] args){ System.out.println(createToken()); System.out.println(createToken().length()); } }
二、密码安全存储
- 禁止使用MD5、SHA1不安全哈希算法
- 可以使用SHA256+salt,salt随机生成,salt长度不低于8byte,每个用户的salt不一样,salt存数据库
- 强烈建议使用PBKDF2秘钥推到函数,安全存储用户密码
如下代码:PBKDF2存储密码DEMO
package net.xdclass.demo; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.sql.Array; /** * 使用PBKDF2生成不可逆的密码 * DK = PBKDF2(PRF, Password, Salt, c, dkLen) * * PRF是一个伪随机函数,例如HASH_HMAC函数,它会输出长度为hLen的结果。 * * Password是用来生成密钥的原文密码。 * * Salt是一个加密用的盐值。 * * c是进行重复计算的次数。 * * dkLen是期望得到的密钥的长度。 * * DK是最后产生的密钥。 */ public class OtherTest { public static StringBuilder savePassword(String password)throws NoSuchAlgorithmException, InvalidKeySpecException { SecureRandom random = new SecureRandom(); byte salt[] = new byte[24]; random.nextBytes(salt); int iterCount=5000;//迭代次数不低于5000次 char[] charPassword = password.toCharArray(); PBEKeySpec pbeKeySpec = new PBEKeySpec(charPassword,salt,iterCount,256); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] hashpassword = secretKeyFactory.generateSecret(pbeKeySpec).getEncoded(); StringBuilder finalPassword = new StringBuilder(); String hv; for (int i=0;i<hashpassword.length;i++){ int j = hashpassword[i] & 0xFF; hv = Integer.toHexString(j); if (hv.length()==1){ hv = "0"+hv; } finalPassword.append(hv); } return finalPassword; } public static void main(String[] args) throws NoSuchAlgorithmException,InvalidKeySpecException { System.out.println(savePassword("123456")); } }
三、文件上传
- 服务端通过白名单限制上传的文件类型
- 限制文件的大小
- 限制文件保存的目录,禁止执行权限
- 压缩包需要考虑解压后大小、文件名是否包含目录跳转字符
- 对上传的文件重命名,上传的路径禁止返回客户端
如下代码:获取文件类型,并判断文件名是否包含空格DEMO
package net.xdclass.demo; import java.io.File; /** * 获取上传文件的文件类型,并判断文件名是否包含空 */ public class OtherTest { public static void main(String[] args) { //取上传文件的文件名 String path="D:\\MyDocument\\3-java\\2-Code\\demo.java"; File file = new File(path); String fileName = file.getName(); StringBuilder finalFileName = new StringBuilder(); //去除文件名中的空字符 for (int i=0;i<fileName.length();i++){ if ('\u0000' != fileName.charAt(i)){ finalFileName.append(fileName.charAt(i)); } } int i = finalFileName.lastIndexOf("."); String fileExtension = finalFileName.substring(i+1); System.out.println(fileExtension); } }
四、SQL注入
防范SQL注入的方法:
- 预编译
- 对不可信的数据进行处理
- 对不可信的数据进行编码
如下代码:通过预编译的方式防范SQL注入漏洞
1、错误示例,拼接SQL语句,导致存在SQL注入漏洞
package Eleven; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.sql.*; public class OtherTest { public static void main(String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user_info?serverTimezone=UTC", "root", "123456"); Statement stmt = null; ResultSet rsSet = null; String userName = "Eleven' or '1'='1'-- "; //拼接一个用户名,形成万能查询 String userPassword = "123456"; String sqlString = "select * from user where name = '" + userName +"' AND password = '" + userPassword + "'"; // 变量未经处理直接与SQL语句拼接在一起 stmt = conn.createStatement(); rsSet = stmt.executeQuery(sqlString); while(rsSet.next()) { String name = rsSet.getString("name"); String password = rsSet.getString("password"); System.out.println(name+" "+password); //输出结果为数据库所有的用户名密码。 } } }
2、正确示例
package Eleven; import java.sql.*; public class Test { public static void main(String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/user_info?serverTimezone=UTC", "root", "123456"); String userName="Eleven"; String userPwd="123456"; String sql = " SELECT * FROM `user` WHERE name=? and password=? "; PreparedStatement pstate = conn.prepareStatement(sql); pstate.setString(1, userName); pstate.setString(2, userPwd); ResultSet rsSet = pstate.executeQuery(); while(rsSet.next()) { String name = rsSet.getString("name"); String password = rsSet.getString("password"); System.out.println(name+" "+password); } rsSet.close(); pstate.cancel(); conn.close(); } }
五、
六、
七、