我正在尝试保护我的数据库免受SQL注入的影响。我有许多存储函数可以返回我的预定义数据类型的 SET。例如:
create type userLoginUserIdPasswordReturnType as
(
userId integer, -- user.id
password text
);
CREATE OR REPLACE FUNCTION "user_login_get_id_password"(usernameIn text)
returns setof userLoginUserIdPasswordReturnType as
$$
declare
sql text;
begin
sql = 'select id, cryptpwd from "user" where username = ' || usernameIn ||';';
return query execute sql;
end;
$$ language 'plpgsql';
我现在想从我的 Java 前端调用这些存储过程。从我所读到的内容来看,使用CallableStatement在SQL注入方面更安全。这是我到目前为止所拥有的:
public ArrayList<String> userLoginGetIdPassword(String username){
ArrayList<String> result = new ArrayList<String>();
try{
String commandText = "{call user_login_get_id_password(?)}";
this.cstmt = this.myConnection.prepareCall(commandText);
this.cstmt.setObject(1, username);
// this.rs = this.cstmt.execute();
this.rs = cstmt.executeQuery();
while (this.rs.next()){
result.add(this.rs.getString(1));
}
} catch (SQLException e){
System.out.println("SQL Exception: ");
e.printStackTrace();
}
return result;
}
如果我尝试使用 execute() 方法,它会要求我将 ResultSet rs 设置为布尔值。如果按原样运行(executeQuery()),则在 ResultSet 中只能看到 returnType 的第一个字段 (userId)。
如果我这样调用存储过程:
public ArrayList<String> userLoginGetIdPassword(String username){
ArrayList<String> result = new ArrayList<String>();
try{
String query = "select "user_login_get_id_password"('" + username + "');";
System.out.println(query);
this.stmt = this.myConnection.createStatement();
this.rs = this.stmt.executeQuery(query);
while (this.rs.next()){
result.add(this.rs.getString(1));
}
} catch (SQLException e){
System.out.println("SQL Exception: ");
e.printStackTrace();
}
return result;
}
我得到了正确的数据。
另外,如果有任何进一步的提示可以保护我的数据库免受SQL注入的影响,请指出它们。我已经创建了应用程序将使用的特定 Postgres 角色,并实现了连接池 (c3p0) 来连接到我的数据库 - 应用程序将在本地网络上运行。我正在验证来自不同Java Swing组件的用户输入,以避免SQL注入攻击(注释--,分号,*和其他SQL命令,如DELETE)。
欢迎任何意见。
谢谢。
首先,PostgreSQL不支持call
命令,所以这就是你的第一种方法失败的原因。
在第二种方法中,您将遇到两个问题。 首先是SQL注入。 使用标准绑定应用程序将解决此问题。 第二个问题是,您将很难像调用函数一样从函数中解析数据。
更好的方法是类似
SELECT * FROM user_login_get_id_password(?)
但请注意,sql 注入可以在函数内部进行。 在您可以避免EXECUTE
可以保护您的范围内。如果必须使用执行,请将查询字符串更改为:
sql = 'select id, cryptpwd from "user" where username = ' || quote_literal(usernameIn);
执行查询中不需要终止分号。 它会自动终止。 不过,您确实需要调用quote_literal(如果引用表/列名称,请使用 quote_ident()。