平时做业务开发的同学,可能很少注意到一些网络安全防范,身边大多数同学写代码都是应付任务即可,不会对代码有太多安全考虑,但是往往一些代码的漏洞,就会导致企业损失惨重,甚至程序员也会面临被裁风险。在国外的金融公司或者银行,都会有很多网络安全培训,我们公司也会定期也会需要在内部考证(
secure code warrior
),确保每个程序员都了解一些常见的攻击手段和防范措施,本文会介绍一些常见的网络攻击手段以及防范措施供大家参考。
XXE 攻击是攻击者利用 XML 解析器在服务器上读取任意文件。Java 应用尤其容易受到 XXE 攻击,因为大多数 Java XML 解析器默认启用了 XXE 的功能
示例:
TransformerFactory trfactory = TransformerFactory.newInstance();
修复方法:
要防止 Java 应用中的 XXE 攻击,您需要显式禁用这些功能。防止 XXE 最安全的方法是完全禁用 DTD(外部实体)
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
SQL 注入攻击是当攻击者注入数据以操纵 SQL 命令。当应用程序没有正确验证用户输入时,攻击者可以插入 SQL 语言特殊的字符以破坏查询的逻辑,从而执行任意 SQL 代码
示例:
String firstname = req.getParameter("firstname");
String lastname = req.getParameter("lastname");
String query = "SELECT id, firstname, lastname FROM authors WHERE firstname = '" + firstname + "' and lastname = '" + lastname + "'";
Statement stmt = connection.createStatement();
ResultSet results = stmt.executeQuery(query);
修复方法:
防止 SQL 注入的最佳方法是使用参数化语句,这使得 SQL 注入几乎不可能
String firstname = req.getParameter("firstname");
String lastname = req.getParameter("lastname");
String query = "SELECT id, firstname, lastname FROM authors WHERE firstname = ? and lastname = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, firstname);
pstmt.setString(2, lastname);
ResultSet results = pstmt.executeQuery();
代码注入是当通过用户输入传递的数据导致程序运行或返回数据的意外副作用。防止代码注入的最简单方法是在输出中应用输入验证和转义
示例:
String userInput = req.getParameter("userInput");
String output = "Hello, " + userInput + "!";
修复方法:
为防止代码注入,您可以在将用户输入用于应用程序之前对其进行清理和转义。例如,您可以使用 Apache Commons Text 库中的 StringEscapeUtils.escapeHtml4()
方法来转义 HTML 字符
import org.apache.commons.text.StringEscapeUtils;
String userInput = req.getParameter("userInput");
String escapedInput = StringEscapeUtils.escapeHtml4(userInput);
String output = "Hello, " + escapedInput + "!";
XSS 攻击是攻击者将恶意脚本注入网站或应用程序内容。要防止 XSS 攻击,可以在解析用户输入之前转义特殊字符,如 <、>
和 @
示例:
String userInput = req.getParameter("userInput");
String output = "" + userInput + "";
修复方法:
要防止 XSS 攻击,您可以在将用户输入包含在 HTML 中之前对其进行转义。您可以使用 Apache Commons Text 库中的 StringEscapeUtils.escapeHtml4
import org.apache.commons.text.StringEscapeUtils;
String userInput = req.getParameter("userInput");
String safeUserInput = StringEscapeUtils.escapeHtml4(userInput);
String output = "" + safeUserInput + "";
不安全的反序列化攻击是指攻击者利用 Java 对象反序列化过程中的漏洞,以执行恶意代码。
示例:
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object deserializedObject = ois.readObject();
修复方法:
为了防止不安全的反序列化攻击,您可以使用安全的反序列化库,如 SerialKiller
,或者限制可反序列化的类。
SerialKiller sk = new SerialKiller("serialkiller.conf");
Object deserializedObject = sk.readObject();
目录遍历攻击是指攻击者试图通过操纵文件路径来访问受限制的文件或目录。
示例:
String filename = req.getParameter("filename");
File file = new File("restricted/" + filename);
修复方法:
为了防止目录遍历攻击,您可以验证用户输入的文件名是否包含非法字符,如 …,并确保只允许访问预定义的安全目录。
String filename = req.getParameter("filename");
if (filename.contains("..")) {
throw new IllegalArgumentException("Invalid filename");
}
File file = new File("restricted/" + filename);
敏感信息泄露是指应用程序在错误信息、日志文件或配置文件中暴露敏感数据,如密码、密钥或系统细节。
示例:
<error-page>
<error-code>500error-code>
<location>/path/to/default_500.jsplocation>
error-page>
<error-page>
<exception-type>java.io.IOExceptionexception-type>
<location>/path/to/default_exception_handler.jsplocation>
error-page>
修复方法:
要防止敏感信息泄露,您可以创建自定义的错误页面,屏蔽敏感信息,而不是显示详细的错误信息。此外,确保配置文件和日志文件不包含敏感数据,并限制对这些文件的访问权限。
<error-page>
<error-code>500error-code>
<location>/path/to/custom_500.jsplocation>
error-page>
<error-page>
<exception-type>java.io.IOExceptionexception-type>
<location>/path/to/custom_exception_handler.jsplocation>
error-page>
命令注入攻击是攻击者利用应用程序执行恶意系统命令。要防止命令注入攻击,您需要对用户输入进行验证,并使用更安全的方法执行系统命令。
示例:
String command = "ping " + ipAddress;
Runtime.getRuntime().exec(command);
修复方法:
在修复方法中,我们将命令和参数分开存储在字符串数组中,这样就可以避免将用户输入直接拼接到命令字符串中,从而防止命令注入攻击。
String[] command = {"ping", ipAddress};
Runtime.getRuntime().exec(command);
格式化字符串攻击是攻击者利用格式化字符串函数(如 printf 和 sprintf)的错误使用来读取和修改内存。为了防止格式化字符串攻击,您需要正确使用格式化字符串函数。
示例:
System.out.printf(userInput);
修复方法:
在修复方法中,我们明确指定了格式化字符串(%s
),这样就可以避免将用户输入错误地解释为格式化字符串,从而防止格式化字符串攻击。
System.out.printf("%s", userInput);
跨站请求伪造攻击是攻击者利用用户的登录凭证向应用程序发起未经授权的请求。为了防止 CSRF 攻击,您需要使用令牌或其他验证方法来验证请求的来源。
示例:
在表单中没有验证令牌:
<form method="POST" action="/changePassword">
<input type="password" name="newPassword" />
<input type="submit" value="Change Password" />
form>
修复方法:
在表单中添加验证令牌:
在后端,验证令牌与存储在会话中的令牌是否匹配。
<form method="POST" action="/changePassword">
<input type="hidden" name="csrfToken" value="YOUR_CSRF_TOKEN" />
<input type="password" name="newPassword" />
<input type="submit" value="Change Password" />
form>
缓冲区溢出攻击是攻击者通过向程序提供的缓冲区写入超过其容量的数据,从而覆盖其他内存区域。为了防止缓冲区溢出攻击,您需要正确检查和限制用户输入的长度
示例:
public static void vulnerableMethod(String userInput) {
char[] buffer = new char[10];
for (int i = 0; i < userInput.length(); i++) {
buffer[i] = userInput.charAt(i);
}
}
修复方法:
在修复方法中,我们限制了从用户输入复制到缓冲区的字符数,从而防止缓冲
public static void fixedMethod(String userInput) {
char[] buffer = new char[10];
int length = Math.min(userInput.length(), buffer.length);
for (int i = 0; i < length; i++) {
buffer[i] = userInput.charAt(i);
}
}