记一次对某博客系统登录认证缺陷的代码审计
字数 2021 2025-08-30 06:50:12

博客系统登录认证缺陷代码审计报告

1. 项目概述

MyBlog是一个基于以下技术栈实现的Java博客系统:

  • SpringBoot
  • Mybatis
  • Thymeleaf

系统特点:

  • 适合个人开发者、小型团队或教育机构使用
  • 提供美观的界面和齐全的功能
  • 部署简单,代码完善
  • 允许用户专注于内容创作,系统处理技术细节

2. 环境搭建步骤

  1. 下载源代码并使用IDEA加载
  2. 创建数据库并导入数据库表数据文件
  3. 启动项目并在浏览器中访问
  4. 后台登录地址可通过访问特定URL进入

3. 代码审计重点

本次审计聚焦于用户登录认证过程中的业务设计缺陷,主要审计以下部分:

  • 登录认证Controller层逻辑
  • 用户服务实现类
  • SQL查询与Mapper定义
  • Cookie生成与验证机制

4. 登录认证流程分析

4.1 Controller层分析

文件路径:blog-master/src/main/java/com/my/blog/website/controller/admin/AuthController.java

主要逻辑:

  1. 获取用户登录认证失败次数
  2. 获取用户名和密码进行身份认证
  3. 处理认证结果

4.2 服务层实现

文件路径:blog-master/src/main/java/com/my/blog/website/service/impl/UserServiceImpl.java

认证流程:

  1. 检查用户名和密码是否为空
  2. 创建Example查询实例
  3. 添加SQL查询断言(要求用户名为输入的用户名)
  4. 调用底层SQL查询

4.3 DAO层与SQL映射

countByExample的DAO层定义:

  • 使用MyBatis的Example查询方式
  • 动态生成SQL语句

SQL映射文件:UserVomapper.xml

关键特性:

  • 使用动态SQL生成WHERE子句
  • 使用foreach标签遍历oredcriteria集合
  • 使用trim去除特定前缀、关键词和后缀
  • 条件判断使用${}进行拼接

4.4 SQL注入分析

虽然使用了${}进行条件拼接,但实际不存在SQL注入风险的原因:

  1. 条件部分(如"username")是预定义的,不是用户输入
  2. 用户输入部分(值)被正确处理
  3. 条件与值分离的实现方式确保了安全性

条件构建过程:

  • 构建二维List数组:List(List("username",value))
  • "username"是预定义的,用户只能控制value部分

最终生成的SQL语句示例:

SELECT COUNT(*) FROM users WHERE username = ?

5. 登录认证缺陷

5.1 用户枚举漏洞

存在差异响应:

  • 当用户不存在时,返回"不存在该用户"
  • 当用户存在但密码错误时,返回不同错误信息

这使得攻击者可以通过响应差异枚举有效用户名。

5.2 认证失败处理

认证失败流程:

  1. 进入catch块
  2. error_count递增
  3. 根据失败次数设置失败回显内容

问题:error_count检测逻辑无法有效防御用户枚举风险。

5.3 Cookie安全问题

认证成功后的Cookie设置:

  1. 设置用户Session
  2. 如果勾选"remember_me",调用TaleUtils.setCookie设置Cookie

setCookie实现细节:

  • 使用Tools.enAes加密用户uid
  • 使用硬编码的AES盐值:0123456789abcdef
  • Cookie键名为S_L_ID
  • 设置生命周期后返回

安全问题分析

  1. 硬编码加密密钥:AES盐值固定且公开
  2. 可预测的Cookie生成:使用uid和固定算法生成
  3. 缺乏系统唯一性:所有使用该系统的实例默认使用相同密钥

攻击场景:

  1. 攻击者识别目标使用此博客系统
  2. 从开源代码获取加密算法和盐值
  3. 查询数据库获取admin的uid(通常为1)
  4. 本地生成有效的管理员Cookie
  5. 使用生成的Cookie直接访问后台

6. 漏洞验证步骤

  1. 本地构建测试类生成有效Cookie:
// 示例测试类代码
String encryptedUID = Tools.enAes("1", "0123456789abcdef");
  1. 构造Cookie内容:
S_L_ID=[生成的加密值]
  1. 使用该Cookie刷新页面,可直接进入后台并正常操作

7. 修复建议

  1. 用户枚举问题

    • 统一认证失败的响应消息
    • 实现延迟响应机制
  2. Cookie安全问题

    • 使用系统安装时生成的随机密钥
    • 实现密钥轮换机制
    • 增加Cookie绑定IP或设备指纹
    • 使用更安全的令牌生成方案(如JWT)
  3. 加密相关

    • 避免硬编码加密密钥
    • 使用环境变量或配置文件存储密钥
    • 考虑使用更强的加密算法

8. 总结

本案例揭示了以下安全问题和知识点:

  1. 认证逻辑缺陷:差异响应导致的用户枚举风险
  2. 加密实现问题:硬编码密钥导致的可预测令牌
  3. 动态SQL安全:正确理解MyBatis Example查询的安全性
  4. 系统唯一性:开源系统部署时应修改默认安全参数

审计要点:

  • 关注认证流程中的差异响应
  • 检查加密实现是否使用可预测的参数
  • 理解框架特性以避免误判(如动态SQL的安全性)
  • 识别硬编码的安全敏感信息
博客系统登录认证缺陷代码审计报告 1. 项目概述 MyBlog 是一个基于以下技术栈实现的Java博客系统: SpringBoot Mybatis Thymeleaf 系统特点: 适合个人开发者、小型团队或教育机构使用 提供美观的界面和齐全的功能 部署简单,代码完善 允许用户专注于内容创作,系统处理技术细节 2. 环境搭建步骤 下载源代码并使用IDEA加载 创建数据库并导入数据库表数据文件 启动项目并在浏览器中访问 后台登录地址可通过访问特定URL进入 3. 代码审计重点 本次审计聚焦于用户登录认证过程中的业务设计缺陷,主要审计以下部分: 登录认证Controller层逻辑 用户服务实现类 SQL查询与Mapper定义 Cookie生成与验证机制 4. 登录认证流程分析 4.1 Controller层分析 文件路径: blog-master/src/main/java/com/my/blog/website/controller/admin/AuthController.java 主要逻辑: 获取用户登录认证失败次数 获取用户名和密码进行身份认证 处理认证结果 4.2 服务层实现 文件路径: blog-master/src/main/java/com/my/blog/website/service/impl/UserServiceImpl.java 认证流程: 检查用户名和密码是否为空 创建Example查询实例 添加SQL查询断言(要求用户名为输入的用户名) 调用底层SQL查询 4.3 DAO层与SQL映射 countByExample 的DAO层定义: 使用MyBatis的Example查询方式 动态生成SQL语句 SQL映射文件: UserVomapper.xml 关键特性: 使用动态SQL生成WHERE子句 使用 foreach 标签遍历 oredcriteria 集合 使用 trim 去除特定前缀、关键词和后缀 条件判断使用 ${} 进行拼接 4.4 SQL注入分析 虽然使用了 ${} 进行条件拼接,但实际不存在SQL注入风险的原因: 条件部分(如"username")是预定义的,不是用户输入 用户输入部分(值)被正确处理 条件与值分离的实现方式确保了安全性 条件构建过程: 构建二维List数组: List(List("username",value)) "username"是预定义的,用户只能控制value部分 最终生成的SQL语句示例: 5. 登录认证缺陷 5.1 用户枚举漏洞 存在差异响应: 当用户不存在时,返回"不存在该用户" 当用户存在但密码错误时,返回不同错误信息 这使得攻击者可以通过响应差异枚举有效用户名。 5.2 认证失败处理 认证失败流程: 进入catch块 error_count 递增 根据失败次数设置失败回显内容 问题: error_count 检测逻辑无法有效防御用户枚举风险。 5.3 Cookie安全问题 认证成功后的Cookie设置: 设置用户Session 如果勾选"remember_ me",调用 TaleUtils.setCookie 设置Cookie setCookie 实现细节: 使用 Tools.enAes 加密用户uid 使用硬编码的AES盐值: 0123456789abcdef Cookie键名为 S_L_ID 设置生命周期后返回 安全问题分析 : 硬编码加密密钥 :AES盐值固定且公开 可预测的Cookie生成 :使用uid和固定算法生成 缺乏系统唯一性 :所有使用该系统的实例默认使用相同密钥 攻击场景: 攻击者识别目标使用此博客系统 从开源代码获取加密算法和盐值 查询数据库获取admin的uid(通常为1) 本地生成有效的管理员Cookie 使用生成的Cookie直接访问后台 6. 漏洞验证步骤 本地构建测试类生成有效Cookie: 构造Cookie内容: 使用该Cookie刷新页面,可直接进入后台并正常操作 7. 修复建议 用户枚举问题 : 统一认证失败的响应消息 实现延迟响应机制 Cookie安全问题 : 使用系统安装时生成的随机密钥 实现密钥轮换机制 增加Cookie绑定IP或设备指纹 使用更安全的令牌生成方案(如JWT) 加密相关 : 避免硬编码加密密钥 使用环境变量或配置文件存储密钥 考虑使用更强的加密算法 8. 总结 本案例揭示了以下安全问题和知识点: 认证逻辑缺陷 :差异响应导致的用户枚举风险 加密实现问题 :硬编码密钥导致的可预测令牌 动态SQL安全 :正确理解MyBatis Example查询的安全性 系统唯一性 :开源系统部署时应修改默认安全参数 审计要点: 关注认证流程中的差异响应 检查加密实现是否使用可预测的参数 理解框架特性以避免误判(如动态SQL的安全性) 识别硬编码的安全敏感信息