九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

admin 2023年10月13日23:11:55评论9 views字数 13202阅读44分0秒阅读模式

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

·前言·

Java Spring是目前企业开发中使用较多的一种java开发框架,因此基于该框架的安全内容尤为重要。Secure Code Warrior编码实验室的Java Spring涉及到的安全问题有9个,分别为缺少功能级别的访问控制、不恰当的身份验证、日志记录和监控不足、SQL注入、明文存储密码、路径遍历、服务器请求伪造、XML外部实体(XXE)、任意文件上传。


因涉及内容较多,完整内容将会在本公众号拆分为多篇内容分别发出。本文为该系列的第三篇——安全问题三:日志记录和监控不足。


往期内容请查看Java Spring编码安全系列


Improve logging with the Logback framework and the SLF4J library.

使用 Logback 框架和 SLF4J 库改进日志记录。


Configure a pattern for the Logback framework and use the SLF4J library to provide relevant logs with the correct logging level.

为Logback框架配置模式并使用SLF4J库提供具有正确日志记录级别的相关日志。


安全问题三:记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足




题目





1、介绍

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

VikingBank has a collection of endpoints, but not all of these endpoints should be accessible to anyone. This means there is a need for authorization rules. To do this, Spring Security comes in handy. The AuthorizationFilter allows for an easy and quick setting up of rules, which are defined using RequestMatchers.


日志记录是跟踪历史事件的过程。这些记录不仅提供了对应用程序内部工作方式的洞察,还是一种宝贵的安全工具,可以帮助识别安全事件,并监控异常事件,这些异常事件可能是威胁行为者准备攻击的指标。


VikingBank uses the SLF4J (Simple Logging Facade for Java) library. It provides an abstract implementation that different logging frameworks can make use of without requiring code changes. The logging framework is responsible for writing logs to an output. VikingBank uses Spring Boot's default Logback framework.


VikingBank使用SLF4J(Simple Logging Facade for Java)库。它提供了一个抽象实现,不需要进行代码更改,不同的日志记录框架可以利用它。日志框架负责将日志写入输出。VikingBank使用Spring Boot的默认日志框架Logback。


Logs with either too little or too much information are not very useful. A log entry should contain the when, where, who and what and the severity for each event.


日志记录的信息量过少或过多都不是很有用。每个事件的日志条目应包含何时、何地、谁、什么以及事件的严重性。


Alright, head over to the assignment folder to improve Vikingbank's logging.


好的,现在前往任务文件夹,改进VikingBank的日志记录。




2、源码

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足


logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?><configuration> <appender name="file_logger" class="ch.qos.logback.core.FileAppender"> <file>logs/vikingbank.log</file> <encoder> <pattern>%msg %n</pattern> </encoder> </appender>
<root level="info"> <appender-ref ref="file_logger" /> </root></configuration>

*左右滑动查看更多


InvoiceController.java
package vikingbank.web.controllers;
import org.springframework.http.HttpStatus;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.ResponseStatus;import vikingbank.web.entities.Invoice;import vikingbank.web.mappers.InvoiceMapper;import vikingbank.web.models.CreateInvoiceDto;import vikingbank.web.services.InvoiceService;import vikingbank.web.services.VikingBankUserService;
import java.security.Principal;
@Controllerpublic class InvoiceController { private final InvoiceService invoiceService; private final VikingBankUserService vikingBankUserService; private final InvoiceMapper invoiceMapper;
public InvoiceController(InvoiceService invoiceService, VikingBankUserService vikingBankUserService, InvoiceMapper invoiceMapper) {
this.invoiceService = invoiceService; this.vikingBankUserService = vikingBankUserService; this.invoiceMapper = invoiceMapper; }
@ResponseStatus(HttpStatus.CREATED) @PostMapping("/invoice/create") public String create(@RequestBody CreateInvoiceDto dto, Principal principal, Model model) { var user = this.vikingBankUserService.findUserByEmail(principal.getName()); try { Invoice invoice = invoiceMapper.toEntity(dto); invoiceService.saveInvoice(invoice); System.out.println(String.format("User with id: %s has created a new invoice with id: %s", user.getId(), invoice.getId()));
return "invoice/index"; } catch (Exception exception) { System.out.println(String.format("Creating an invoice for user with id: %s failed!", user.getId())); model.addAttribute("invoice", dto);
return "invoice/view"; } }}

*左右滑动查看更多


九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足




3、步骤一 配置日志返回

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足


为了方便阅读和使用日志,需要对其进行格式化。请前往资源文件夹中的logback-spring.xml,该文件是Logback框架的配置文件。


3.1 task1

重写日志的布局模式,使其结构化如下:

  • 可读性强的日期和时间,表示日志创建的时间。使用可排序的格式yyyy-MM-dd HH:mm:ss.fff;

  • 日志级别;

  • 完整的日志记录器名称;

  • 日志消息;

  • 可能由日志提供的完整异常信息;

  • 以换行符(%n)结束日志。 不要忘记用竖线符号(|)将数据分隔开,除了消息和异常之间!如果正确完成,下面是一个日志的示例:

2023-05-10 13:59:46,807|ERROR|vikingbank.web.controllers.InvoiceController|Something went wrong! java.lang.NullPointerException: Cannot invoke [..]

*左右滑动查看更多

Hint
The required format attributes:
%date{yyyy-MM-dd HH:mm:ss.ffff},%level,%logger,%msg,%ex


Step Solution
<pattern>%d{yyyy-MM-dd HH:mm:ss.fff}|%level|%c|%msg %ex %n</pattern>

*左右滑动查看更多

根据要求修改代码。

logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?><configuration> <!-- 定义一个名为 "file_logger" 的文件记录器 --> <appender name="file_logger" class="ch.qos.logback.core.FileAppender"> <!-- 指定日志文件的路径和文件名 --> <file>logs/vikingbank.log</file> <!-- 定义日志消息的格式 --> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%logger|%msg %ex %n</pattern> </encoder> </appender>
<!-- 配置根日志记录器 --> <root level="info"> <!-- 将根日志记录器的输出关联到 "file_logger" 文件记录器 --> <appender-ref ref="file_logger" /> </root></configuration>

*左右滑动查看更多


第一步完成通过,分析一下。


该配置文件指定了一个名为 "file_logger" 的文件记录器,它将日志记录到指定的文件 "logs/vikingbank.log" 中。日志消息的格式由<pattern>元素内的格式字符串定义。具体的格式包括日期、日志级别、日志记录器名称、日志消息以及可选的异常信息。


根日志记录器(root logger)的日志级别被设置为 "info",表示只记录 info 级别及以上的日志消息。而根日志记录器的输出被关联到 "file_logger" 文件记录器,因此日志消息将被输出到指定的日志文件中。




4、步骤二 记录信息事件

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

既然Logback已经配置好了,让我们开始捕获日志事件吧!在InvoiceController.java文件中,有一个用于创建新发票的方法。如果成功,将在控制台上输出一个包含事件的来源和内容的消息,但这也应该添加到日志中。


4.1 task1

在控制器级别添加 Lombok Slf4j 注释来为此类创建记录器。 


按照要求修改代码。

InvoiceController.java
package vikingbank.web.controllers;
import org.springframework.http.HttpStatus;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.ResponseStatus;import vikingbank.web.entities.Invoice;import vikingbank.web.mappers.InvoiceMapper;import vikingbank.web.models.CreateInvoiceDto;import vikingbank.web.services.InvoiceService;import vikingbank.web.services.VikingBankUserService;
import java.security.Principal;import lombok.extern.slf4j.Slf4j;
@Slf4j@Controllerpublic class InvoiceController { private final InvoiceService invoiceService; private final VikingBankUserService vikingBankUserService; private final InvoiceMapper invoiceMapper;
/** * InvoiceController的构造函数,用于依赖注入 * * @param invoiceService 发票服务 * @param vikingBankUserService VikingBank用户服务 * @param invoiceMapper 发票对象映射器 */ public InvoiceController(InvoiceService invoiceService, VikingBankUserService vikingBankUserService, InvoiceMapper invoiceMapper) { this.invoiceService = invoiceService; this.vikingBankUserService = vikingBankUserService; this.invoiceMapper = invoiceMapper; }
/** * 创建发票 * * @param dto 创建发票的数据传输对象 * @param principal 当前用户的主体对象 * @param model 数据模型 * @return 如果创建成功,返回 "invoice/index" 视图;如果创建失败,返回 "invoice/view" 视图 */ @ResponseStatus(HttpStatus.CREATED) @PostMapping("/invoice/create") public String create(@RequestBody CreateInvoiceDto dto, Principal principal, Model model) { var user = this.vikingBankUserService.findUserByEmail(principal.getName()); try { // 将数据传输对象转换为实体对象 Invoice invoice = invoiceMapper.toEntity(dto); // 保存发票 invoiceService.saveInvoice(invoice); // 记录创建发票的用户ID和发票ID到日志中 log.info("User with id: {} has created a new invoice with id: {}", user.getId(), invoice.getId());
return "invoice/index"; // 如果创建成功,返回 "invoice/index" 视图 } catch (Exception exception) { log.error("Creating an invoice for user with id: {} failed!", user.getId()); model.addAttribute("invoice", dto);
return "invoice/view"; // 如果创建失败,返回 "invoice/view" 视图 } }}

*左右滑动查看更多


4.2 task2

  • 使用log关键字调用记录器;

  • 为此信息事件选择正确的日志记录级别; 

  • 添加日志消息:使用与控制台日志相同的消息。

Step Solution import lombok.extern.slf4j.Slf4j; @Slf4j@Controllerpublic class InvoiceController { log.info("User with id: {} has created a new invoice with id: {}", user.getId(), invoice.getId());

*左右滑动查看更多


整理后得到:

InvoiceController.java package vikingbank.web.controllers; import org.springframework.http.HttpStatus;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.ResponseStatus;import vikingbank.web.entities.Invoice;import vikingbank.web.mappers.InvoiceMapper;import vikingbank.web.models.CreateInvoiceDto;import vikingbank.web.services.InvoiceService;import vikingbank.web.services.VikingBankUserService; import java.security.Principal;import lombok.extern.slf4j.Slf4j; @Slf4j@Controllerpublic class InvoiceController {    private final InvoiceService invoiceService;    private final VikingBankUserService vikingBankUserService;    private final InvoiceMapper invoiceMapper;     /**     * InvoiceController的构造函数,用于依赖注入     *     * @param invoiceService        发票服务     * @param vikingBankUserService VikingBank用户服务     * @param invoiceMapper         发票对象映射器     */    public InvoiceController(InvoiceService invoiceService,                             VikingBankUserService vikingBankUserService,                             InvoiceMapper invoiceMapper) {        this.invoiceService = invoiceService;        this.vikingBankUserService = vikingBankUserService;        this.invoiceMapper = invoiceMapper;    }     /**     * 创建发票     *     * @param dto       创建发票的数据传输对象     * @param principal 当前用户的主体对象     * @param model     数据模型     * @return 如果创建成功,返回 "invoice/index" 视图;如果创建失败,返回 "invoice/view" 视图     */    @ResponseStatus(HttpStatus.CREATED)    @PostMapping("/invoice/create")    public String create(@RequestBody CreateInvoiceDto dto, Principal principal, Model model) {        var user = this.vikingBankUserService.findUserByEmail(principal.getName());        try {            Invoice invoice = invoiceMapper.toEntity(dto);            invoiceService.saveInvoice(invoice);            log.info("User with id: {} has created a new invoice with id: {}", user.getId(), invoice.getId());             return "invoice/index"; // 如果创建成功,返回 "invoice/index" 视图        } catch (Exception exception) {            log.error("Creating an invoice for user with id: {} failed!", user.getId());            model.addAttribute("invoice", dto);             return "invoice/view"; // 如果创建失败,返回 "invoice/view" 视图        }    }}

*左右滑动查看更多

提交通过。




5、步骤三 记录异常

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足


实施基于角色的身份验证VikingBank 有端点来查看其日志。 这些应该只对具有 DEV 和/或 ADMIN 角色的用户可用。 让我们使用基于角色的授权来限制谁可以访问哪些日志。


5.1 task1

与上一步类似:

  • 调用记录器,并选择合适的记录级别; 

  • 添加现有日志消息作为第一个参数; 

  • 添加异常作为第二个参数。

Step Solution log.error("Creating an invoice for user with id: {} failed!", user.getId(), exception);

*左右滑动查看更多

修改下代码:

InvoiceController.java package vikingbank.web.controllers; import org.springframework.http.HttpStatus;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.ResponseStatus;import vikingbank.web.entities.Invoice;import vikingbank.web.mappers.InvoiceMapper;import vikingbank.web.models.CreateInvoiceDto;import vikingbank.web.services.InvoiceService;import vikingbank.web.services.VikingBankUserService; import java.security.Principal;import lombok.extern.slf4j.Slf4j; @Slf4j@Controllerpublic class InvoiceController {    private final InvoiceService invoiceService;    private final VikingBankUserService vikingBankUserService;    private final InvoiceMapper invoiceMapper;     /**     * InvoiceController的构造函数,用于依赖注入     *     * @param invoiceService        发票服务     * @param vikingBankUserService VikingBank用户服务     * @param invoiceMapper         发票对象映射器     */    public InvoiceController(InvoiceService invoiceService,                             VikingBankUserService vikingBankUserService,                             InvoiceMapper invoiceMapper) {        this.invoiceService = invoiceService;        this.vikingBankUserService = vikingBankUserService;        this.invoiceMapper = invoiceMapper;    }     /**     * 创建发票     *     * @param dto       创建发票的数据传输对象     * @param principal 当前用户的Principal对象     * @param model     Spring MVC的Model对象     * @return 发票页面的视图名称     */    @ResponseStatus(HttpStatus.CREATED)    @PostMapping("/invoice/create")    public String create(@RequestBody CreateInvoiceDto dto, Principal principal, Model model) {        var user = this.vikingBankUserService.findUserByEmail(principal.getName());        try {            Invoice invoice = invoiceMapper.toEntity(dto);            invoiceService.saveInvoice(invoice);            log.info("User with id: {} has created a new invoice with id: {}", user.getId(), invoice.getId());             return "invoice/index"; // 如果创建成功,返回 "invoice/index" 视图        } catch (Exception exception) {            log.error("Creating an invoice for user with id: {} failed!", user.getId(), exception);            model.addAttribute("invoice", dto);             return "invoice/view"; // 如果创建失败,返回 "invoice/view" 视图        }    }}

*左右滑动查看更多

提交通过,分析一下。

该代码片段是一个控制器类,用于处理发票相关的请求。以下是对每个方法和构造函数的详细注释:

  • InvoiceController的构造函数:用于依赖注入相关的服务和映射器对象。

  • create()方法:在/invoice/create端点上创建发票。接收CreateInvoiceDto作为请求体,使用Principal对象获取当前用户的信息。将数据传输对象转换为实体对象,并调用invoiceService保存发票。如果创建成功,返回 invoice/index 视图;如果创建失败,返回 invoice/view 视图。同时,在日志中记录创建发票的用户ID和发票ID。 

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足


总结



当必须弄清楚哪些功能以某种方式运行时,跟踪应用程序中的日志事件非常有用。 重要的是要知道:

  • 不要记录太多,也不要记录太少。

  • 考虑要记录哪些数据,不记录哪些数据。

  • 选择正确的日志记录级别。

(未完待续)

插播一条招聘信息


一、安全研究工程师实习生(24/25届)

工作地点:深圳
岗位职责:

1、具有较强的责任感、具备能够独立的开展工作的能力、自学能力强、做事踏实认真; 
2、对防御对抗、反溯源、攻击利用等相关红队工具进行研究和开发;
3、熟悉OWASP TOP 10,具有网络安全、系统安全、Web安全等方面的理论基础;
4、熟悉常见编程语言中的一种(Java、Python、PHP、GO),并能够熟练写出针对性的测试脚本;
5、参与区域内网渗透测试、代码审计、红蓝对抗活动、最新漏洞动态跟踪及复现、风险评估、客户培训等工作;
6、主要参与新服务、新技术创新服务的研究;
7、根据ATT&CK框架梳理研究相关TPPs,并形成对应的检测规则。
加分项:
1、具有渗透测试经验或逆向分析能力或溯源分析能力,曾经参与过大型的红蓝对抗项目;
2、熟悉Java、Python、PHP、GO等编程,并有良好的编程习惯和丰富的代码经验;
3、具备钻研精神,愿意在安全领域做出技术突破;
4、具有较强的责任感、具备能够独立的开展工作的能力、自学能力强、做事踏实认真; 

二、代码审计工程师实习生(24/25届)
工作地点:深圳
岗位职责:

1、跟踪和分析业界最新安全漏洞。
2、挖掘Java、PHP程序中未知的安全漏洞和代码缺陷,并对漏洞进行验证,编制安全加固报告;
3、主要参与新服务、新技术创新服务的研究;
任职要求:
1、对JAVA/PHP编程有较深入的了解,具备较强的Java/PHP代码审计能力,有丰富实战能力;
2、熟悉JAVA/PHP主流框架,具备有一定的编程能力;
3、深入理解常见安全漏洞产生原理及防范方法;
4、熟练掌握源代码测试工具及测试流程,有CNVD、CNNVD等漏洞证书、CVE或CTF比赛获奖者者优先。
5、熟悉主流的源代码审计工具;
6、思路清晰,具有优秀的分析、解决问题的能力,有良好的学习能力及团队协作能力;
7、具备较强的沟通能力、抗压能力,团队合作精神及钻研精神。


简历投递可扫描本文末二维码添加小编微信,或直接发送至邮箱[email protected]


往期回顾

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足


关于安恒信息安全服务团队
安恒信息安全服务团队由九维安全能力专家构成,其职责分别为:红队持续突破、橙队擅于赋能、黄队致力建设、绿队跟踪改进、青队快速处置、蓝队实时防御,紫队不断优化、暗队专注情报和研究、白队运营管理,以体系化的安全人才及技术为客户赋能。

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足


原文始发于微信公众号(安恒信息安全服务):九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月13日23:11:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   九维团队-绿队(改进)| Java Spring编码安全系列之日志记录和监控不足http://cn-sec.com/archives/2103316.html

发表评论

匿名网友 填写信息