常见fortify漏洞与整改规则

背景

针对于代码质量管控来说,代码安全扫描作为其中极其重要的一环,是一项必不可少的且能够有效把控代码质量的操作。其中fortify,作为比较常用的扫描工具之一,尝尝被用来进行此环节的把关。
为了能够在代码编写过程中,自然的遵循代码安全规范,所以此处列举一些在代码安全扫描中较为常见的问题。

规则清单

Path Maniputation

规则描述
当满足以下两个条件时,就会产生 path manipulation 错误:

  1. 攻击者可以指定某一文件系统操作中所使用的路径。

  2. 攻击者可以通过指定特定资源来获取某种权限,而这种权限在一般情况下是不可能获得的。

例如,在某一程序中,攻击者可以获得特定的权限,以重写指定的文件或是在其控制的配置环境下运行程序。

在这种情况下,攻击者可以指定通过 AttachmentController.java 中第 155 行的 getParameter() 进入程序的值,这一数值可以通过 AttachmentController.java 中第 205 行的 File() 访问文件系统资源。

例 1: 下面的代码使用来自于 HTTP 请求的输入来创建一个文件名。程序员没有考虑到攻击者可能使用像“…/…/tomcat/conf/server.xml”一样的文件名,从而导致应用程序删除它自己的配置文件。

String rName = request.getParameter("reportName");
File rFile = new File("/usr/local/apfr/reports/" + rName);
rFile.delete();

解决方案
进行文件名校验

    public static final Character[] INVALID_WINDOWS_SPECIFIC_CHARS = {'"', '`', '<', '>', '?', '|','\000'};
    public static boolean validateStringFilenameUsingContains(String filename) {
        if (filename == null || filename.isEmpty() || filename.length() > 255) {
            return false;
        }
        return Arrays.stream(INVALID_WINDOWS_SPECIFIC_CHARS)
                .noneMatch(ch -> filename.contains(ch.toString()));
    }
public static boolean validateFileName(String fileName) {
        // Check if the file name is null or empty
        if (fileName == null || fileName.isEmpty()) {
            return false;
        }

        // Check if the file name contains any invalid characters
        String invalidChars = "[/\\\\?%*:|\"<>]";
        if (fileName.matches(".*" + invalidChars + ".*")) {
            return false;
        }

        // Check if the file name ends with a period or space
        if (fileName.endsWith(".") || fileName.endsWith(" ")) {
            return false;
        }

        // Check if the file name is too long
        int maxFileNameLength = 255;
        if (fileName.length() > maxFileNameLength) {
            return false;
        }

        // If all checks pass, the file name is valid

        return true;
    }

Header Manipulation

规则描述
以下情况中会出现 Header Manipulation 漏洞:

  1. 数据通过一个不可信赖的数据源进入 Web 应用程序,最常见的是 HTTP 请求。

  2. 数据包含在一个 HTTP 响应头文件里,未经验证就发送给了 Web 用户。

如同许多软件安全漏洞一样,Header Manipulation 只是通向终端的一个途径,它本身并不是终端。从本质上看,这些漏洞是显而易见的:一个攻击者将恶意数据传送到易受攻击的应用程序,且该应用程序将数据包含在 HTTP 响应头文件中。

其中最常见的一种 Header Manipulation 攻击是 HTTP Response Splitting。为了成功地实施 HTTP Response Splitting 盗取,应用程序必须允许将那些包含 CR(回车,由 %0d 或 \r 指定)和 LF(换行,由 %0a 或 \n 指定)的字符输入到头文件中。攻击者利用这些字符不仅可以控制应用程序要发送的响应剩余头文件和正文,还可以创建完全受其控制的其他响应。

如今的许多现代应用程序服务器可以防止 HTTP 头文件感染恶意字符。例如,如果尝试使用被禁用的字符设置头文件,最新版本的 Apache Tomcat 会抛IllegalArgumentException。如果您的应用程序服务器能够防止设置带有换行符的头文件,则其具备对 HTTP Response Splitting 的防御能力。然而,单纯地过滤换行符可能无法保证应用程序不受 Cookie Manipulation 或 Open Redirects 的攻击,因此必须在设置带有用户输入的 HTTP 头文件时采取措施。

示例:下列代码片段会从 HTTP 请求中读取网络日志项的作者名字 author,并将其置于一个 HTTP 响应的 cookie 头文件中。

String author = request.getParameter(AUTHOR_PARAM);
Cookie cookie = new Cookie(""author"", author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);

假设在请求中提交了一个字符串,该字符串由标准的字母数字字符组成,如“Jane Smith”,那么包含该 cookie 的 HTTP 响应可能表现为以下形式:

HTTP/1.1 200 OK

Set-Cookie: author=Jane Smith

然而,因为 cookie 值来源于未经校验的用户输入,所以仅当提交给 AUTHOR_PARAM 的值不包含任何 CR 和 LF 字符时,响应才会保留这种形式。如果攻击者提交的是一个恶意字符串,比如“Wiley Hacker\r\nHTTP/1.1 200 OK\r\n…”,那么 HTTP 响应就会被分割成以下形式的两个响应:

HTTP/1.1 200 OK

Set-Cookie: author=Wiley Hacker

HTTP/1.1 200 OK

显然,第二个响应已完全由攻击者控制,攻击者可以用所需的头文件和正文内容构建该响应。攻击者可以构建任意 HTTP 响应,从而发起多种形式的攻击,包括:cross-user defacement、网络和浏览器缓存中毒、cross-site scripting 和 page hijacking。

用户信息涂改:攻击者可以向一个易受攻击的服务器发出一个请求,导致服务器创建两个响应,其中第二个响应可能会被曲解为对其他请求的响应,而这一请求很可能是与服务器共享相同 TCP 连接的另一用户发出的。这种攻击可以通过以下方式实现:攻击者诱骗用户,让他们自己提交恶意请求;或在远程情况下,攻击者与用户共享同一个连接到服务器(如共享代理服务器)的 TCP 连接。最理想的情况是,攻击者只能通过这种做法让用户相信自己的应用程序已经遭受了黑客攻击,进而对应用程序的安全性失去信心。最糟糕的情况是,攻击者可能提供经特殊技术处理的内容,这些内容旨在模仿应用程序的执行方式,但会重定向用户的私人信息(如帐号和密码),将这些信息发送给攻击者。

缓存中毒: 如果多用户 Web 缓存或者单用户浏览器缓存将恶意构建的响应缓存起来,该响应的破坏力会更大。如果响应缓存在共享的 Web 缓存(如在代理服务器中常见的缓存)中,那么使用该缓存的所有用户都会不断收到恶意内容,直到清除该缓存项为止。同样,如果响应缓存在单个用户的浏览器中,那么在清除该缓存项以前,该用户会不断收到恶意内容。然而,影响仅局限于本地浏览器的用户。

跨站脚本攻击:一旦攻击者控制了应用程序传送的响应,就可以选择多种恶意内容来传播给用户。Cross-Site Scripting 是最常见的攻击形式,这种攻击在响应中包含了恶意的 JavaScript 或其他代码,并在用户的浏览器中执行。基于 XSS 的攻击手段花样百出,几乎是无穷无尽的,但通常它们都会包含传输给攻击者的私人数据(如 Cookie 或者其他会话信息)。在攻击者的控制下,指引受害者进入恶意的网络内容;或者利用易受攻击的站点,对用户的机器进行其他恶意操作。对于易受攻击的应用程序用户,最常见且最危险的攻击就是使用 JavaScript 将会话和 authentication 信息返回给攻击者,而后攻击者就可以完全控制受害者的帐号了。

网页劫持:除了利用一个易受攻击的应用程序向用户传输恶意内容,还可以利用相同的根漏洞,将服务器生成的供用户使用的敏感内容重定向,转而供攻击者使用。攻击者通过提交一个会导致两个响应的请求,即服务器做出的预期响应和攻击者创建的响应,致使某个中间节点(如共享的代理服务器)误导服务器所生成的响应,将本来应传送给用户的响应错误地传给攻击者。因为攻击者创建的请求产生了两个响应,第一个被解析为针对攻击者请求做出的响应,第二个则被忽略。当用户通过同一 TCP 连接发出合法请求时,攻击者的请求已经在此处等候,并被解析为针对受害者这一请求的响应。这时,攻击者将第二个请求发送给服务器,代理服务器利用针对受害者(用户)的、由该服务器产生的这一请求对服务器做出响应,因此,针对受害者的这一响应中会包含所有头文件或正文中的敏感信息。

Cookie Manipulation:当与类似 Cross-Site Request Forgery 的攻击相结合时,攻击者就可以篡改、添加、甚至覆盖合法用户的 cookie。

打开重定向:如果允许未验证的输入来控制重定向机制所使用的 URL,可能会有利于攻击者发动钓鱼攻击。

解决方案

     public static String fixFilenameByRegex(String filename) throws UnsupportedEncodingException {
        String regex = "[`~!@#$%^&*()\\+\\=\\{}|\"?><【】\\r\\n]";
        Pattern pa = Pattern.compile(regex);
        Matcher ma = pa.matcher(filename);
        if (ma.find()) {
            filename = ma.replaceAll("");
        }
        return URLEncoder.encode(filename, "UTF-8");
    }

Dynamic Code Evaluation:Unsafe JSON Derialization (动态代码之json反序列化)

规则描述
在运行时对用户控制的对象流进行反序列化,会让攻击者有机会在服务器上执行任意代码、滥用应用程序逻辑和/或导致 Denial of Service。

解决方案

对于json反序列化操作

     /**
     * Dynamic Code Evaluation: Unsafe Deserialization
     */
    public static void checkSerializeStream(String[] args) throws IOException, ClassNotFoundException {
        Bar bar = new Bar("bar-test");
        Foo foo = new Foo("test-foo", bar);

        // write into an array buffer
        ByteArrayBuffer buffer = new ByteArrayBuffer();
        try (ObjectOutputStream serializeStream = new ObjectOutputStream(buffer)) {
            serializeStream.writeObject(foo);
        }

        try (ValidatingObjectInputStream stream = new ValidatingObjectInputStream(buffer.newInputStream())) {
            // add validated classes
            stream.accept(Foo.class);
            stream.accept(Bar.class);


            Foo foo2 = (Foo) stream.readObject();
            System.out.println(foo2);
        }
    }

    public static class Foo implements Serializable {
        private String name;
        private Bar bar;

        public Foo(String name, Bar bar) {
            this.name = name;
            this.bar = bar;
        }

        @Override
        public String toString() {
            return "Foo{" +
                    "name='" + name + '\'' +
                    ", bar=" + bar +
                    '}';
        }
    }

    public static class Bar implements Serializable {
        private String name;

        public Bar(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Bar{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

对于redisTemplate初始化设置

    @Bean
    public RedisTemplate<String, Serializable> limitRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值