开发安全培训
安全开发的意义
- 安全是开发的核心前提:在
代码编写与系统设计的全流程中,主动预判并规避潜在安全漏洞是开发工作的基础环节,而非后续补充。这要求开发者将安全思维融入需求分析、编码实现、测试验证等每一步,从源头降低漏洞引入风险。 - 安全的本质是风险可控:
没有绝对的安全是行业共识,安全开发的目标并非追求 “零风险”,而是通过体系化措施将风险压缩至可接受的极低范围,实现 “相对安全” 的稳态。 - 安全投入直接关联核心利益:产品安全工作的深度与广度,直接决定了黑客的攻击成本。每多一分安全加固,都能提升攻击门槛,从而为客户数据安全与公司商业利益增添一道坚固屏障。
- 左移安全显著降低综合成本:将安全考量与问题解决
左移至产品生命周期的早期(如设计阶段、编码初期),实现早规划、早发现、早修复,能大幅降低后期(如上线后)修复漏洞的技术难度与时间成本,更能有效避免漏洞被利用导致的业务中断、数据泄露等重大损失。
安全威胁来自哪里

- 黑/灰产业链:用户信息买卖、订单信息买卖、数据库买卖、电信订单诈骗、黄牛刷单(活动低价商品、优惠券)、0元订单。
- 白帽子黑客
- 竞争对手
- 合作伙伴
- 供应商
- 外包商
- 内部人员
漏洞排行榜
OWASP是什么?

- OWASP 是 Open Web Application Security Project(开放式 Web 应用程序安全项目)的缩写,是一个全球性的、非营利性的网络安全组织,致力于提升 Web 应用程序的安全性。
- OWASP 的核心使命是 “使安全成为软件开发生命周期中不可分割的一部分”。它通过汇聚全球各地的安全专家、开发人员、研究人员和技术爱好者,以开源协作的方式产出免费、公开的安全资源,帮助企业和个人理解、应对 Web 应用面临的安全风险,打破安全知识和工具的壁垒。
OWASP VS XXX TOP10
| OWASP TOP 10 | XXX TOP 10 |
|---|---|
| 失效的访问控制 | 不安全的设计 |
| 加密失败 | 敏感信息泄露 |
| 注入 | 失效的访问控制 |
| 不安全的设计 | 安全配置错误 |
| 安全配置错误 | 使用易受攻击和过时的组件 |
| 使用易受攻击和过时的组件 | 注入 |
| 身份识别和身份验证失败 | 跨站脚本 |
| 软件和数据完整性故障 | 不安全的反序列化 |
| 安全日志记录和监控失败 | 服务端请求伪造 |
| 服务器端请求伪造 | 加密失败 |
OWASP TOP 10高频场景

- 失效的访问控制:用户的访问权限控制不足,攻击者可能未经授权访问敏感数据或执行关键操作。
-
水平越权(同权限组用户互访) 某电商 APP 的 “我的订单” 接口为
GET /api/metacat/XXX?groupId=61,后端仅校验用户是否登录,未校验groupId属于当前登录用户。攻击者修改groupId为他人组号(如 62),即可直接查看其他用户的信息。 -
垂直越权(低权限用户访问高权限功能) 某电网管理系统中,普通员工登录后页面隐藏 “管理员后台” 入口,但管理员后台的核心接口
POST /api/admin/deleteUser未校验用户角色。攻击者通过抓包工具直接构造该接口请求,可删除任意员工账号。 -
未授权访问接口资源 某学校管理系统
Swagger接口文档信息泄露,通过一个个试验发现存在部门信息接口访问无需权限控制,最终通过小红书求助拿到整个学校的全部学生信息和账号密码。




- 加密失败:数据加密过程存在问题,导致敏感信息在传输或存储时面临泄露风险。
- 密码弱哈希存储 某论坛为简化开发,将用户密码用 MD5 算法直接哈希后存储(无盐值)。攻击者获取数据库后,通过 "彩虹表"快速破解大量密码(如
MD5("123456")=e10adc3949ba59abbe56e057f20f883e可直接匹配)。 - 传输层未加密 某小型理财平台仍使用 HTTP 协议传输用户登录数据,攻击者通过"中间人攻击"(如抓包),可直接捕获用户输入的账号密码明文,无需破解即可登录。
- 密钥硬编码与弱加密 某 APP 为保护本地存储的用户 Token,用 AES 算法加密但将密钥硬编码在代码中(
String aesKey = "abcdef1234567890"),且加密模式设为 ECB(电子密码本模式,相同明文加密结果相同)。攻击者反编译 APP 获取密钥后,可轻松解密所有用户 Token。

- 注入:攻击者向应用输入字段注入恶意代码,使应用执行恶意命令或查询,常见的有 SQL 注入、模板注入等。
- 某工业控制系统管理后台登录页面账号处输入
admin' OR '1'='1',使用任意密码可成功登录成admin账户 - 某商城系统使用 Freemarker 模板,攻击者注入
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","calc").start()},可执行任意命令获取服务器权限

- 不安全的设计:在应用程序设计阶段没有充分考虑安全因素,导致系统存在固有弱点。
- 某深圳餐饮App提现的功能设计时,仅校验"用户余额≥提现金额",未设计
幂等性校验,导致高并发请求提交同一笔提现请求,余额仅扣减 1 次,但实际到账很多次。 - 登录系统设计为 “连续输错 5 次密码锁定账户30分钟”,但未限制 “同一 IP 对多个账户的尝试次数”。攻击者可利用脚本对大量账户进行批量暴力破解,导致大量用户都被锁定。

- 安全配置错误:Web 服务器、应用服务器、数据库服务器等配置不当,如开放不必要的服务、使用默认账户密码等。
- 使用易受攻击和过时的组件:应用程序使用了存在已知安全漏洞的第三方库、框架或软件版本。
- 某广州职业学院后台使用actuator对应用系统进行自省和监控,配置不当导致所有的核心信息泄露。
management.endpoints.web.exposure.include=
management.endpoint.shutdown.enabled=true - 某企业泛微OA使用的
Tomcat9.0.1中间件开启了基于文件的会话持久化功能以及启用了DefaultServlet-Partial PUT请求,导致会话存储文件被恶意的反序列化,造成了任意命令执行。
<Context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore"/>
</Manager>
</Context>
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet> 

- 身份识别和身份验证失败:身份验证和会话管理功能实现不当,攻击者可破解密码、会话令牌等冒充其他用户。
某网站前端登录采用RSA加密(公私钥均放到前端js文件中),用于用户鉴别的RSA-JWT与登录使用的一样,导致JWT能够被成功伪造,出现任意用户登录。

- 软件和数据完整性故障:软件更新、关键数据或基础设施组件未得到妥善保护,导致数据或软件被篡改。
NPM官方仓库中的开发者valorkin维护86个开源项目中的2个组件(ngx-bootstrap 与 ng2-file-upload)以及开发者scttcper维护54个开源项目中的14个组件(@ctrl/tinycolor等),总计涉及近40个开源组件项目,其中受影响的多个热门组件历史总下载量超过亿次,影响范围极大**。**攻击者在组件源码中直接植入混淆恶意文件bundle.js,其主要功能是在组件安装过程中静默窃取受害者系统平台信息、环境变量数据以及主流业务(包括NPM、Github、AWS及GCP等)的token密钥。

受害者被投毒攻击后,敏感数据将直接在github上公开泄漏,base64解码后可还原出github token凭证


- 安全日志记录和监控失败:缺乏有效的安全日志记录和实时监控机制,无法及时发现和响应安全事件。
- 某系统虽开启了日志记录,但日志文件未定期备份且存储在本地,攻击者入侵后直接删除日志文件,销毁攻击痕迹,管理员无法进行事后溯源
- 服务器端请求伪造:攻击者操纵服务器向其他系统发送未经授权的请求,访问内部系统资源或获取敏感数据。
@PostMapping(consumes = "multipart/form-data", value = "/file/pdf")
@Operation(
summary = "Convert a file to a PDF using LibreOffice",
description =
"This endpoint converts a given file to a PDF using LibreOffice API Input:ANY"
+ " Output:PDF Type:SISO")
public ResponseEntity<byte[]> processFileToPDF(@ModelAttribute GeneralFile generalFile)
throws Exception {
MultipartFile inputFile = generalFile.getFileInput();
File file = null;
try {
file = convertToPdf(inputFile);
PDDocument doc = pdfDocumentFactory.load(file);
return WebResponseUtils.pdfDocToWebResponse(
doc,
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", "")
+ "_convertedToPDF.pdf");
} finally {
if (file != null) file.delete();
}
}
public File convertToPdf(MultipartFile inputFile) throws IOException, InterruptedException {
String originalFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename());
if (originalFilename == null
|| !isValidFileExtension(FilenameUtils.getExtension(originalFilename))) {
throw new IllegalArgumentException("Invalid file extension");
}
Path tempInputFile =
Files.createTempFile("input_", "." + FilenameUtils.getExtension(originalFilename));
inputFile.transferTo(tempInputFile);
Path tempOutputFile = Files.createTempFile("output_", ".pdf");
try {
//LibreOffice
List<String> command =
new ArrayList<>(
Arrays.asList(
runtimePathConfig.getUnoConvertPath(),
"--port",
"2003",
"--convert-to",
"pdf",
tempInputFile.toString(),
tempOutputFile.toString()));
ProcessExecutorResult returnCode =
ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE)
.runCommandWithOutputHandling(command);
return tempOutputFile.toFile();
} finally {
if (tempInputFile != null) Files.deleteIfExists(tempInputFile);
}
}
POST /api/v1/convert/file/pdf HTTP/1.1
Host: 172.25.24.140:8080
Content-Length: 230
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXk10yDA52D8VSaGO
Accept: */*
Origin: http://172.25.24.140:8080
Referer: http://172.25.24.140:8080/file-to-pdf?lang=zh_CN
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=node0dylx4lgdtzxwixmnid86661r1.node0
Connection: close
------WebKitFormBoundaryXk10yDA52D8VSaGO
Content-Disposition: form-data; name="fileInput"; filename="1.html"
Content-Type: text/html
<img src='http://xxx.dnslog.cn'>
------WebKitFormBoundaryXk10yDA52D8VSaGO-- - 某Web应用程序存在
SSRF且云上配置不当,导致所有云机器被接管。
https://help.aliyun.com/zh/eci/user-guide/obtain-the-metadata-from-a-container-1
https://help.aliyun.com/zh/ecs/user-guide/view-instance-metadata 

知名例子
麦当劳
弱口令+失效的访问控制
麦当劳的招聘平台McHire使用了Paradox.ai开发的名为Olivia的AI聊天机器人,来收集求职者的个人信息,包括姓名、电话号码、电子邮件地址、物理地址等,McHire存在安全漏洞,超过6400万求职者的个人信息遭暴露。在成功登录后,可以修改网址中的数字参数(应聘者的ID编号),任何McHire账户持有者都能查看其他申请人的聊天交互机密信息。


安全配置错误
- 麦当劳系统Design Hub(120个国家团队使用的品牌资产平台) 中发现漏洞,平台仅依靠客户端密码进行保护,只需要将
https://admin.me.mcd.com/feel-good-design/login中的login改成register,即可访问开放端点,该API还会提示用户填写缺失字段,使得账户被任意创建。 - Design Hub中的JavaScript文件硬编码存储了
Magicbell API密钥和密钥允许列出用户并通过麦当劳基础设施发送钓鱼通知,导致允许通过麦当劳的基础设施发送钓鱼通知。


印度麦当劳App
McDelivery系统,允许订购麦当劳外卖,在Google Play下载量超过1000万次,在印度开发了独有的App。

订单信息遍历
McDelivery网站和移动应用程序使用Angular构建,面向消费者的高交互性应用程序,在main.js中找到了了关于订单信息的接口和参数。

- 通过尝试
order-tracking中的参数,倾向输入0或1查看是否是简单的整数值,果然得到了结果。

- 订单 ID 是连续的,这意味着您可以不断将数字加 1 以获取下一个订单的详细信息,从而获取更多用户信息,由于在API调用中需要JWT令牌,尚未创建帐户,也未登录。该令牌来自加载网站时在后台运行的访客登录 API 调用。


零元购
麦当劳选购下单的时候,选择一个海上的地址进行配送,底部显示地址无法使用,通过删除前端的disable按钮进行下单,点击支付,跳转到支付处理器Juspay。



在JustPay订单支付的接口里面,有字段名orderDetails,它包含所有需要修改的键值,包括订单的ID,价格等等。因为存在一个校验字段Signature服务端的校验字段,更改它们不会产生预期的结果,签名在服务器端生成如下:
- 订单 API 接受您的购物车 ID 并从购物车对象中提取所有需要的信息。
- 创建 orderDetails 对象,并基于该对象创建 RSA 签名。
- 两者都返回给客户端

购物车对象能够接受商品更新,但它也能接受价格更新吗?我整理了一个 PUT 请求来更新价格,成功更新了价格,并且得到了一个有效的签名,最终完成了零元购。

管理数据泄露
McDelivery 的管理面板位于https://oms.mcdelivery.co.in/,通过JS数据找到了一个管理端点来查看接受消费者 JWT 令牌的KPI报告。


本企业漏洞案例
保密问题暂删除。
注意事项
| 类别 | 注意事项 |
|---|---|
| 注入 | 1.SQL 注入防护:强制使用参数化查询(PreparedStatement),避免直接拼接用户输入到 SQL 语句中;若使用 ORM 框架(如 MyBatis),通过 XML 映射文件的参数占位符(#{})实现参数化,而非字符串拼接的${}。 2. 命令注入防护:避免直接调用Runtime.exec()等系统命令执行方法,若必须使用,需对用户输入进行白名单校验(只允许特定命令或参数)。 3. XSS 注入防护:对输出到 HTML 页面的用户输入,使用 HTML 转义工具(如 OWASP Java Encoder)进行转义;前端也需配合,使用框架自带的安全渲染方式。 4. 模板注入防护:若使用模板引擎(如 Thymeleaf、Freemarker),禁用动态模板生成;对模板中的变量,强制进行沙箱限制(如 Freemarker 禁用execute等危险方法)。 |
| 失效的身份认证 | 1.密码安全:使用强哈希算法(如 BCrypt、Argon2)存储密码,而非 MD5、SHA-1 等已被破解的算法;强制要求用户设置复杂密码(长度≥8,包含大小写、数字、特殊字符),并在前端 / 后端双重校验。 2. 会话管理:会话 ID 必须随机且不可预测,使用框架自带的安全会话生成机制(如 Java Servlet 的HttpSession);设置合理的会话超时时间(如 30 分钟无操作则过期),并在用户注销时立即销毁会话;为 Cookie 设置HttpOnly和Secure属性,防止 XSS 窃取和非 HTTPS 传输。 3. 多因素认证(MFA):敏感操作(如修改密码、转账)强制启用 MFA(短信验证码 + 密码、硬件令牌等);自定义 MFA 逻辑时,确保验证码一次性有效、有过期时间。 |
| 敏感信息泄露 | 1.数据传输安全:所有涉及敏感信息(密码、身份证号、银行卡号等)的传输,必须使用 HTTPS 协议,启用 TLS 1.2 及以上版本。 2. 数据存储安全:对敏感数据进行加密存储,使用强加密算法(如 AES),并通过密钥管理系统(KMS)安全管理密钥,避免硬编码密钥。 3. 日志与错误处理:日志中禁止记录敏感信息(如密码明文);错误页面禁止返回详细的堆栈跟踪或系统路径,只展示通用错误提示,详细错误信息在后台日志中记录。 4. 数据脱敏:展示敏感信息时进行脱敏处理(如手机号显示为138****1234),仅在必要场景(如用户本人查看)展示完整信息。 |
| 不安全的设计 | 1.安全设计评审:核心功能(如支付、用户权限管理)的设计方案,必须经过安全专家评审,确保符合最小权限、纵深防御等安全原则。 2. 业务流程安全:关键业务流程(如订单生成、支付、退款)需设计多重校验(如服务端二次验证前端参数、异步对账),防止流程被篡改或绕过。 3. 应急响应设计:在系统设计中融入应急响应机制,明确安全事件的检测、通知、处置流程,例如:异常流量监控、数据备份与恢复策略。 |
| 安全配置错误 | 1.环境隔离与最小化:开发、测试、生产环境严格隔离;服务器只安装必要的软件和服务,关闭不必要的端口(如 FTP、Telnet)和默认服务。 2. 默认配置修改:修改所有系统、应用、数据库的默认账户和密码,使用强密码,并定期更换;避免使用默认的加密密钥、证书等。 3. 配置文件安全:敏感配置(如数据库连接信息、API 密钥)禁止硬编码在代码中,应存储在加密的配置中心(如 Nacos、Consul)或环境变量中。 |
| 使用易受攻击和过时的组件 | 1.主动维护清单:清楚掌握项目中使用的每一个第三方库、框架及其版本号(例如使用 package-lock.json、pom.xml 等)。 2.只从官方渠道获取:永远只从官方仓库或可信源安装组件(如 Maven Central、npmjs、官方 GitHub Release),避免使用来路不明的 Jar 包或 JS 文件。 3.移除无用依赖:及时清理项目中不再使用的库,减少不必要的攻击面。 |
| 身份识别和身份验证失败 | 1.登录安全:设置登录失败次数限制(如 5 次失败后锁定账户 15 分钟),并启用验证码或行为验证(如滑块验证)防止暴力破解。 2. token 安全:若使用 JWT 等令牌,确保签名密钥的安全性,设置合理的过期时间,避免令牌被盗用后长期有效;令牌传输必须通过 HTTPS,禁止在 URL 中传递令牌。 3. 第三方认证集成:集成第三方登录(如 OAuth、OpenID)时,严格校验回调参数,确保重定向 URL 在白名单内,防止开放重定向漏洞。 4. 权限校验粒度:权限校验需精准到 “操作 + 资源” 维度,例如:用户只能修改自己创建的订单,管理员才能删除用户,避免 “一刀切” 的权限设计。 |
| 软件和数据完整性故障 | 1.不要相信来自外部的数据:任何从客户端、API、第三方服务收到的数据都可能被篡改,必须进行严格校验。 2.验证数字签名:如果使用了来自外部的代码、更新包或配置文件,必须检查其数字签名,确保它来自可信作者且未被修改。 3.使用安全的依赖库:只从官方渠道获取第三方库,并定期检查是否有已知漏洞,防止引入恶意依赖。 |
| 安全日志记录和监控失败 | 1.日志记录范围:关键操作(登录 / 登出、数据修改、权限变更、异常请求)必须记录日志,包含时间、用户 ID、IP 地址、操作内容、请求 ID 等信息。 2. 日志存储与保护:日志存储在安全的位置,设置访问权限,防止未授权访问;定期备份日志,保留足够长的时间(如 6 个月)以满足审计需求。 3. 实时监控与告警:集成安全监控工具(如 ELK + Wazuh、Splunk),对日志进行实时分析,设置告警规则(如多次登录失败、异常数据访问),确保安全事件能及时发现。 4. 日志格式与标准化:统一日志格式(如 JSON 格式),便于自动化分析和关联,例如:将所有系统的日志按照相同字段格式输出。 |
| 服务器端请求伪造 | 1.输入白名单校验:对用户可控的 URL 参数,强制校验域名和协议,只允许访问白名单内的合法域名,禁止访问内部 IP 段(如127.0.0.1、192.168..)。 2. URL 解析与规范化:使用安全的 URL 解析库(如 Java 的java.net.URL)对用户输入的 URL 进行解析和规范化,避免利用 URL 的特殊格式(如@、//)绕过校验。 3. 网络层限制:通过防火墙或网络策略,限制应用服务器对外发起请求的范围,仅允许访问必要的外部服务。 4. 错误信息限制:处理外部请求时,禁止将详细的错误信息(如内部服务的堆栈跟踪、网络拓扑)返回给用户,防止攻击者利用错误信息进行进一步攻击。 |
靶场体验
抓包工具:https://github.com/yaklang/yakit/releases/tag/v1.4.4-0912
代理工具:Google插件proxy
靶场地址:https://github.com/whgojp/JavaSecLab

文章标题:开发安全培训
文章链接:https://aiwin.net.cn/index.php/archives/4520/
最后编辑:2025 年 10 月 20 日 20:28 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)