大聪明教你学Java | Spring Boot 项目设置 X-Content-Type-Options 响应头

Posted 不肯过江东丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大聪明教你学Java | Spring Boot 项目设置 X-Content-Type-Options 响应头相关的知识,希望对你有一定的参考价值。

前言

我们在开发应用系统的时候,总会遇到各种各样的漏洞,即便是项目上线后,甲方霸霸也会找专门搞安全的公司来对我们的应用系统进行扫描,扫描完后或多或少也会出现一些漏洞,我们就得加班对这些漏洞进行修复…

大聪明开发的应用系统已经上线三年了,然而就在昨天依然被扫描出了一个漏洞 —— 远程 Web 系统应用程序不采取措施来减轻一类 Web 应用程序漏洞,说白了就是远程网络应用程序不设置 X-Content-Type 响应头。刚看到扫描报告的时候还真有点麻爪,不知道如何下手,最后经过一番努力还是成功的修复了这个漏洞✌,那么借此机会,大聪明就和大家分享一下如何修复此类漏洞😊。

漏洞修复

漏洞简介

首先我们先简单说说 Content-Type 响应头有什么用👇

互联网上的资源类型有很多种,通常浏览器会根据响应头的 Content-Type 字段来分辨这些资源的类型。比如 “text/html” 代表的是 html 类型的文件, “image/png” 则代表的是 png 图片,“text/css” 则是 css 样式文件。然而有些资源的 Content-Type 字段是错的或者未定义。此时某些浏览器就会启用 MIME-sniffing 来猜测该资源的类型(但是猜测的并不一定准确),猜测后再解析该文件的内容并开始执行。如果此时我们给一个 html 文件指定 Content-Type 为 “text/plain” ,那么在某些浏览器中这个文档依然会被当做 html 文件来解析。因为某些浏览器存在这个特性,攻击者就利用这一特行让原本应该解析为图片的请求被解析为 javascript 代码,从而实现一些不可告人的目的🈲。为了防止应用系统被攻击,我们就需要将应用系统的页面都设置上响应头 —— X-Content-Type-Options:nosniff。

说完 Content-Type 响应头,可能有一些小伙伴会想到另外两个响应头—— X-Frame-Options 和 X-XSS-Protection,那么我们这里也简单的说几句👇

  1. X-Frame-Options 响应头:为了减少点击劫持(Clickjacking)而引入的 X-Frame-Options 响应头,这个响应头支持三种配置:① DENY(不允许被任何页面嵌入);② SAMEORIGIN(不允许被本域以外的页面嵌入);③ ALLOW-FROM URL(不允许被指定的域名以外的页面嵌入)。
  2. X-XSS-Protection响应头:该响应头的作用是防范 XSS 攻击,现在主流浏览器都支持该响应头,并且都默认开启了 XSS 保护,该响应头也是有三种配置:① 0(禁用XSS保护);② 1(启用XSS保护);③ 1;mode=block(启用XSS保护,并在检查到XSS攻击时,停止渲染页面)。不过有一点是需要注意的,虽然浏览器提供的防止 XSS 攻击保护机制并不完美,但是开启后仍然可以给予一定的防护能力,所以没有特殊的情况,我们一定不要关闭该响应头。

漏洞修复方法

修复此类漏洞的方式也很简单,下面就针对两种常见情况和大家分享一下修复此类漏洞的方法~

情况一:应用系统部署在 Tomcat 下

如果我们的应用系统部署在了 Tomcat 下,那么我们只需要在 Tomcat 下的 web.xml 配置文件中增加几行配置即可(文件路径:Tomcat 根目录/conf/web.xml)👇

<filter>
    <filter-name>httpHeaderSecurity</filter-name>
    <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
    <init-param>
        <param-name>antiClickJackingOption</param-name>
        <param-value>SAMEORIGIN</param-value>
    </init-param>
    <async-supported>true</async-supported>
</filter>

<filter-mapping>
    <filter-name>httpHeaderSecurity</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

如果我们使用的是低版本的 Tomcat ,有可能会找不到 org.apache.catalina.filters.HttpHeaderSecurityFilter 相关的 jar 包,此时我们可以去高版本的 Tomcat 中将对应的 HttpHeaderSecurityFilter 文件复制到低版本的 Tomcat 中。配置成功后我们需要重启 Tomcat 服务,增加的配置才会生效。如下图所示👇

情况二:Spring Boot 项目生成的 Jar 包(Tomcat 集成在 Jar 包中)

如果我们使用 Spring Boot 开发应用系统,很多小伙伴都会选择将项目打成 Jar 包来进行部署,此时我们就没办法通过修改 Tomcat 下的 web.xml 配置文件来修复此类漏洞了(毕竟 Spring Boot 帮我们把 Tomcat 集成在项目中了,想找 web.xml 配置文件都找不到😂)。针对此类情况,我们就可以使用过滤器来解决问题👇

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 使用过滤器增加响应头(以 X-Content-Type-Options:nosniff 为例)
 * @description: AddResponseHeaderFilter 
 * @author: 庄霸.liziye
 * @create: 2022-03-03 15:21
 **/
@Component
public class AddResponseHeaderFilter extends OncePerRequestFilter 
 
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
        FilterChain filterChain) throws ServletException, IOException 
        httpServletResponse.addHeader("X-Content-Type-Options", "nosniff");
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    

过滤器增加后,我们重启一下项目看看效果👇


解决此类漏洞方式是不是跟简单 (●’◡’●) ,通过这么几行代码就能让我们的应用系统的安全性提升一大截✌~

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西

大聪明教你学Java | 深入浅出聊递归(以汉诺塔为例)

前言

不知道各位小伙伴是什么时候接触到了递归,记得我第一次学习递归的时候是通过“汉诺塔”的例子来学习的,“汉诺塔”的代码虽然没几行,却也困扰了我很久(脑子太笨了,转不过来😂),经过了很长一段时间以后才算是理解了其中的逻辑,今天就和大家深入浅出的聊聊什么是递归,分享一下我学习递归的一些经验和心得。

汉诺塔

汉诺塔(又称河内塔)源于印度的一个古老传说:大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

后来这个传说就演变成了现在的汉诺塔游戏,这个游戏的规则也很简单👇

  1. 有三根杆子A、B、C,在 A 杆上有若干个圆盘
  2. 每次只能移动一个圆盘,并且小的只能叠在大的上面
  3. 将所有的圆盘从 A 柱子挪到 C 柱子就算成功


知道了游戏规则,接下来我们该思考一下如何完成游戏的最终任务呢~
俗话说“繁琐问题必有猥琐解法”:我有劲💪,直接把圆盘全拿起来放到 C 柱子上就搞定了,区区几个小圆盘还是能拿得动的😂 这个办法确实够“猥琐”,也够简单粗暴,但仅仅是跟大家开个小玩笑,下面我们还是言归正传,正经的分析一下这个游戏的奥秘👇

当 A 柱上仅仅只有1个圆盘时,那么直接将圆盘从 A 柱上移动到 C 石柱上即可。

当 A 柱上有2个圆盘时,把 A 柱上的圆盘从上往下按照大小顺序编为1号和2号,那么要将圆盘全部从 A 柱移动到 C 柱,首先需要将1号圆盘移动到 B 柱,再将2号圆盘从 A 柱移动到 C 柱,最后将1号圆盘从 B 柱移动到 C 柱。

当 A 柱上有3个圆盘时,仍然从上往下按照大小顺序将圆盘编为1号、2号和3号,这时候我们就发现问题变得复杂了一些,所以我们可以将1号和2号圆盘看做一个整体(即“1-2号”圆盘),此时我们需要解决的就是将“1-2号”圆盘和3号圆盘移动到 C 柱的问题,也就是需要先将“1-2号”圆盘移动到 B 柱,再将3号圆盘移动到 C 柱,最后将“1-2号”圆盘移动到 C 柱即可。

以此类推,如果当 A 柱上有 N 个圆盘的时候,我们依然使用“看作整体”的办法来解决问题,我们依然是将 A 柱上的圆盘从上往下按照大小顺序编为1号、2号、3号…N号,我们此时就需要将1号到 N-1 号圆盘看做一个整体(记作“1→N-1”号圆盘),这时候我们就需要先将“1→N-1”号圆盘移动到 B 柱上,再将N号圆盘移动到 C 柱上,最后再把 B 柱上的“1→N-1”号圆盘移动到 C 柱上。我们可以发现当N号圆盘移动到 C 柱后,它就不需要再次移动了,也就是说无论其他的圆盘怎么移动都不需要N号圆盘的参与了,这也就代表了我们看作的整体(“1→N-1”号圆盘)和单独的个体(N号圆盘)相互独立且互不影响,所以我们就可以把“1→N-1”号圆盘按照上面的逻辑继续拆分,最终即可将所有的圆盘从 A 柱移动到 C 柱上。

我们可以对上面的分析做一个总结👇

  1. 第一种情况就是只有一个圆盘的时候,我们只需要操作一步,直接将圆盘从 A 柱移动到 C 柱即可
  2. 当有N个圆盘时,我们的操作过程就分成了三步:①将前N-1个圆盘从 A 柱移动到 B 柱 ②将N号圆盘从 A 柱移动到 C 柱 ③将前N-1个圆盘从 B 柱移动到 C 柱

我们有了上面的总结,就可以写出一个移动汉诺塔的代码👇

/**
 * 汉诺塔
 * @description: HanoiDemo
 * @author: 庄霸.liziye
 * @create: 2022-01-11 10:18
 **/
public class HanoiDemo 
    public void hanoi(int n, String A, String B, String C) 
        if (n == 1) 
            //只有一个圆盘,直接将圆盘从A柱移动到 C柱
            move(n, A, C);
         else 
            //借助C柱将前N-1个圆盘从A柱移动到B柱
            hanoi(n - 1, A, C, B);
            //将N号圆盘从 A 柱移动到 C 柱
            move(n, A, C);
            //借助A柱将前N-1个圆盘从 B 柱移动到 C 柱
            hanoi(n - 1, B, A, C);
        
    

    /**
     * 移动N号圆盘
     **/
    private void move(int n, String A, String C) 
        System.out.println("将"+ n +"号圆盘从" + A + "移动到" + C);
    

    public static void main(String[] args) 
        HanoiDemo hanoiDemo = new HanoiDemo();
        System.out.println("A柱上有3个圆盘时移动的步骤:");
        hanoiDemo.hanoi(3, "A柱子", "B柱子", "C柱子");
    

递归

通过代码可以看出来,我们利用递归的思想成功了解决了汉诺塔的问题,其实递归的思想也很简单,我们只需要注意两点就行:结束条件和让问题规模变小。在汉诺塔的例子中,将所有圆盘从 A 柱移动到 C 柱就是结束条件,我们使用到的“看作整体”的方式就是让问题的规模变小。可能很多人都觉得在学习的过程中要做到“知其然知其所以然”,但是在学习递归的时候却恰恰相反,我们只需要做到“不求甚解”就可以了。学习递归的“猥琐”办法就是舍弃,说的具体一点就是要舍弃掉想理解和跟踪递归全程的想法,否则会让自己陷入到一个死循环里,不仅无法理解递归,还会让自己懵逼。

回归到汉诺塔的例子中,如果此时有人让我们去移动“1→N-1”号圆盘怎么办?我们可能会很头疼,有N-1个圆盘需要我们去移动,这不就把我们累坏了😭,其实解决问题的办法很简单,我们也可以学给我们分配任务的那个人,把工作外包出去,也就是说我们只管N-1号圆盘,其他的“1→N-2”号圆盘都外包给别人,这下我们就成了一个包工头,这回我们是不是就轻松了许多😄。

这时候我们就千万别再管接到外包工作的家伙有多头疼了,丢给他就让他难受去,千万别再折磨自己了。 接到外包工作的人愿意怎么实现就怎么实现去,跟我们没有鸡毛关系,我们只需要关注自己的那个圆盘就好了。或许接到我们的外包工作的人也想省点心,就把“1→N-3”号圆盘外包出去了,自己只管N-2号圆盘…

这最终就成了一个任务链:大包工头→包工头→小包工头→小小包工头→小小小包工头… 当最后一个超小的包工头完成了自己的任务,那么在他之上的各位包工头也就很容易的完成了自己所负责的那部分任务,在各位包工头的共同努力之下,整个任务就被完美的完成啦✌(是不是有点像一句俗话:大懒支小懒🤭)。

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西

以上是关于大聪明教你学Java | Spring Boot 项目设置 X-Content-Type-Options 响应头的主要内容,如果未能解决你的问题,请参考以下文章

大聪明教你学Java | 深入浅出聊递归(以汉诺塔为例)

大聪明教你学Java | Hutool - A set of tools that keep Java sweet

大聪明教你学Java | Log4j 漏洞到底是怎么一回事?Log4j 2.15.0 也不靠谱了...

强烈推荐,建议收藏一万字手把手教你学习Spring Cloud,带你快速入门

强烈推荐,建议收藏一万字手把手教你学习Spring Cloud,带你快速入门

为什么你学了 N 遍 Spring Boot,至今还是学生项目?你的问题在这里 | 原力计划