You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中使用AWS Athena预准备语句防范SQL注入风险

AWS Athena Java集成预准备语句实操方案

你之前通过字符串拼接构造Athena查询的SQL注入风险,通过以下两种预准备语句集成方式即可完全规避,符合OWASP安全规范要求:

前置依赖说明

  • 推荐使用官方AWS Java SDK 2.x版本,兼容性更好
  • 若采用JDBC方式对接,需引入对应版本Athena JDBC驱动包

两种实现方式

方式1:使用AWS Java SDK 2.x原生实现

Athena预准备语句分为两步执行:先通过PREPARE定义带占位符的参数化查询,再通过EXECUTE传入实际参数执行,底层自动处理参数转义:

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.athena.AthenaClient;
import software.amazon.awssdk.services.athena.model.*;
import java.util.List;

public class AthenaPreparedStatementDemo {
    private static final String ATHENA_OUTPUT_BUCKET = "s3://你的Athena结果存储桶路径/";
    private static final String WORKGROUP = "primary";

    public static void main(String[] args) {
        AthenaClient athenaClient = AthenaClient.builder()
                .region(Region.US_EAST_1)
                .build();

        // 1. 定义预准备语句,?为参数占位符
        String prepareQuery = "PREPARE user_query FROM " +
                "'SELECT user_id, user_name FROM user_db.user_info WHERE country = ? AND register_time > ?'";

        QueryExecutionContext context = QueryExecutionContext.builder().database("user_db").build();
        ResultConfiguration resultConfig = ResultConfiguration.builder().outputLocation(ATHENA_OUTPUT_BUCKET).build();
        StartQueryExecutionRequest prepareRequest = StartQueryExecutionRequest.builder()
                .queryString(prepareQuery)
                .queryExecutionContext(context)
                .resultConfiguration(resultConfig)
                .workGroup(WORKGROUP)
                .build();
        String prepareExecutionId = athenaClient.startQueryExecution(prepareRequest).queryExecutionId();
        // 等待PREPARE执行完成,可自行封装轮询判断逻辑
        waitForQueryToComplete(athenaClient, prepareExecutionId);

        // 2. 执行EXECUTE传入实际参数,无需手动转义
        String executeQuery = "EXECUTE user_query USING '中国', DATE '2023-01-01'";
        StartQueryExecutionRequest executeRequest = StartQueryExecutionRequest.builder()
                .queryString(executeQuery)
                .queryExecutionContext(context)
                .resultConfiguration(resultConfig)
                .workGroup(WORKGROUP)
                .build();
        String executeExecutionId = athenaClient.startQueryExecution(executeRequest).queryExecutionId();
        waitForQueryToComplete(athenaClient, executeExecutionId);

        // 3. 获取查询结果
        GetQueryResultsResponse results = athenaClient.getQueryResults(GetQueryResultsRequest.builder()
                .queryExecutionId(executeExecutionId)
                .build());
        processResult(results.resultSet().rows());
    }
    // 省略等待查询完成、结果解析工具方法实现
}

方式2:使用JDBC PreparedStatement实现(适配传统JDBC开发习惯)

如果使用Athena JDBC驱动,可直接使用标准PreparedStatement接口,驱动会自动转换为Athena预准备语句逻辑,无需手动编写PREPARE/EXECUTE语句:

import java.sql.*;
import java.util.Properties;

public class AthenaJDBCPreparedDemo {
    public static void main(String[] args) throws Exception {
        // JDBC连接URL格式
        String url = "jdbc:awsathena://athena.us-east-1.amazonaws.com:443/";
        Properties props = new Properties();
        props.setProperty("User", "你的AWS访问密钥AK");
        props.setProperty("Password", "你的AWS访问密钥SK");
        props.setProperty("S3OutputLocation", "s3://你的Athena结果存储桶路径/");
        props.setProperty("Workgroup", "primary");

        Connection conn = DriverManager.getConnection(url, props);
        // 直接写参数化SQL,用?做占位符
        String sql = "SELECT user_id, user_name FROM user_db.user_info WHERE country = ? AND register_time > ?";
        PreparedStatement pstmt = conn.prepareStatement(sql);
        // 传入参数,底层自动处理转义,完全规避SQL注入
        pstmt.setString(1, "中国");
        pstmt.setDate(2, Date.valueOf("2023-01-01"));

        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            System.out.println("用户ID:" + rs.getString("user_id") + " 用户名:" + rs.getString("user_name"));
        }
        rs.close();
        pstmt.close();
        conn.close();
    }
}

注意事项

  • 预准备语句的生命周期和当前Session绑定,Session失效后需要重新执行PREPARE定义
  • 单个预准备语句最多支持256个参数占位符,超出需要调整查询逻辑
  • 字符串类型参数传入时无需手动加单引号,底层会自动处理转义
  • 若使用JDBC方式,需确保驱动版本不低于2.0.2,对预准备语句的支持更完善

内容的提问来源于stack exchange,提问作者Sandesh Dumbre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 01:09:04