阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > SQL注入防护

SQL注入防护

作者:陈川 阅读数:53745人阅读 分类: Node.js

SQL注入攻击原理

SQL注入是一种常见的Web安全漏洞,攻击者通过在用户输入中插入恶意SQL代码来操纵数据库查询。当应用程序直接将用户输入拼接到SQL语句中时,就会产生这种风险。典型的攻击方式包括:

  1. 通过表单输入注入恶意代码
  2. 修改URL参数进行注入
  3. 伪造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

上一篇:XSS防护

下一篇:依赖安全扫描

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌