安全测试指南
安全测试方法对比
| 类型 | 全称 | 工作方式 | 常用工具 |
|---|---|---|---|
| SAST | 静态应用安全测试 | 不运行代码,分析源代码 | Semgrep、CodeQL、SonarQube |
| DAST | 动态应用安全测试 | 对运行中的应用发起攻击(黑盒) | OWASP ZAP、Burp Suite、Nuclei |
| SCA | 软件成分分析 | 扫描依赖中已知 CVE | Snyk、Dependabot、OWASP Dependency-Check |
| 密钥扫描 | 检测代码/历史中的凭据 | 扫描 git 历史和文件的模式匹配 | GitLeaks、TruffleHog、GitHub Secret Scanning |
CI 安全流水线
# GitHub Actions 安全检查
jobs:
security:
steps:
# 1. 密钥检测(最快,最先失败)
- name: 扫描密钥
uses: gitleaks/gitleaks-action@v2
# 2. 依赖漏洞扫描
- name: Snyk 扫描
uses: snyk/actions/node@master
with:
args: --severity-threshold=high
# 3. SAST——Semgrep
- name: Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: p/owasp-top-ten
# 4. 容器镜像扫描
- name: Trivy 镜像扫描
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
安全回归测试
// 与功能代码同步编写安全测试,防止回归
describe('认证安全', () => {
it('拒绝过期的 JWT', async () => {
const expiredToken = generateToken({ userId: 1 }, { expiresIn: '-1s' });
await request(app)
.get('/api/profile')
.set('Authorization', `Bearer ${expiredToken}`)
.expect(401);
});
it('防止 IDOR:用户不能访问他人数据', async () => {
const user1Token = await loginAs('[email protected]');
await request(app)
.get('/api/users/user2-id/orders')
.set('Authorization', `Bearer ${user1Token}`)
.expect(403);
});
it('登录尝试频率限制', async () => {
for (let i = 0; i < 10; i++) {
await request(app).post('/api/login').send({ email: 'x', password: 'x' });
}
const res = await request(app).post('/api/login').send({ email: 'x', password: 'x' });
expect(res.status).toBe(429); // 请求过多
});
});