资讯动态

Java与PHP源码审计双线实战:从应急响应到漏洞溯源

发布时间:2026/9/19 19:21:47 来源:尧图企业网站定制
1. 从一次应急响应说起为什么要同时掌握Java和PHP两条审计线很多做安全的人都有一个习惯就是给自己贴标签——我是搞Java安全的或者我是搞PHP的。这个标签在平时接项目的时候确实能帮你聚焦但真到了应急响应或者红队复盘的时候你会发现攻击者从来不管你擅长什么。我印象很深的一次某业务系统对外暴露的是一个PHP写的管理后台但后台调用的核心业务接口是Spring Boot写的。攻击者先是通过PHP后台的一个文件上传点拿到了WebShell然后顺着配置文件里的数据库连接串横向到了Java服务最后用Java端的反序列化漏洞把整个内网打穿了。整个链路横跨两种语言如果你只懂一边排查到一半就断了。这就是我想写这篇东西的原因。Java-PHP源码审计不是一个两选一的命题而是一套需要打通的方法论。Java的生态庞大、框架层次深、编译产物多审计时更像是在做逆向配置梳理PHP的生态碎片化、动态特性强、框架版本差异大审计时更像是在做模式匹配数据流追踪。两者的思路有交集但侧重点完全不同。把这两条线都摸清楚你在面对混合技术栈的目标时才能做到心里有底。这篇文章适合谁看如果你已经能看懂基本的Java和PHP代码知道什么是SQL注入、什么是反序列化但拿到一整套源码时不知道从哪里下手那这篇内容就是给你准备的。我会从审计流程的搭建讲起然后分别拆Java和PHP的审计要点再讲漏洞溯源的技巧最后聊一些实战中踩过的坑。全程不堆概念只讲能直接上手用的东西。提示本文讨论的所有技术内容仅用于授权的安全测试和代码审计工作请务必在合法合规的前提下使用。2. 审计前的准备工作环境、工具与心态2.1 拿到源码后的第一件事不是打开IDE很多人拿到源码压缩包第一反应是解压然后用IDE打开开始一个文件一个文件地看。这个习惯非常低效。我自己的流程是先不打开任何代码文件而是先做结构侦察。具体怎么做先在命令行里把目录树打出来看整体结构。对于Java项目你要关注的是有没有pom.xml或者build.gradle有几个模块WEB-INF下面有没有web.xml有没有application.properties或application.yml。对于PHP项目你要关注的是入口文件在哪index.php、admin.php有没有composer.json有没有框架特征目录比如application/、thinkphp/、vendor/laravel/。这一步的目的是建立地图感。你得先知道这个项目有多大、用了什么框架、大概有哪些功能模块然后再决定从哪里切入。上来就啃代码很容易迷失在细节里。2.2 工具链的搭配逻辑工具不在多在于搭配合理。我常用的组合是这样的用途Java方向PHP方向依赖梳理Maven/Gradle依赖树、mvn dependency:treecomposer.lock解析、composer show静态扫描CodeQL、Semgrep、FortifyRIPS、Semgrep、Psalm反编译JD-GUI、CFR、Procyon无PHP是解释型动态调试IDEA远程Debug、ArthasXdebug VSCode/PHPStorm辅助搜索grep、ripgrep、Everythinggrep、ripgrep、ack这里重点说两个容易被忽略的工具。一个是ripgrep它比grep快得多而且默认支持递归搜索和.gitignore过滤在大型项目里搜关键字效率极高。另一个是Semgrep它支持自定义规则你可以把常见的危险模式写成规则批量扫描整个项目。比如你可以写一条规则匹配所有Runtime.getRuntime().exec(的调用然后快速定位命令执行点。注意静态扫描工具的结果只能作为线索不能作为结论。工具报出来的问题需要人工确认工具没报出来的问题不代表不存在。我见过太多人过度依赖工具结果漏掉了最关键的逻辑漏洞。2.3 心态准备审计是体力活也是脑力活说句实话代码审计的很大一部分工作是重复性的——搜索关键字、追踪变量、确认过滤逻辑。这个过程很枯燥但你不能跳过。我的经验是把审计分成粗筛和精读两个阶段。粗筛阶段用工具和关键字快速过一遍标记出可疑点精读阶段针对可疑点逐个深入分析。这样既能保证覆盖面又不会在无关代码上浪费太多时间。另外一定要养成做笔记的习惯。审计过程中你会遇到大量的类名、方法名、参数名光靠脑子记不住。我一般用Markdown记一个审计日志记录每个可疑点的位置、分析结论、待确认事项。这个日志在后续写报告的时候会省你很多事。3. Java源码审计的核心切入点3.1 先看配置文件再看代码Java项目的配置文件里藏着大量信息。application.yml或者application.properties里可能有数据库密码、Redis密码、第三方API密钥。web.xml里能看到所有的Servlet映射和Filter配置。pom.xml里能看到所有依赖的版本号直接对应已知漏洞。我审计Java项目的时候第一步永远是看依赖版本。比如看到fastjson 1.2.24不用看代码就知道大概率有反序列化问题看到shiro 1.2.4就知道默认密钥的问题可能存在看到log4j-core 2.14.1就知道Log4Shell的影响范围。这一步能帮你快速锁定高价值目标。!-- 典型的pom.xml依赖片段版本号是审计的第一线索 -- dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.24/version /dependency3.2 入口点梳理从Controller到ServletJava Web应用的入口点主要有几类Spring MVC的Controller/RestController、Servlet的doGet/doPost、Filter和Interceptor、WebSocket端点、定时任务等。你需要把这些入口全部找出来然后逐个分析每个入口的参数处理逻辑。对于Spring项目搜索RequestMapping、GetMapping、PostMapping这些注解就能快速定位所有HTTP入口。对于传统Servlet项目搜索extends HttpServlet或者web.xml里的servlet-mapping。找到入口之后重点看参数是怎么接收的——是RequestParam、PathVariable还是RequestBody不同类型的参数处理方式不同安全风险也不同。3.3 数据流追踪从Source到Sink这是Java审计的核心方法论。Source就是用户可控的输入点Sink就是危险操作点。你要做的是追踪数据从Source到Sink的完整路径看中间有没有有效的过滤。常见的Source包括HTTP请求参数、HTTP头、Cookie、文件上传内容、数据库读取的数据二次注入场景。常见的Sink包括Runtime.exec()、ProcessBuilder、Statement.execute()、ObjectInputStream.readObject()、FileOutputStream、Response.getWriter().write()等。追踪的过程中要特别注意净化函数——也就是那些做了安全处理的函数。比如Integer.parseInt()会把字符串转成整数天然免疫SQL注入StringEscapeUtils.escapeHtml4()会做HTML转义能防XSS。但要注意净化函数用错了地方或者用错了顺序等于没净化。// 看似安全的写法实际上如果id来自用户输入且未做类型校验仍可能出问题 String id request.getParameter(id); int userId Integer.parseInt(id); // 这里如果id不是数字会抛异常但至少防了注入 String sql SELECT * FROM users WHERE id userId;3.4 反序列化Java审计绕不开的坎Java反序列化漏洞是Java安全里最经典也最复杂的一类。审计的时候你要找的是readObject()的调用点然后看能不能控制传入的字节流。常见的触发点包括HTTP请求体直接反序列化、RMI通信、JMS消息、缓存读取等。但找到readObject()只是第一步你还需要确认classpath里有没有可以利用的gadget chain。这就回到了依赖梳理——commons-collections、fastjson、jackson、xstream这些库的特定版本都可能提供gadget。实际审计中我一般会先确认目标环境里有哪些库然后再去构造利用链。3.5 框架特性带来的隐形漏洞Java框架的很多特性在方便开发的同时也引入了安全风险。举几个例子Spring Boot Actuator如果management.endpoints.web.exposure.include配置为*那么/actuator/env、/actuator/heapdump等端点会直接暴露敏感信息。Spring Cloud GatewaySpEL表达式注入问题在特定版本中存在。Shiro默认密钥问题、权限绕过问题。Struts2OGNL表达式注入历史漏洞极多。审计的时候看到这些框架就要条件反射地去查对应版本的已知漏洞。这不是偷懒而是效率最高的做法。4. PHP源码审计的独特打法4.1 PHP项目的入口比Java更分散PHP没有Java那种统一的Servlet容器概念入口文件可能有很多个。一个典型的PHP项目可能有index.php、admin.php、api.php、upload.php等多个入口每个入口都可能直接接收用户输入。所以审计PHP项目的第一步是找全所有入口文件。怎么找搜索$_GET、$_POST、$_REQUEST、$_COOKIE、$_FILES、$_SERVER这些超全局变量的使用位置。每一个使用点都是一个潜在的Source。然后顺着这些变量往下追看数据流向了哪里。4.2 危险函数清单PHP审计的基本功PHP的危险函数比Java更直观因为很多函数本身就是危险的代名词。下面这张表是我审计时必查的漏洞类型危险函数审计要点命令执行system、exec、shell_exec、passthru、popen、proc_open、反引号参数是否可控是否有拼接代码执行eval、assert、preg_replace/e修饰符、create_function、call_user_func参数来源是否可绕过文件包含include、require、include_once、require_once路径是否可控是否允许远程文件操作file_get_contents、file_put_contents、fopen、unlink、rename路径是否可控是否有目录穿越SQL注入mysql_query、mysqli_query、PDO::query是否拼接是否用了预处理反序列化unserialize参数是否可控是否有__wakeup/__destruct魔术方法这张表不是让你死记硬背而是让你在审计时有一个检查清单。看到这些函数就要停下来仔细看参数是怎么传进来的。4.3 框架路由与MVC结构的影响现代PHP项目大多用框架比如Laravel、ThinkPHP、Symfony、Yii。框架会改变审计的方式。以ThinkPHP为例它的路由规则、控制器命名、参数绑定方式都有特定的模式。你需要先理解框架的路由解析逻辑才能知道一个URL对应的是哪个控制器、哪个方法。ThinkPHP历史上出过很多RCE漏洞比如5.0.x的captcha路由问题、5.1.x的request方法调用问题。这些漏洞的根源都是框架在处理用户输入时没有做好过滤。审计ThinkPHP项目时要特别关注app目录下的控制器代码以及route目录下的路由定义。Laravel相对安全一些但也不是没有问题。比如unserialize的使用、env文件的暴露、debug模式下的信息泄露等。审计Laravel项目时要关注config目录、.env文件、routes目录。4.4 文件上传与文件包含PHP的重灾区PHP的文件上传漏洞之所以多很大程度上是因为早期教程里充斥着不安全的写法。很多老项目的上传逻辑是这样的检查文件扩展名如果不在黑名单里就允许上传。这种黑名单机制很容易被绕过——php3、php5、phtml、pht等扩展名可能不在黑名单里但服务器可能仍然会解析。审计文件上传功能时要重点看几个点扩展名检查逻辑、MIME类型检查逻辑、文件内容检查逻辑、上传后的文件存储路径、文件是否可被直接访问。任何一个环节有问题都可能导致GetShell。文件包含漏洞的审计要点是包含的路径是否可控、是否允许远程包含allow_url_include、是否有php://input等伪协议可以利用。本地文件包含LFI配合文件上传往往能形成完整的攻击链。4.5 变量覆盖与魔术方法PHP的暗坑PHP的extract()函数、parse_str()函数、$$可变变量等特性可能导致变量覆盖漏洞。比如一个函数用extract($_GET)把请求参数导入到当前作用域攻击者就可以覆盖函数内部的变量改变程序逻辑。魔术方法是PHP面向对象编程里的特殊方法以__开头比如__construct、__destruct、__wakeup、__toString、__call、__get、__set。这些方法在特定条件下会被自动调用如果里面有不安全的操作就可能被利用。审计PHP反序列化漏洞时核心工作就是找一条从unserialize入口到危险魔术方法的利用链。// 一个典型的魔术方法利用场景 class FileHandler { public $filename; public function __destruct() { // 如果filename可控这里就可能删除任意文件 unlink($this-filename); } } // 攻击者构造序列化数据控制filename属性5. 漏洞溯源从现象反推根因5.1 溯源的本质是逆向数据流漏洞溯源和代码审计的方向是相反的。代码审计是从Source找Sink溯源是从Sink反推Source。当你发现一个漏洞点比如一个命令执行你需要回答几个问题这个危险函数的参数是从哪里来的经过了哪些处理有没有可能被用户控制这个过程需要你对代码的调用关系有清晰的理解。我的做法是从危险函数开始一层一层往上追。比如看到Runtime.exec(cmd)先看cmd变量在当前方法里是怎么赋值的如果是方法参数就找谁调用了这个方法一直追到HTTP入口。5.2 利用调用链分析工具提效手工追调用链很累尤其是大型项目。这时候可以用一些工具辅助。IDEA的Find Usages功能可以快速找到方法的所有调用点。CodeQL可以写查询语句自动追踪数据流。Arthas可以在运行时观察方法的调用栈。但工具不是万能的。Java的动态代理、反射调用、Spring的依赖注入等机制会让静态分析工具看走眼。所以工具给出的结果需要人工验证不能全信。5.3 日志与流量溯源的另一条路有时候你拿不到源码或者源码不完整这时候日志和流量就是重要的线索。Web访问日志能告诉你攻击者访问了哪些URL、传了什么参数。如果开启了POST日志还能看到请求体。结合WAF日志、数据库日志、系统日志可以还原出攻击的完整过程。我处理过一次应急目标是一个PHP站点源码被加密了用了类似dezend的工具。我们拿不到明文源码但通过分析Nginx的access log发现攻击者访问了一个异常的URL参数里带有明显的命令执行特征。顺着这个URL我们在加密源码里定位到了对应的文件虽然代码是加密的但通过行为分析还是确认了漏洞点。5.4 常见漏洞的溯源模式不同类型的漏洞溯源时有不同的关注点SQL注入追参数到SQL语句的拼接过程看有没有预处理、有没有转义。命令执行追参数到命令拼接的过程看有没有白名单校验。反序列化追字节流到readObject/unserialize的路径看有没有过滤。文件上传追文件名和文件内容到存储路径的过程看校验逻辑。SSRF追URL参数到HTTP请求发起的过程看有没有协议限制和IP限制。掌握这些模式之后溯源就变成了按图索骥效率会高很多。6. 实战中踩过的坑与经验总结6.1 不要忽略业务逻辑漏洞代码审计工具擅长发现技术型漏洞注入、XSS、反序列化但对业务逻辑漏洞几乎无能为力。比如越权访问、支付逻辑绕过、验证码复用、密码重置逻辑缺陷等。这些漏洞需要你理解业务站在攻击者的角度思考这个功能有没有可能被滥用。我审计过一个电商系统技术层面做得很扎实参数化查询、输出编码、CSRF Token都有。但它的优惠券领取接口没有做频率限制也没有校验用户是否已经领取过。结果就是可以无限领取优惠券造成资损。这种漏洞工具是扫不出来的。6.2 版本信息比代码本身更重要很多时候你不需要逐行读代码只需要确认组件版本就能判断是否存在已知漏洞。所以审计时一定要花时间梳理依赖版本。Java看pom.xml和lib目录PHP看composer.lock和vendor目录。把版本号和CVE数据库比对能快速找到高价值目标。6.3 过滤逻辑要看上下文看到一个过滤函数不要急着下结论说这里安全了。要看这个过滤函数是在什么上下文里使用的。比如htmlspecialchars()在HTML上下文里能防XSS但如果输出点在JavaScript代码块里htmlspecialchars()就不够了需要json_encode()。再比如addslashes()能防SQL注入但如果数据库连接使用了GBK编码宽字节注入就可能绕过它。6.4 审计报告要能复现写审计报告的时候不要只写这里存在SQL注入要写清楚漏洞文件路径、漏洞代码行号、触发URL、请求方法、请求参数、Payload示例、预期结果。这样开发人员才能快速定位和修复复测人员也能快速验证。我见过太多报告只写一个漏洞名称开发看了半天不知道说的是哪里。6.5 持续学习漏洞库和社区代码审计是一个需要持续学习的方向。新的框架、新的组件、新的漏洞类型层出不穷。我的习惯是定期看几个地方GitHub上的安全公告、CVE数据库、Seebug漏洞平台、先知社区、安全客。不需要每篇都精读但要知道最近有什么新漏洞、影响哪些版本。这样在审计时看到相关组件就能立刻联想到。6.6 关于自动化的一些思考现在有很多自动化代码审计工具比如CodeQL、Fortify、Checkmarx。这些工具确实能提高效率但它们不能替代人工审计。工具擅长发现模式化的漏洞但面对复杂的业务逻辑、框架特性、绕过技巧时还是需要人来判断。我的建议是把工具当作助手用它来做初筛和辅助追踪但最终的判断和深入分析还是要靠人。另外自己写一些小的脚本也很有用。比如写一个Python脚本批量提取Java项目里所有的RequestMapping路径或者写一个脚本批量检查PHP项目里所有的unserialize调用点。这些脚本不复杂但能省很多时间。7. 两条线打通之后的能力边界把Java和PHP两条审计线都摸清楚之后你会发现自己的能力边界扩展了很多。面对一个混合技术栈的目标你能快速判断从哪里切入、用什么方法、重点关注什么。面对一个未知的源码包你能在短时间内建立起地图感找到高价值的审计点。面对一个应急事件你能从日志和流量出发逆向还原攻击链路。但也要清楚自己的边界。代码审计只是安全测试的一个环节它不能替代渗透测试、不能替代配置核查、不能替代安全开发生命周期管理。审计发现的漏洞需要修复和验证审计没发现的漏洞不代表不存在。保持敬畏保持学习才是这个方向能走远的关键。最后分享一个我自己的小习惯每次审计完一个项目我会把遇到的典型漏洞模式、绕过技巧、工具用法整理成一个笔记。时间长了这个笔记就成了我自己的审计知识库。下次遇到类似的项目翻一翻笔记往往能快速找到思路。这个习惯看起来笨但确实管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价