JAVA开发中涉及的基础安全问题和解决方法初探
字数 575 2025-08-26 22:11:51
JAVA开发中涉及的基础安全问题和解决方法
SQL注入
漏洞原理
SQL注入是由于未对用户输入进行过滤,直接将用户输入拼接到SQL语句中执行,导致攻击者可以构造恶意SQL语句执行非授权操作。
漏洞示例代码
public String jdbc_sqli_vul(@RequestParam("username") String username) {
StringBuilder result = new StringBuilder();
try {
Connection con = DriverManager.getConnection(url, user, password);
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'"; // 直接拼接用户输入
ResultSet rs = statement.executeQuery(sql);
// ...
} catch (Exception e) {
logger.error(e.toString());
}
return result.toString();
}
安全解决方案
使用预处理语句(PreparedStatement)和参数化查询:
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username); // 安全地设置参数
ResultSet rs = st.executeQuery();
错误使用案例
即使使用了PreparedStatement,如果仍然拼接SQL字符串,依然存在风险:
String sql = "select * from users where username = '" + username + "'";
PreparedStatement st = con.prepareStatement(sql); // 错误用法,仍然存在SQL注入
XSS(跨站脚本攻击)
漏洞类型
- 反射型XSS:恶意脚本来自当前HTTP请求
- 存储型XSS:恶意脚本被存储到服务器(如数据库)中
漏洞示例代码
// 反射型XSS
public static String reflect(String xss) {
return xss; // 直接返回用户输入
}
// 存储型XSS(存储到cookie)
public String store(String xss, HttpServletResponse response) {
Cookie cookie = new Cookie("xss", xss);
response.addCookie(cookie);
return "Set param into cookie";
}
// 从cookie中读取并展示XSS
public String show(@CookieValue("xss") String xss) {
return xss;
}
安全解决方案
- 转义特殊字符:
public static String encode(String origin) {
origin = StringUtils.replace(origin, "&", "&");
origin = StringUtils.replace(origin, "<", "<");
origin = StringUtils.replace(origin, ">", ">");
origin = StringUtils.replace(origin, "\"", """);
origin = StringUtils.replace(origin, "'", "'");
origin = StringUtils.replace(origin, "/", "/");
return origin;
}
- 输入验证:
public static String safe(String xss) {
if (code(xss)=="false"){
System.out.println("参数不合法");
}
}
private static String code(String origin){
if (origin.contains(""+'&')||origin.contains(""+'<')||origin.contains(""+'>')||origin.contains(""+'&')||origin.contains(""+'"')){
return "false";
}
return "true";
}
文件上传漏洞
漏洞示例代码
public String singleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
try {
byte[] bytes = file.getBytes();
Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
Files.write(path, bytes); // 直接保存上传的文件,无任何过滤
} catch (IOException e) {
logger.error(e.toString());
}
return "redirect:/file/status";
}
安全解决方案
- 文件后缀名白名单校验
- MIME类型黑名单校验
- 文件内容校验
public String uploadPicture(@RequestParam("file") MultipartFile multifile) throws Exception {
// 1. 校验文件后缀名
String fileName = multifile.getOriginalFilename();
String Suffix = fileName.substring(fileName.lastIndexOf("."));
String[] picSuffixList = {".jpg", ".png", ".jpeg", ".gif", ".bmp", ".ico"};
boolean suffixFlag = false;
for (String white_suffix : picSuffixList) {
if (Suffix.toLowerCase().equals(white_suffix)) {
suffixFlag = true;
break;
}
}
if (!suffixFlag) {
logger.error("[-] Suffix error: " + Suffix);
return "Upload failed. Illeagl picture.";
}
// 2. 校验MIME类型
String mimeType = multifile.getContentType();
String[] mimeTypeBlackList = {"text/html", "text/javascript", "application/javascript"};
for (String blackMimeType : mimeTypeBlackList) {
if (SecurityUtil.replaceSpecialStr(mimeType).toLowerCase().contains(blackMimeType)) {
logger.error("[-] Mime type error: " + mimeType);
return "Upload failed. Illeagl picture.";
}
}
// 3. 校验文件内容
File excelFile = convert(multifile);
boolean isImageFlag = isImage(excelFile);
if (!isImageFlag) {
logger.error("[-] File is not Image");
return "Upload failed. Illeagl picture.";
}
// 安全保存文件
byte[] bytes = multifile.getBytes();
Path path = Paths.get(UPLOADED_FOLDER + multifile.getOriginalFilename());
Files.write(path, bytes);
return String.format("You successfully uploaded '%s'", path);
}
XXE(XML外部实体注入)
漏洞示例代码
public String DocumentBuilderVuln01(HttpServletRequest request) {
String body = WebUtils.getRequestBody(request);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(body);
InputSource is = new InputSource(sr);
Document document = db.parse(is); // 解析XML,未禁用外部实体
// ...
}
安全解决方案
禁用DOCTYPE声明和外部实体:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder db = dbf.newDocumentBuilder();
路径遍历漏洞
漏洞示例代码
public String getImage(String filepath) throws IOException {
File f = new File(filepath);
if (f.exists() && !f.isDirectory()) {
byte[] data = Files.readAllBytes(Paths.get(filepath));
return new String(Base64.encodeBase64(data)); // 直接读取文件,无路径过滤
}
return "File doesn't exist or is not a file.";
}
安全解决方案
对路径参数进行过滤:
public static String pathFilter(String filepath) {
String temp = filepath;
// 处理URL编码
while (temp.indexOf('%') != -1) {
try {
temp = URLDecoder.decode(temp, "utf-8");
} catch (Exception e) {
return null;
}
}
// 禁止相对路径和绝对路径
if (temp.contains("..") || temp.charAt(0) == '/') {
return null;
}
return filepath;
}
public String getImageSec(String filepath) throws IOException {
if (SecurityUtil.pathFilter(filepath) == null) {
return "Bad boy. Illegal file path.";
}
return getImgBase64(filepath);
}
总结
- SQL注入:使用预处理语句和参数化查询
- XSS:对输出进行HTML编码或验证输入
- 文件上传:校验文件后缀、MIME类型和文件内容
- XXE:禁用外部实体和DOCTYPE声明
- 路径遍历:过滤路径中的特殊字符和相对路径
安全开发应该遵循"不信任任何输入"的原则,对所有用户输入进行严格的验证和过滤。