分组加密算法

1.实验目的

  • 了解当前信息系统的常见安全技术。

  • 基于C++、Java或.net中成熟的框架和开发库,实现一个简单信息系统的登录认证模块

2.相关理论和实验

2.1 相关基本理论

公钥密码算法加密与解密的基本原理

1)信息系统中用户登录和认证的基本原理:查阅文献,了解信息系统用户登录认证等功能的一般应用背景,了解其主要支撑技术的基本原理。

2)框架和编程库的调用:了解C++、Java或.net等常用语言或框架下对于B/S或C/S系统下用户登录及认证开发框架或库的调用,掌握一种语言环境下调用系统框架或功能库的方法,能够在现实环境中调用。

3.实验内容与结果

3.1 信息系统用户登录认证的基本原理

查阅文献,并对信息系统用户登录认证的基本原理和常见技术加以叙述。

实验要求

要求详细叙述以下内容:

  1. 信息系统用户登录认证的常见应用现状
  2. 解释相关技术的基本概念。
  3. 比较分析常见登录认证技术在安全性、易用性、可靠性等方面的发展历程和异同。

1. 信息系统用户登录认证的常见应用现状

信息系统中的用户登录认证应用广泛且技术多样,主要目的是确保系统安全性和用户数据的保护。以下是一些常见的用户登录认证应用现状:

  • 单点登录(SSO):单点登录允许用户只需要进行一次身份验证,就可以多个应用和服务,不仅可以提供更好的用户体验,还可以减少密码被盗用的风险 [1] 。SSO通常用于企业环境,通过统一的身份验证平台(如PingOne或Okta),实现对多个内部和外部应用的集中管理 。
  • 多因素认证(MFA):结合两种或多种认证方式(如密码加上短信验证码或指纹),大大提高了安全性。MFA 是目前广泛采用的增强安全措施。
  • 自适应认证:利用机器学习和行为分析技术,自适应认证系统可以根据用户的登录行为和环境进行风险评估,动态调整认证强度。这种方式有助于在提升安全性的同时,提供更好的用户体验。例如,IBM Security Verify使用自适应认证来监控用户活动并评估风险,从而有效防止未授权访问 [2]
  • 无密码登录:无密码登录是指用户无需输入传统密码即可完成认证的方式,通常通过一次性链接、OTP、或生物特征识别实现。这种方式既提高了安全性,又减轻了用户记忆密码的负担。例如,雅虎的Yahoo Account Key和二维码扫描登录 [3]

2. 相关技术的基本概念

信息系统用户登录认证涉及多种技术,确保系统的安全性和用户数据的保护。

  • 双因素认证(2FA):双因素认证是一种安全技术,要求用户提供两种不同的验证因素来访问系统。常见的双因素认证方法包括:
    • SMS验证码:通过短信发送一次性密码(OTP)到用户的手机,用户输入该密码完成验证。这种方法简单易用,但存在被拦截的风险 。
    • 硬件令牌:使用设备生成的OTP进行验证,安全性高于短信验证,但设备可能丢失或损坏。
    • 生物识别:通过指纹、面部识别或虹膜扫描进行验证,安全性高,但成本较高。
  • 基于用户行为的身份认证技术:是根据用户行为习惯的差异性进行身份认证,使用用户的浏览行为和终端系统操作行为的数据进行身份认证模型建立,这种行为数据采集方便并且不依赖额外的硬件设备[2]
  • 零信任架构:零信任架构是一种安全模型,假设网络内外的所有用户和设备都是不可信的。它要求严格验证每个访问请求,并持续监控用户活动,确保最小权限原则的实现。零信任架构通常结合多因素认证、加密和细粒度访问控制策略 。
  • 数字证书:数字证书使用公钥基础设施(PKI)来验证用户身份。每个用户或设备都有一个唯一的公钥和私钥对,公钥存储在数字证书中。验证过程中,系统使用公钥解密用户发送的信息,以确认其身份。

3. 常见登录认证技术的比较分析

  • 安全性
    • 密码认证:安全性较低,容易被猜测或通过钓鱼攻击获取。
    • MFA:显著提高安全性,即使密码被盗,攻击者也很难通过第二种认证要素。
    • 生物识别认证:提供高安全性,但存在隐私问题和误识别风险。
    • 基于证书的认证:安全性高,适合高敏感度的企业和政府应用。
  • 易用性
    • 密码认证:易用性高,但用户需要记住多个复杂密码。
    • MFA:增加了额外步骤,但提供了多种方式(如短信、应用生成的验证码),相对易用。
    • 生物识别认证:用户体验好,无需记住密码,只需提供生物特征。
    • 基于证书的认证:配置复杂,对用户不友好,适合技术熟练的企业用户。
  • 可靠性
    • 密码认证:依赖于用户记忆,忘记密码会导致访问问题。
    • MFA:非常可靠,但依赖于多个系统和设备。
    • 生物识别认证:可靠性高,但设备故障或生物特征变化(如受伤)可能影响认证。
    • 基于证书的认证:极其可靠,但证书管理复杂,需要专业维护。

3.2 环境搭建说明

实验内容

选定一种语言或框架,搭建完整的开发环境。采用选定的开发框架或开发库,在开发环境中设置引用,完成程序的实现准备工作。

实验要求

要求完成以下内容:

  1. 选定一种语言及对应的开发框架或功能库,搭建完整的开发环境。要求该环境必须能够顺利完成代码的开发、调试和编译。要求描述所选定的语言和框架,并简要说明选择该语言及框架的原因。
  2. 在开发环境中设置对于相关框架和开发库的引用。要求在引用设置的各个步骤进行截图,并粘贴到实验报告中。

实验过程

1.选定一种语言及对应的开发框架或功能库,搭建完整的开发环境

  • 语言:Java
  • 开放框架:Spring Boot
  • 原因:Java跨平台,高性能,广泛应用于企业级开发。Spring Boot简化了Spring应用的创建和开发,提供了内置的服务器,适合快速开发RESTful应用。

image-20240619203516164

image-20240618190131053

image-20240618190141078

image-20240618190226510

image-20240619203556531

image-20240619203633365

image-20240620203638549

image-20240621192851836

3.3 信息系统用户登录认证功能的实现

实验内容

采用上节所搭建好的开发环境,引用上节选定的开发环境,完成一个B/S或C/S信息系统,实现其用户登录和认证的功能。

实验要求

要求完成以下内容:

  1. 实现一个简单的信息系统。要求该系统采用实验报告前述章节选定的语言和开发框架/开发库,实现系统中用户登录的核心功能。

    image-20240621210100211

    • image-20240621210133476
  2. 要求该系统能够对正确的用户完成正常的登录功能。认证方式可包括密码、证书、第三方等。

    • 密码认证:image-20240621193017140

    • 双因素认证:输入密码后再输入一次验证码

      image-20240621205558266

  3. 系统能够对错误的用户提示登录错误。

    • image-20240621193030694
  4. 系统可包含简单的用户管理功能

    • 注册:image-20240621193049717

    • image-20240621193111328

    • 注册成功,跳转登录界面,同时更新数据库

      image-20240621193230594

      image-20240621193427672

  5. 系统的各类提示信息应重复考虑不同条件下的安全影响。

    • 不泄露用户信息:当用户登录失败时,不要提供过于详细的错误信息。给出通用的错误消息,“用户名或密码错误”。

      image-20240621193451268

    • 防止暴力破解:

      多次输入密码错误后提示

      image-20240621195704909

  6. 要求在代码的关键步骤添加注释。整个程序的注释数量不得少于5处。

    部分关键代码:

    usercontroller:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69

    @Controller
    public class UserController {
    @Resource
    private UserService userService;

    @GetMapping(value = "/login")
    public String showLoginPage() {
    return "login"; // 返回登录页面视图
    }

    @PostMapping(value = "/login")
    public String login(User user, Model model, HttpSession session) {
    try {
    User authenticatedUser = userService.get(user, session);
    if (authenticatedUser != null) {
    session.setAttribute("user", authenticatedUser);
    return "redirect:/user"; // 登录成功后跳转到用户页面
    } else {
    model.addAttribute("message", "用户名或密码错误");
    return "login"; // 登录失败返回登录页面
    }
    } catch (RuntimeException e) {
    model.addAttribute("message", e.getMessage());
    return "login"; // 登录失败返回登录页面
    }
    }

    @GetMapping(value = "/register")
    public String showRegisterPage() {
    return "register"; // 返回注册页面视图
    }

    @PostMapping(value = "/register")
    public String register(User user, Model model) {
    if (!user.getUsername().matches("\\d+")) {
    model.addAttribute("message", "用户名不合法,必须是一串数字");
    return "register"; // 返回注册页面并显示错误信息
    }

    boolean isRegistered = userService.register(user);

    if (isRegistered) {
    model.addAttribute("message", "注册成功,请登录");
    return "login"; // 注册成功后跳转到登录页面
    } else {
    model.addAttribute("message", "注册失败,请重试");
    return "register"; // 注册失败返回注册页面
    }
    }

    @GetMapping(value = "/user")
    public String showUserPage(Model model, HttpSession session) {
    User user = (User) session.getAttribute("user");
    if (user != null) {
    model.addAttribute("user", user);
    return "user"; // 返回用户页面视图
    } else {
    return "redirect:/login"; // 未登录,重定向到登录页面
    }
    }

    @GetMapping(value = "/logout")
    public String logout(HttpSession session) {
    session.invalidate();
    return "redirect:/login"; // 注销后重定向到登录页面
    }
    }

    captchacontroller:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    @RestController
    public class CaptchaController {

    private final Cage cage = new GCage();

    @GetMapping("/captcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String token = cage.getTokenGenerator().next();
    request.getSession().setAttribute("captcha", token);
    response.setContentType("image/png");
    cage.draw(token, response.getOutputStream());
    }
    }

    userservice:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.sdxb.springbootexample.service;

    import com.sdxb.springbootexample.entity.User;

    import javax.servlet.http.HttpSession;

    public interface UserService {
    User get(User user, HttpSession session);
    boolean register(User user);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67

    @Service
    public class UserServiceImpl implements UserService {
    @Resource
    private UserDao userDao;

    private Map<String, Integer> loginAttempts = new HashMap<>();
    private Map<String, LocalDateTime> lockTime = new HashMap<>();
    private static final int MAX_LOGIN_ATTEMPTS = 5;
    private static final int LOCK_TIME_DURATION = 30; // 30 seconds

    @Override
    public User get(User user, HttpSession session) {
    String username = HtmlUtils.htmlEscape(user.getUsername());

    // 检查用户是否被锁定
    if (lockTime.containsKey(username)) {
    LocalDateTime lockEndTime = lockTime.get(username);
    if (LocalDateTime.now().isBefore(lockEndTime)) {
    throw new RuntimeException("账户已锁定,请在" + LOCK_TIME_DURATION + "秒后重试");
    } else {
    // 锁定时间已过,解除锁定
    lockTime.remove(username);
    loginAttempts.remove(username);
    }
    }

    User foundUser = userDao.selectByUsername(username);
    if (foundUser != null && foundUser.getPassword().equals(user.getPassword())) {
    // 验证码验证
    String sessionCaptcha = (String) session.getAttribute("captcha");
    if (sessionCaptcha == null || !sessionCaptcha.equals(user.getCaptcha())) {
    throw new RuntimeException("验证码错误");
    }

    // 登录成功,清除登录尝试计数和锁定时间
    loginAttempts.remove(username);
    lockTime.remove(username);
    session.removeAttribute("captcha");
    return foundUser;
    } else {
    // 记录登录尝试次数
    loginAttempts.put(username, loginAttempts.getOrDefault(username, 0) + 1);
    if (loginAttempts.get(username) >= MAX_LOGIN_ATTEMPTS) {
    // 锁定用户
    lockTime.put(username, LocalDateTime.now().plusSeconds(LOCK_TIME_DURATION));
    throw new RuntimeException("账户已锁定,请在" + LOCK_TIME_DURATION + "秒后重试");
    }
    return null;
    }
    }

    @Override
    public boolean register(User user) {
    String username = HtmlUtils.htmlEscape(user.getUsername());

    // 检查用户名是否已存在
    if (userDao.selectByUsername(username) == null) {
    user.setPassword(HtmlUtils.htmlEscape(user.getPassword()));
    userDao.insert(user);
    return true;
    } else {
    return false;
    }
    }
    }

  7. 要求提交程序的源代码、调用的库文件(列在源代码目录下)、编译完成的可执行文件、README.txt说明文档。全部文件存入文件夹下,打包为zip压缩包。

3.4 测试结果及分析

实验内容

采用上节实现的系统测试用户登录认证。

实验要求

要求完成以下内容:

  1. 截图或采用规范的格式粘贴所采用的明文以及密码。

    image-20240621193512889

  2. 运行程序。若程序包含界面或字符式交互过程,请截图并粘贴在实验报告中。

    image-20240621205915853

  3. 使用多组正常用户登录,观察结果,并记录相应的过程。

    尝试登录并登录成功:

    image-20240621193700312

    image-20240621193838175

  4. 使用错误用户登录,可能包括用户名错误(不存在),密码错误等,并记录相应的过程。

    密码错误:image-20240621193606543

    用户名错误:image-20240621193632612

    验证码错误:image-20240621205645281

  5. 可进行其它登录认证功能过程,观察结果,并分析原因。同时,重点分析系统中不同功能逻辑对于各类可能的威胁所存在的脆弱性及风险。

    登录功能的脆弱性及风险:

    • 密码暴力破解:尽管我们添加了登录尝试次数限制,仍需要考虑使用更复杂的验证码或双因素认证(2FA)来进一步提高安全性。
    • 信息泄露:在登录失败时,避免显示过多详细信息。例如,不区分“用户名不存在”和“密码错误”,统一提示“用户名或密码错误”。

    注册功能的脆弱性及风险:

    • 输入验证不足:需要确保用户输入的所有字段都经过验证和转义,以防止XSS攻击和SQL注入。
    • 弱密码:建议在注册时要求用户设置强密码,增加密码复杂度检查。
  6. 要求各实验步骤均进行截图记录。

    部分关键实验步骤:

    • 登录和注册页面采用jsp

    • 验证码引用了库,配置依赖项

      image-20240621210932540

    • 使用了IEDA自带的maven,数据库与本地mysql相连。配置遇到了很多问题,通过上网找资料解决了。

    • 通过项目内文件实现页面对数据库的操作

      image-20240621211133280

[1]柯家海.高校统一身份认证平台的单点登录技术探究.自动化与信息工程,2023,44(06),60-63

[2]姚伟.无口令身份认证技术的研究与实现.互联网技术,2020.

[3]胡思莹.基于移动信息技术的数字校园无密码认证研究.北京林业大学,2017.