-
Notifications
You must be signed in to change notification settings - Fork 819
Commit
* test: Add failing test for simple query mode parameter injection Adds a failing test to demonstrate how direct parameter injection in simple query mode allows for modifying the executed SQL. The issue arises when a bind placeholder is prefixed with a negation. The direct replacement of a negative value causes the resulting token to be considered a line comment. For example the SQL: SELECT -?, ? With parameter values of -1 and any text with a newline in the second parameter allows arbitrary command execution, e.g. with values -1 and "\nWHERE false" causes the query to return no rows. More complicated examples can be created by adding statement terminators. * fix: Escape literal parameter values in simple query mode Escape all literal parameter values and wrap them in parentheses to prevent SQL injection when using specially crafted parameters and SQL in simple query mode. Previously the raw value of the parameter, e.g. 123, was injected into the ? placeholder. With this change all parameters are injected as '...value...' literals that are cast to the desired type by the server and wrapped in parentheses. So the SQL SELECT -? with a parameter of -123 would become: SELECT -('-123'::int4)
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* Copyright (c) 2024, PostgreSQL Global Development Group | ||
* See the LICENSE file in the project root for more information. | ||
*/ | ||
|
||
package org.postgresql.jdbc; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import org.postgresql.test.TestUtil; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
|
||
public class ParameterInjectionTest { | ||
@Test | ||
Check failure on line 20 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
Check failure on line 20 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / ubuntu, 17 seed build cache
|
||
public void negateParameter() throws Exception { | ||
try (Connection conn = TestUtil.openDB()) { | ||
Check failure on line 22 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
|
||
PreparedStatement stmt = conn.prepareStatement("SELECT -?"); | ||
Check failure on line 23 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
Check failure on line 23 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / ubuntu, 17 seed build cache
|
||
|
||
stmt.setInt(1, 1); | ||
Check failure on line 25 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
Check failure on line 25 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / ubuntu, 17 seed build cache
|
||
try (ResultSet rs = stmt.executeQuery()) { | ||
Check failure on line 26 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
|
||
assertTrue(rs.next()); | ||
Check failure on line 27 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
Check failure on line 27 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / ubuntu, 17 seed build cache
|
||
assertEquals(1, rs.getMetaData().getColumnCount(), "number of result columns must match"); | ||
Check failure on line 28 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
Check failure on line 28 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / ubuntu, 17 seed build cache
|
||
int value = rs.getInt(1); | ||
Check failure on line 29 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / Code style
Check failure on line 29 in pgjdbc/src/test/java/org/postgresql/jdbc/ParameterInjectionTest.java GitHub Actions / ubuntu, 17 seed build cache
|
||
assertEquals(-1, value); | ||
} | ||
|
||
stmt.setInt(1, -1); | ||
try (ResultSet rs = stmt.executeQuery()) { | ||
assertTrue(rs.next()); | ||
assertEquals(1, rs.getMetaData().getColumnCount(), "number of result columns must match"); | ||
int value = rs.getInt(1); | ||
assertEquals(1, value); | ||
} | ||
} | ||
} | ||
|
||
@Test | ||
public void negateParameterWithContinuation() throws Exception { | ||
try (Connection conn = TestUtil.openDB()) { | ||
PreparedStatement stmt = conn.prepareStatement("SELECT -?, ?"); | ||
|
||
stmt.setInt(1, 1); | ||
stmt.setString(2, "\nWHERE false --"); | ||
try (ResultSet rs = stmt.executeQuery()) { | ||
assertTrue(rs.next(), "ResultSet should contain a row"); | ||
assertEquals(2, rs.getMetaData().getColumnCount(), "rs.getMetaData().getColumnCount("); | ||
int value = rs.getInt(1); | ||
assertEquals(-1, value); | ||
} | ||
|
||
stmt.setInt(1, -1); | ||
stmt.setString(2, "\nWHERE false --"); | ||
try (ResultSet rs = stmt.executeQuery()) { | ||
assertTrue(rs.next(), "ResultSet should contain a row"); | ||
assertEquals(2, rs.getMetaData().getColumnCount(), "rs.getMetaData().getColumnCount("); | ||
int value = rs.getInt(1); | ||
assertEquals(1, value); | ||
} | ||
} | ||
} | ||
} |