SQL注入防护
SQL注入攻击原理
SQL注入是一种常见的Web安全漏洞,攻击者通过在用户输入中插入恶意SQL代码来操纵数据库查询。当应用程序直接将用户输入拼接到SQL语句中时,就会产生这种风险。典型的攻击方式包括:
- 通过表单输入注入恶意代码
- 修改URL参数进行注入
- 伪造HTTP头信息
// 危险的SQL拼接示例
const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;
Node.js中的防护措施
使用参数化查询
参数化查询是最有效的防护手段,它确保用户输入始终被当作数据处理而非SQL代码。在Node.js中,可以使用各种数据库驱动提供的参数化查询功能。
// 使用mysql2库的参数化查询
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = ? AND password = ?',
[username, password]
);
ORM框架防护
ORM(Object-Relational Mapping)框架通常内置了SQL注入防护机制:
// 使用Sequelize ORM
const user = await User.findOne({
where: {
username: username,
password: password
}
});
输入验证与过滤
白名单验证
对输入数据进行严格的白名单验证,只允许符合特定模式的数据通过:
// 用户名只允许字母数字
if (!/^[a-zA-Z0-9]+$/.test(username)) {
throw new Error('Invalid username format');
}
类型检查
确保数字参数确实是数字类型:
// 确保ID是数字
const id = Number(req.params.id);
if (isNaN(id)) {
return res.status(400).send('Invalid ID');
}
数据库权限最小化
应用账户权限限制
数据库用户应该只拥有必要的最小权限:
-- 创建只有查询权限的用户
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT ON app_db.* TO 'app_user'@'localhost';
存储过程使用
将敏感操作封装在存储过程中:
// 调用存储过程
await connection.execute('CALL authenticate_user(?, ?)', [username, password]);
其他防护措施
错误处理策略
避免在错误信息中泄露数据库结构:
try {
// 数据库操作
} catch (err) {
console.error(err);
res.status(500).send('Internal Server Error');
}
定期安全审计
使用工具进行SQL注入漏洞扫描:
# 使用sqlmap进行扫描
sqlmap -u "http://example.com/login" --data="username=admin&password=123"
实战案例分析
登录表单防护
// 安全的登录处理
app.post('/login', async (req, res) => {
const { username, password } = req.body;
// 输入验证
if (!username || !password) {
return res.status(400).send('Missing credentials');
}
// 参数化查询
try {
const [users] = await pool.query(
'SELECT id, username FROM users WHERE username = ? AND password = SHA2(?, 256)',
[username, password]
);
if (users.length === 0) {
return res.status(401).send('Invalid credentials');
}
res.json(users[0]);
} catch (err) {
console.error(err);
res.status(500).send('Login failed');
}
});
搜索功能防护
// 安全的搜索实现
app.get('/search', async (req, res) => {
const { q } = req.query;
// 输入过滤
const searchTerm = q.replace(/[^\w\s]/gi, '');
try {
const [results] = await pool.query(
'SELECT * FROM products WHERE name LIKE ?',
[`%${searchTerm}%`]
);
res.json(results);
} catch (err) {
console.error(err);
res.status(500).send('Search failed');
}
});
高级防护技术
预编译语句缓存
// 使用mysql2的预编译语句
const stmt = await connection.prepare(
'SELECT * FROM users WHERE id = ?'
);
// 后续查询使用缓存的语句
const [rows] = await stmt.execute([userId]);
动态查询构建防护
当需要动态构建查询时,使用安全的构建方式:
// 安全的动态查询构建
function buildUserQuery(filters) {
const params = [];
let query = 'SELECT * FROM users WHERE 1=1';
if (filters.name) {
query += ' AND name LIKE ?';
params.push(`%${filters.name}%`);
}
if (filters.age) {
query += ' AND age = ?';
params.push(Number(filters.age));
}
return { query, params };
}
const { query, params } = buildUserQuery(req.query);
const [users] = await connection.query(query, params);
性能与安全的平衡
查询优化与防护
// 使用分页参数化
app.get('/posts', async (req, res) => {
const page = Math.max(1, Number(req.query.page) || 1);
const limit = Math.min(100, Math.max(1, Number(req.query.limit) || 10));
const offset = (page - 1) * limit;
const [posts] = await pool.query(
'SELECT * FROM posts ORDER BY created_at DESC LIMIT ? OFFSET ?',
[limit, offset]
);
res.json(posts);
});
批量操作防护
// 安全的批量插入
app.post('/products/batch', async (req, res) => {
const products = req.body;
if (!Array.isArray(products)) {
return res.status(400).send('Invalid payload');
}
const placeholders = products.map(() => '(?, ?, ?)').join(',');
const values = [];
products.forEach(product => {
values.push(
product.name,
Number(product.price),
product.category
);
});
try {
await pool.query(
`INSERT INTO products (name, price, category) VALUES ${placeholders}`,
values
);
res.status(201).send('Products created');
} catch (err) {
console.error(err);
res.status(500).send('Batch insert failed');
}
});
监控与日志记录
可疑查询监控
// 记录慢查询和可疑查询
pool.on('query', (query) => {
if (query.sql.includes('1=1') || query.sql.includes('--')) {
console.warn('Suspicious query detected:', query.sql);
}
if (query.duration > 1000) {
console.log('Slow query:', query.sql, query.duration);
}
});
审计日志实现
// 数据库操作审计
async function auditLog(userId, action, details) {
try {
await pool.query(
'INSERT INTO audit_logs (user_id, action, details, ip_address) VALUES (?, ?, ?, ?)',
[userId, action, JSON.stringify(details), req.ip]
);
} catch (err) {
console.error('Failed to write audit log:', err);
}
}
// 在关键操作中调用
app.post('/transfer', async (req, res) => {
// 执行转账操作
await auditLog(req.user.id, 'money_transfer', {
amount: req.body.amount,
recipient: req.body.to
});
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn