安全规约
2025年3月30日大约 7 分钟
安全规约
开发中
此部分内容正在完善中,敬请期待...
安全规约是保障应用安全的重要基础,通过安全的编码规范,可以有效防范常见的安全漏洞和风险。本文将介绍阿里巴巴Java开发规范中关于安全编码的最佳实践和规范要求。
安全编码规约
输入验证和数据处理
强制
- 用户输入的SQL参数严格使用参数绑定或者METADATA字段值限定,防止SQL注入,禁止字符串拼接SQL访问数据库。
// 正例:
PreparedStatement ps = conn.prepareStatement("SELECT * FROM table WHERE id = ?");
ps.setInt(1, id);
// 反例:
Statement stmt = conn.createStatement();
stmt.executeQuery("SELECT * FROM table WHERE id = " + id);
强制
- 用户请求传入的任何参数必须做有效性验证。
- 忽略参数校验可能导致:
- 页面访问者伪造参数对数据库进行非法操作
- 缓存击穿
- 重复暴力请求
- SQL注入
- 正则输入源串拒绝服务RegexDOS
- 说明:Java代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。
- 忽略参数校验可能导致:
// 正例:
public void validateParam(String param) {
if (param == null || param.isEmpty()) {
throw new IllegalArgumentException("参数不能为空");
}
if (param.length() > 100) {
throw new IllegalArgumentException("参数长度不能超过100");
}
// 使用安全的正则表达式验证
if (!Pattern.matches("^[a-zA-Z0-9]+$", param)) {
throw new IllegalArgumentException("参数只能包含字母和数字");
}
}
强制
- 禁止向HTML页面输出未经安全过滤或未正确转义的用户数据。
// 正例:使用Spring的HtmlUtils或者commons-lang的StringEscapeUtils进行HTML转义
String escapeHtml = HtmlUtils.htmlEscape(userContent);
强制
- 表单、AJAX提交必须执行CSRF安全验证。
- 说明:CSRF(Cross-site request forgery)跨站请求伪造是一种常见的攻击,防范的方法有:
- 使用Token认证来验证用户
- 再次输入密码确认
- 检验Referer
- 说明:CSRF(Cross-site request forgery)跨站请求伪造是一种常见的攻击,防范的方法有:
// 正例:使用Spring Security提供的CSRF保护
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
强制
- 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防护措施,如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。
- 说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰他人。
// 正例:实现短信发送频率限制
public boolean sendVerificationCode(String phoneNumber) {
// 从缓存中获取当前手机号今日发送次数
Integer count = (Integer) redisCache.get("sms:count:" + phoneNumber);
if (count != null && count >= 5) {
return false; // 每天最多发送5条
}
// 从缓存中获取上次发送时间
Long lastSendTime = (Long) redisCache.get("sms:time:" + phoneNumber);
long now = System.currentTimeMillis();
if (lastSendTime != null && now - lastSendTime < 60000) {
return false; // 限制60秒内不能重复发送
}
// 发送验证码
boolean success = smsService.send(phoneNumber, generateCode());
if (success) {
// 更新缓存
redisCache.put("sms:time:" + phoneNumber, now);
redisCache.increment("sms:count:" + phoneNumber, 1, 24 * 60 * 60); // 24小时有效期
}
return success;
}
数据加密解密
强制
- 用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
// 正例:
String displayIDCard = DesensitizationUtil.idCardNum(user.getIdCard(), 6, 4);
String displayPhone = DesensitizationUtil.mobilePhone(user.getPhoneNumber());
强制
- 【加密】敏感数据在数据库中应该加密存储,加密方法需要安全可靠,白盒测试中不能找到密钥。
// 正例:使用加密工具类对敏感信息进行加密
String encryptedIDCard = AESEncryptUtil.encrypt(user.getIdCard(), secureKey);
userDao.saveEncryptedIDCard(userId, encryptedIDCard);
强制
- 【加密】敏感数据在网络传输过程中必须加密,加密方法需要安全可靠。
- 说明:通过HTTPS加密传输或用其他加密方式,如:非对称加密。
强制
- 【加密】密码加密存储时,应该是不可逆的加密方式,推荐使用带salt的单向哈希算法,禁止采用可逆的加密算法或者明文存储。
// 正例:使用BCrypt加密密码
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
// 验证密码
boolean matched = BCrypt.checkpw(inputPassword, hashedPassword);
强制
- 【加密】秘钥和密码不能硬编码在源代码中,应该从配置文件、数据库、或者第三方系统动态获取。
// 反例:
private static final String KEY = "d7fad32b41094c7ab9535afca77b9d16";
// 正例:从配置文件或环境变量中获取
@Value("${encryption.key}")
private String encryptionKey;
推荐
- 【授权】服务间调用必须执行授权认证,禁止使用明文用户名/密码作为权限认证机制,推荐使用OAuth、HMAC、JWT、双向TLS等安全认证机制。
// 正例:使用JWT进行API认证
String token = JwtUtil.createToken(userId, role, expirationTime);
推荐
- 【授权】系统和系统之间严禁直接使用数据库访问,必须通过服务API方式访问。
安全编码实践
强制
- 禁止在系统日志中存放敏感信息,需要过滤或者脱敏后存放。
// 反例:
logger.info("User credit card: {}", user.getCreditCard());
// 正例:
logger.info("User signed in: {}", user.getUsername());
强制
- 禁止使用被证实存在安全漏洞的三方库,必须使用安全版本。
- 说明:可以使用 OWASP Dependency-check 等工具检查第三方依赖的安全性。
<!-- Maven插件配置示例 -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.5.0</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
推荐
- 在使用 SSO 单点登录机制时,必须检验返回的授权信息,防止 CSRF 攻击。
推荐
- 对于文件上传功能,应该严格限制上传的文件类型和大小,防止恶意文件上传。
// 正例:验证上传文件类型和大小
public String uploadFile(MultipartFile file) {
// 检查文件大小
if (file.getSize() > MAX_FILE_SIZE) {
throw new IllegalArgumentException("文件大小超过限制");
}
// 检查文件类型
String filename = file.getOriginalFilename();
String extension = FilenameUtils.getExtension(filename);
if (!ALLOWED_EXTENSIONS.contains(extension.toLowerCase())) {
throw new IllegalArgumentException("不支持的文件类型");
}
// 处理文件上传
// ...
}
推荐
- 应用层统一进行安全日志打印,如使用拦截器或AOP记录关键操作的日志信息,包括操作时间、操作人员、操作内容等。
参考
- 可以使用Spring Security、Shiro等安全框架来保护应用安全。
参考
- 定期进行安全测试和代码审计,如使用OWASP ZAP等工具进行安全扫描。
常见安全漏洞防范
1. SQL注入防范
SQL注入是最常见的Web安全漏洞之一,可以通过以下方式防范:
// 正确做法:使用参数化查询
public User findByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{username}, userRowMapper);
}
// 使用ORM框架
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
2. XSS攻击防范
XSS(跨站脚本攻击)是另一种常见的Web安全漏洞,可以通过以下方式防范:
// 输出到HTML前进行转义
String safeHtml = HtmlUtils.htmlEscape(userInput);
// 使用安全的模板引擎
// Thymeleaf默认会对输出进行HTML转义
// <p th:text="${userContent}">Default content</p>
3. CSRF攻击防范
CSRF(跨站请求伪造)攻击可以通过以下方式防范:
// 在表单中添加CSRF Token
<form action="/transfer" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<!-- 其他表单字段 -->
</form>
// 在Spring Security中启用CSRF保护
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
4. 敏感信息保护
保护敏感信息的存储和传输:
// 使用加密算法保护敏感数据
public void saveUserInfo(User user) {
// 加密敏感信息
String encryptedSSN = encryptionService.encrypt(user.getSocialSecurityNumber());
user.setEncryptedSSN(encryptedSSN);
user.setSocialSecurityNumber(null); // 不存储原始数据
userRepository.save(user);
}
// 实现数据脱敏
public UserDTO getUserInfo(Long userId) {
User user = userRepository.findById(userId);
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setUsername(user.getUsername());
// 脱敏手机号
String phone = user.getPhoneNumber();
if (phone != null && phone.length() > 10) {
dto.setPhoneNumber(phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4));
}
return dto;
}
总结
安全规约是软件开发中不可忽视的关键部分,遵循阿里巴巴Java开发规范中的安全编码原则,可以有效防止常见的安全漏洞,保护系统和用户数据的安全。安全不仅仅是安全团队的责任,更是每一位开发人员的责任。通过在开发过程中遵循安全最佳实践,我们可以构建更加安全、可靠的应用系统。