【代码调优】Java开发中总结的代码质量优化技巧,springboot企业级开发教程SELECT * FROM db_user WHERE username=‘validuser’ OR ‘1’=‘1’ AND password=’’
同样,攻击者可以为password提供如下字符串。
’ OR ‘1’='1
当其注⼊到命令时,命令就会变成:
SELECT * FROM db_user WHERE username=’’ AND password=’’ OR ‘1’=‘1’
解决思路:
使⽤java.sql.PreparedStatement代替java.sql.Statement,做⼀个预编译,例如,select * from db_user where username=? and password=?,然后向PreparedStatement对象中添加?对应的属性
public void doPrivilegedAction(String username, char[] password) throws SQLException {
Connection connection = getConnection();
if (connection == null) {
// Handle error
}
try {
String pwd = hashPassword(password);
// Ensure that the length of user name is legitimate
if ((username.length() > 8) {
// Handle error
}
String sqlString = “select * from db_user where username=? and password=?”;
PreparedStatement stmt = connection.prepareStatement(sqlString);
stmt.setString(1, username);
stmt.setString(2, pwd);
ResultSet rs = uteQuery();
if (!rs.next()) {
throw new SecurityException(“User name or password incorrect”);
}
try {
connection.close();
} catch (SQLException x) {
// forward to handler
} finally {
// forward to handler
}
}
3、不安全的随机数
Java API中提供了java.util.Random类实现PRNG(),该PRNG是可移植和可重复的,如果两个java.util.Random类的实例使⽤相同的种⼦,会在所有Java实现中⽣成相同的数值序列。
例如:下⾯代码⽚段中,使⽤了java.util.Random类,该类对每⼀个指定的种⼦值⽣成同⼀个序列。
import java.util.Random;
public static void main (String args[]) {
for (int i = 0; i < 10; i++) {
Random random = new Random(123456);
int number = Int(21);
…
}
}
解决思路:
在安全性要求较⾼的应⽤中,应使⽤更安全的随机数⽣成器,如java.security.SecureRandom类。
例如:下⾯代码⽚段中,使⽤java.security.SecureRandom来⽣成更安全的随机数。
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
public static void main (String args[]) {
try {
SecureRandom random = Instance(“SHA1PRNG”);
for (int i = 0; i < 10; i++) {
int number = Int(21);
…
} catch (NoSuchAlgorithmException nsae) {
…
}
}
使⽤java.security.SecureRandom可以确保是真*伪随机数,详见
4、硬编码的密码
程序中采⽤硬编码⽅式处理密码,⼀⽅⾯会降低系统安全性,另⼀⽅⾯不易于程序维护。
例如:下列代码中采⽤硬编码⽅式处理密码。
public class ConnectionConfig{
String url = “localhost”;
String name = “admin”;
String password = “123456”;
…
}
解决思路:
程序中不应对密码进⾏硬编码,可以使⽤配置⽂件或数据库存储的springboot推荐算法
《⼀线⼤⼚Java⾯试题解析+后端开发学习笔记+最新架构讲解视频+实战项⽬源码讲义》
【docs.qq/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
⽅式来存储系统所需的数据;并且录⼊数据时,还可以在对敏感数据做加密处理之后再进⾏数据的录⼊,对于双向的加密算法可以解密后判断;对于单向的加密算法,可以通过对输⼊值的再次加密来判断。
可以构造⼀个属性⽂件⼯具类PropertiesUtil和加解密⼯具类EncryptUtil,把敏感数据加密后存在属性⽂件中,需要的时候再解码出来调⽤。⽐如可以⽤jasypt加密密码。
例如:下列代码中从配置⽂件中获取经过加密的密码值并解密使⽤。
public class ConnectionConfig{
String url = EncryptUtil.(“connection.url”));
String name = EncryptUtil.(“connection.username”));
String password = EncryptUtil.(“connection.password”));
…
}
jasypt详细使⽤可查:
5、SimpleDateFormat的线程不安全
public class DateUtil{
//全局属性 new SimpleDateFormat
private static SimpleDateFormat dateFormatter = new SimpleDateFormat();
/**
格式化时间到毫秒级
*/
public static String longDateFormat(Long time, String dateFormat) {
if (time == null) {
return null;
}
dateFormatter.applyPattern(dateFormat);
return dateFormatter.format(new Date(time));
}
这样⼀个时间⼯具类,在并发场景下,可能会产⽣线程不安全的情况,即某些调⽤者线程会读取到意料之外的⽇期(⾮⾃⼰输⼊),出现了幻读。
下图来⾃⽹络:
写个单元测试看⼀下效果:
public class test{
@Test
public void test1() {
//创建⾃定义线程对象
MyThread mt = new MyThread(“新的线程!”);
//开启新线程
mt.start();
//在主⽅法中执⾏for循环
for (int i = 0; i < 100; i++) {
logger.info(“main线程!” + i + DateUtil.longDateFormat(1692517360211l,“yyyy-MM-dd HH-mm-ss”));
}
}
class MyThread extends Thread {
//定义指定线程名称的构造⽅法
public MyThread(String name) {
//调⽤⽗类的String参数的构造⽅法,指定线程的名称
super(name);
}
重写run⽅法,完成该线程执⾏的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
logger.info(getName()+":正在执⾏!" + i + DateUtil.longDateFormat(1622517360211l,“yyyy-MM-dd HH-mm-ss”));
}
}
}
}
解决思路:
1. 可以把SimpleDateFormat对象的创建放到⽅法内部,当成局部变量来使⽤,这样的好处是逻辑很简单直接,坏处是每次调⽤⽅法都
要创建⼀个SimpleDateFormat对象,浪费堆内存。
SimpleDateFormat dateFormatter = new SimpleDateFormat();
2. 使⽤ThreadLocal本地线程类,创建⼀个只属于每个副本的本地变量,不同线程互不可见从⽽保证线程安全。(推荐)
//使⽤ThreadLocal代替原来的new SimpleDateFormat
private static final ThreadLocal dateFormatter = new ThreadLocal(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(“yyyy-MM-dd”);
}
};
以上代码可以⽤Lambda表达式简化为:private static final ThreadLocal<SimpleDateFormat> dateFormatter =
ThreadLocal.withInitial(SimpleDateFormat::new);
对应的⽅法修改为:
public static String longDateFormat(Long time, String dateFormat) {
if (time == null) {
return null;
}
<().applyPattern(dateFormat);
().format(new Date(time));
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论