网络安全攻防实战
Web应用安全防护全攻略
安全威胁概述
Web安全是保障用户数据和系统稳定性的重要环节。了解常见攻击手段是有效防御的第一步。
OWASP Top 10: 最重要的十大Web应用安全风险,是安全评估的基准。
1. XSS(跨站脚本攻击)
1.1 攻击原理
攻击者将恶意脚本注入到网页中,当用户浏览该页面时,脚本会在用户浏览器中执行。
http://example.com/search?q=<script>alert('XSS')</script>
<script>stealCookie(document.cookie)</script>
<script>
// 漏洞代码
document.getElementById('output').innerHTML =
location.hash.substring(1);
</script>
http://example.com/page#<img src=x onerror=alert('XSS')>
1.2 防御策略
// 1. 输入验证
function sanitizeInput(input) {
// 移除危险字符
return input.replace(/[<>"'&]/g, function(match) {
return {
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'&': '&'
}[match];
});
}
// 2. 输出编码
function encodeForHTML(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 3. 使用CSP(内容安全策略)
// HTTP头部
Content-Security-Policy: default-src 'self';
script-src 'self' https://trusted.cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self';
// 4. 设置Cookie安全属性
Set-Cookie: sessionId=abc123;
HttpOnly;
Secure;
SameSite=Strict;
// 5. 使用安全的DOM操作
// 避免使用innerHTML
element.textContent = userInput;
// 或使用DOMPurify
const cleanHTML = DOMPurify.sanitize(userInput);
element.innerHTML = cleanHTML;
2. CSRF(跨站请求伪造)
2.1 攻击原理
攻击者诱导用户在已登录状态下访问恶意网站,从而以用户身份执行非预期操作。
<!-- CSRF攻击示例 -->
<!-- 攻击者页面 -->
<html>
<body>
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
2.2 防御策略
// 1. CSRF Token
// 生成Token
const crypto = require('crypto');
function generateCSRFToken() {
return crypto.randomBytes(32).toString('hex');
}
// 服务器端验证
app.post('/transfer', (req, res) => {
const userToken = req.body._csrf;
const sessionToken = req.session.csrfToken;
if (!userToken || userToken !== sessionToken) {
return res.status(403).send('CSRF token验证失败');
}
// 处理业务逻辑
});
// 2. SameSite Cookie属性
// 在设置Cookie时
res.cookie('sessionId', 'abc123', {
httpOnly: true,
secure: true,
sameSite: 'strict' // 或 'lax'
});
// 3. 验证Referer头部
app.use((req, res, next) => {
const referer = req.headers.referer;
const origin = req.headers.origin;
if (req.method === 'POST' || req.method === 'PUT' || req.method === 'DELETE') {
// 验证Referer是否来自同一域名
if (referer && !referer.startsWith('https://yourdomain.com')) {
return res.status(403).send('非法请求来源');
}
}
next();
});
// 4. 使用自定义请求头部
// 前端设置
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
});
// 后端验证
app.use((req, res, next) => {
if (req.get('X-Requested-With') !== 'XMLHttpRequest') {
return res.status(403).send('必须使用AJAX请求');
}
next();
});
3. SQL注入
3.1 攻击原理
-- 原始查询
SELECT * FROM users WHERE username = '$username' AND password = '$password';
-- 攻击输入
username: admin' --
password: anything
-- 最终执行的SQL
SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything';
-- 注释掉后续条件,绕过密码验证
-- 更危险的注入
username: admin' OR '1'='1
password: ' OR '1'='1
-- 最终执行的SQL
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '' OR '1'='1';
-- 返回所有用户
3.2 防御策略
// 1. 使用参数化查询(Prepared Statements)
// Node.js + MySQL
const mysql = require('mysql2/promise');
async function getUser(username, password) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test'
});
// 使用参数化查询
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = ? AND password = ?',
[username, password]
);
return rows[0];
}
// 2. 使用ORM框架
// Sequelize示例
const User = require('./models/user');
async function findUser(username, password) {
return await User.findOne({
where: {
username: username,
password: password
}
});
}
// 3. 输入验证
function validateInput(input) {
// 只允许字母数字和下划线
if (!/^[a-zA-Z0-9_]+$/.test(input)) {
throw new Error('非法输入');
}
return input;
}
// 4. 最小权限原则
-- 创建专门用于查询的数据库用户
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON database.users TO 'webapp'@'localhost';
-- 不授予DELETE、DROP等危险权限
// 5. 使用存储过程
DELIMITER //
CREATE PROCEDURE GetUser(IN p_username VARCHAR(50), IN p_password VARCHAR(50))
BEGIN
SELECT * FROM users
WHERE username = p_username AND password = p_password;
END //
DELIMITER ;
-- 调用存储过程
CALL GetUser('admin', 'password123');
4. 文件上传漏洞
4.1 攻击原理
<!-- 攻击者上传恶意文件 -->
1. 上传PHP Webshell
<?php system($_GET['cmd']); ?>
2. 上传恶意HTML
<script>alert('XSS')</script>
3. 利用路径遍历
filename: ../../../etc/passwd
4. 利用文件包含
<?php include($_GET['file']); ?>
4.2 防御策略
// 1. 文件类型验证
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
// 生成随机文件名
const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1E9);
// 获取文件扩展名
const ext = path.extname(file.originalname).toLowerCase();
// 只允许特定扩展名
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf'];
if (!allowedExtensions.includes(ext)) {
return cb(new Error('文件类型不支持'));
}
cb(null, uniqueName + ext);
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024, // 5MB
files: 1
},
fileFilter: (req, file, cb) => {
// 检查MIME类型
const allowedMimeTypes = [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf'
];
if (!allowedMimeTypes.includes(file.mimetype)) {
return cb(new Error('文件类型不支持'), false);
}
cb(null, true);
}
});
// 2. 重命名文件
function generateSafeFilename(originalname) {
// 移除特殊字符
const safeName = originalname.replace(/[^a-zA-Z0-9.-]/g, '_');
// 添加随机前缀
const randomPrefix = Date.now() + '-' + Math.random().toString(36).substr(2, 9);
return randomPrefix + '_' + safeName;
}
// 3. 隔离上传目录
// 将上传文件存放在Web根目录之外
app.use('/uploads', express.static(path.join(__dirname, '../uploads')));
// 4. 文件内容检查
const fileType = require('file-type');
const fs = require('fs');
async function validateFileContent(filePath) {
const buffer = fs.readFileSync(filePath);
const type = await fileType.fromBuffer(buffer);
if (!type) {
throw new Error('无法识别文件类型');
}
// 验证实际文件类型与扩展名是否匹配
const allowedTypes = [
{ ext: 'jpg', mime: 'image/jpeg' },
{ ext: 'png', mime: 'image/png' },
{ ext: 'gif', mime: 'image/gif' },
{ ext: 'pdf', mime: 'application/pdf' }
];
const isValid = allowedTypes.some(allowed =>
allowed.ext === type.ext && allowed.mime === type.mime
);
if (!isValid) {
throw new Error('文件内容与类型不匹配');
}
return true;
}
// 5. 设置文件权限
// 上传的文件应该只有读取权限
fs.chmodSync(filePath, 0o644);
5. 安全头部配置
// Express安全头部配置
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
imgSrc: ["'self'", "data:", "https://*.example.com"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'", "https://fonts.example.com"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
frameAncestors: ["'none'"],
upgradeInsecureRequests: []
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
permissionsPolicy: {
features: {
camera: ["'none'"],
microphone: ["'none'"],
geolocation: ["'none'"]
}
}
}));
// 自定义安全头部
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
next();
});
6. 安全测试工具
6.1 自动化扫描工具
# OWASP ZAP基础使用
# 启动ZAP
./zap.sh -daemon -port 8080 -config api.disablekey=true
# 使用Docker运行ZAP
docker run -u zap -p 8080:8080 -i owasp/zap2docker-stable \
zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.disablekey=true
# 使用ZAP API扫描
curl "http://localhost:8080/JSON/core/action/newSession/?name=test&overwrite=true"
curl "http://localhost:8080/JSON/spider/action/scan/?url=http://target.com"
curl "http://localhost:8080/JSON/ascan/action/scan/?url=http://target.com"
# 使用ZAP进行主动扫描
docker run -u zap -p 8080:8080 -i owasp/zap2docker-stable \
zap-full-scan.py -t http://target.com -r report.html
# Burp Suite社区版
# 1. 配置代理(127.0.0.1:8080)
# 2. 安装证书
# 3. 使用Intruder进行暴力破解测试
# 4. 使用Repeater修改请求
# 5. 使用Scanner自动扫描
# SQLMap示例
sqlmap -u "http://target.com/product?id=1" --dbs
sqlmap -u "http://target.com/product?id=1" -D database --tables
sqlmap -u "http://target.com/product?id=1" -D database -T users --dump
# Nmap安全扫描
nmap -sV -sC -O target.com
nmap --script vuln target.com
nmap -p 80,443,8080,8443 target.com
6.2 代码审计工具
# Semgrep(代码静态分析)
semgrep --config auto .
semgrep --config "p/security-audit" .
# ESLint安全插件
# .eslintrc.js
module.exports = {
plugins: ['security'],
extends: ['plugin:security/recommended'],
rules: {
'security/detect-object-injection': 'error',
'security/detect-eval-with-expression': 'error',
'security/detect-non-literal-fs-filename': 'error',
'security/detect-pseudoRandomBytes': 'error'
}
};
# npm audit检查依赖漏洞
npm audit
npm audit fix
npm audit fix --force
# Snyk安全检查
npx snyk test
npx snyk monitor
npx snyk wizard
# OWASP Dependency-Check
dependency-check --project "My Project" --scan ./ --out ./report
7. 应急响应计划
7.1 安全事件响应流程
// 1. 检测与识别
const securityEvents = {
methods: [
'实时日志监控',
'入侵检测系统(IDS)',
'Web应用防火墙(WAF)告警',
'异常流量检测'
],
indicators: [
'异常登录尝试',
'大量404错误',
'数据库查询异常',
'文件系统变化'
]
};
// 2. 隔离与遏制
const incidentResponse = {
immediateActions: [
'断开受影响系统网络',
'备份系统日志',
'保存内存dump',
'记录时间线'
],
containmentStrategies: [
'修改防火墙规则',
'重置用户密码',
'禁用受影响账户',
'关闭相关服务'
]
};
// 3. 根除与恢复
const recoveryProcess = {
steps: [
'识别攻击入口点',
'修复安全漏洞',
'清除恶意文件',
'恢复干净备份',
'验证系统完整性'
],
verification: [
'安全扫描验证',
'渗透测试验证',
'日志审计验证',
'功能测试验证'
]
};
// 4. 总结与改进
const postMortem = {
requirements: [
'编写事件报告',
'分析攻击路径',
'评估影响范围',
'制定改进措施'
],
improvements: [
'更新安全策略',
'加强监控系统',
'员工安全培训',
'完善应急预案'
]
};
总结
网络安全是一个持续的过程,而非一次性的任务。建立多层防御体系、定期安全审计和持续的安全意识教育是保障系统安全的关键。
安全原则:
- 最小权限原则
- 深度防御原则
- 默认拒绝原则
- 完整监控原则