65.9K
CodeProject 正在变化。 阅读更多。
Home

防御 SQL 注入攻击的方法

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2024年10月15日

CPOL

2分钟阅读

viewsIcon

13171

SQL 注入 (SQLi) 是一种严重的安全性漏洞,它允许攻击者在数据库上执行任意 SQL 代码。这可能导致未经授权的访问、数据泄露,甚至导致整个系统崩溃。

Image

1. 使用预处理语句和参数化查询

1.1 什么是预处理语句?

预处理语句是大多数数据库管理系统提供的一个功能,允许您使用参数执行 SQL 查询。与传统的 SQL 查询不同,预处理语句将 SQL 代码与数据分离。这可以防止攻击者将恶意 SQL 代码注入查询中。
示例
以下是如何在 Java 中使用 JDBC 的预处理语句的示例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class SQLInjectionDemo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://:3306/mydatabase";
        String user = "root";
        String password = "password";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            String sql = "SELECT * FROM users WHERE username = ?";
            PreparedStatement stmt = conn.prepareStatement(sql);
            stmt.setString(1, "admin' OR '1'='1");
            ResultSet rs = stmt.executeQuery();

            while (rs.next()) {
                System.out.println("User: " + rs.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
预处理语句会自动处理特殊字符的转义,从而降低 SQL 注入的风险。
重用预处理语句可以更有效率,因为数据库可以缓存执行计划。

1.2 如何使用参数化查询

参数化查询的工作方式与预处理语句类似,但在某些库和框架中可以直接使用。
使用 Spring JDBC 的示例
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

public class UserService {
    private JdbcTemplate jdbcTemplate;

    public UserService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<User> getUserByUsername(String username) {
        String sql = "SELECT * FROM users WHERE username = ?";
        return jdbcTemplate.query(sql, new Object[]{username}, (rs, rowNum) -> {
            User user = new User();
            user.setUsername(rs.getString("username"));
            return user;
        });
    }
}

2. 使用存储过程

存储过程是预编译的 SQL 语句,存储在数据库中。它们可以封装复杂的 SQL 逻辑,并在应用程序和数据库之间提供一个抽象层。
示例
以下是 MySQL 中存储过程的示例
DELIMITER //
CREATE PROCEDURE GetUser(IN username VARCHAR(50))
BEGIN
    SELECT * FROM users WHERE username = username;
END //
DELIMITER ;
在 Java 中,您可以像这样调用此存储过程
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

public class StoredProcedureDemo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://:3306/mydatabase";
        String user = "root";
        String password = "password";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            CallableStatement stmt = conn.prepareCall("{call GetUser(?)}");
            stmt.setString(1, "admin");
            ResultSet rs = stmt.executeQuery();

            while (rs.next()) {
                System.out.println("User: " + rs.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
通过抽象 SQL 代码,存储过程可以最大限度地降低 SQL 注入风险。
存储过程可以在多个应用程序之间重复使用。

3. 净化和验证用户输入

净化和验证用户输入对于防止 SQL 注入攻击至关重要。通过确保用户输入符合预期的格式和类型,您可以降低恶意数据作为 SQL 代码执行的风险。
Java 中输入验证的示例
以下是如何在 Java 中验证和净化用户输入
import java.util.regex.Pattern;

public class InputValidator {
    private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{3,15}$");

    public static boolean isValidUsername(String username) {
        return USERNAME_PATTERN.matcher(username).matches();
    }
}
净化涉及清理用户输入以删除或转义潜在的有害字符。
输入净化的示例
public class Sanitizer {
    public static String sanitize(String input) {
        return input.replaceAll("[^a-zA-Z0-9_]", "");
    }
}
在此示例中,sanitize 方法删除任何不是字母数字或下划线的字符。

4. 使用 ORM 框架

对象关系映射 (ORM) 框架(如 Hibernate 和 Entity Framework)抽象了数据库交互并处理 SQL 生成。通过使用 ORM,您可以利用内置机制,这些机制通常包括针对 SQL 注入的保护。
使用 Hibernate 的示例
这是一个使用 Hibernate 和参数化查询的基本示例
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateDemo {
    public static void main(String[] args) {
        SessionFactory factory = new Configuration().configure().buildSessionFactory();
        Session session = factory.openSession();
        session.beginTransaction();

        String hql = "FROM User WHERE username = :username";
        List<User> users = session.createQuery(hql, User.class)
                                  .setParameter("username", "admin")
                                  .getResultList();

        for (User user : users) {
            System.out.println("User: " + user.getUsername());
        }

        session.getTransaction().commit();
        session.close();
        factory.close();
    }
}
ORM 框架生成 SQL 查询,从而降低了手动创建可能容易受到攻击的查询的风险。许多 ORM 框架都包含旨在防止 SQL 注入的功能。

5. 结论

防止 SQL 注入涉及多种技术的结合,包括使用预处理语句、存储过程、输入验证、ORM 框架以及遵守数据库安全最佳实践。通过实施这些方法,您可以显着降低 SQL 注入攻击的风险并增强应用程序的整体安全性。
如果您有任何问题或需要进一步的说明,请在下方留言!

在以下位置阅读更多文章: 防御 SQL 注入攻击的方法

© . All rights reserved.