sql_injection:primary_defenses
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
sql_injection:primary_defenses [2016/10/13 13:25] – peter | sql_injection:primary_defenses [2020/04/16 20:53] (current) – removed peter | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== SQL Injection - Primary Defenses ====== | ||
- | |||
- | ===== Primary Defenses ===== | ||
- | |||
- | * Use Prepared Statements (Parameterized Queries) | ||
- | * Use Stored Procedures | ||
- | * Escape all User Supplied Input | ||
- | |||
- | |||
- | ===== Prepared Statements ===== | ||
- | |||
- | The use of prepared statements with variable binding (aka parameterized queries) is how all developers should first be taught how to write database queries. | ||
- | |||
- | Prepared statements ensure that an attacker is not able to change the intent of a query, even if SQL commands are inserted by an attacker. | ||
- | |||
- | |||
- | ==== PHP ==== | ||
- | |||
- | === Using mysqli === | ||
- | |||
- | The MySQL Improved extension handles bound parameters. | ||
- | |||
- | <code php> | ||
- | $stmt = $db-> | ||
- | $stmt-> | ||
- | $stmt-> | ||
- | </ | ||
- | |||
- | or | ||
- | |||
- | <code php> | ||
- | $stmt = $dbh-> | ||
- | $dbh-> | ||
- | </ | ||
- | |||
- | === Using ADODB === | ||
- | |||
- | ADODB provides a way to prepare, bind and execute all in the same method call. | ||
- | |||
- | <code php> | ||
- | $dbConnection = NewADOConnection($connectionString); | ||
- | $sqlResult = $dbConnection-> | ||
- | ' | ||
- | array($_REQUEST[' | ||
- | ); | ||
- | </ | ||
- | |||
- | |||
- | === Using the ODBC layer === | ||
- | |||
- | <code php> | ||
- | $stmt = odbc_prepare( $conn, ' | ||
- | $success = odbc_execute( $stmt, array($email) ); | ||
- | </ | ||
- | |||
- | or: | ||
- | |||
- | <code php> | ||
- | $res = odbc_exec($conn, | ||
- | $sth = $dbh-> | ||
- | $sth-> | ||
- | </ | ||
- | |||
- | === Using the PDO layer === | ||
- | |||
- | Here's the long way to do bind parameters. | ||
- | |||
- | <code php> | ||
- | $dbh = new PDO(' | ||
- | $stmt = $dbh-> | ||
- | $stmt-> | ||
- | $stmt-> | ||
- | |||
- | // insert one row | ||
- | $name = ' | ||
- | $value = 1; | ||
- | $stmt-> | ||
- | </ | ||
- | |||
- | And a shorter way to pass things in. | ||
- | |||
- | <code php> | ||
- | $dbh = new PDO(' | ||
- | $stmt = $dbh-> | ||
- | $stmt-> | ||
- | </ | ||
- | |||
- | |||
- | === Using PostgreSQL === | ||
- | |||
- | <code php> | ||
- | $result = pg_query_params( $dbh, ' | ||
- | </ | ||
- | |||
- | |||
- | |||
- | ==== Java ==== | ||
- | |||
- | === JDBC === | ||
- | |||
- | The JDBC API has a class called PreparedStatement which allows the programmer to safely insert user-supplied data into a SQL query. The location of each input value in the query string is marked with a question mark. The various set*() methods are then used to safely perform the insertion. | ||
- | |||
- | <code java> | ||
- | String name = //user input | ||
- | int age = //user input | ||
- | Connection connection = DriverManager.getConnection(...); | ||
- | PreparedStatement statement = connection.prepareStatement( | ||
- | " | ||
- | statement.setString(1, | ||
- | statement.setInt(2, | ||
- | ResultSet rs = statement.executeQuery(); | ||
- | while (rs.next()){ | ||
- | //... | ||
- | } | ||
- | </ | ||
- | |||
- | === Hibernate === | ||
- | |||
- | Hibernate uses named parameters to safely insert data into a query. A named parameter consists of a colon, followed by a unique name for the parameter. | ||
- | |||
- | <code java> | ||
- | String name = //user input | ||
- | int age = //user input | ||
- | Session session = //... | ||
- | Query query = session.createQuery(" | ||
- | query.setString(" | ||
- | query.setInteger(" | ||
- | Iterator people = query.iterate(); | ||
- | </ | ||
- | |||
- | |||
- | ==== Perl ==== | ||
- | |||
- | Perl's DBI, available on the CPAN, supports parameterized SQL calls. Both the do method and prepare method support parameters (" | ||
- | |||
- | <code perl> | ||
- | $sth = $dbh-> | ||
- | foreach my $email (@emails) { | ||
- | $sth-> | ||
- | $row = $sth-> | ||
- | [...] | ||
- | } | ||
- | </ | ||
- | |||
- | However, you can't use parameterization for identifiers (table names, column names) so you need to use DBI's quote_identifier() method for that: | ||
- | |||
- | <code perl> | ||
- | # Make sure a table name we want to use is safe: | ||
- | my $quoted_table_name = $dbh-> | ||
- | |||
- | # Assume @cols contains a list of column names you need to fetch: | ||
- | my $cols = join ',', | ||
- | |||
- | my $sth = $dbh-> | ||
- | </ | ||
- | |||
- | You could also avoid writing SQL by hand by using DBIx:: | ||
- | |||
- | |||
- | ==== SQLite ==== | ||
- | |||
- | Use **sqlite3_prepare()** to create a statement object. | ||
- | |||
- | |||
- | <code sql> | ||
- | sqlite3_stmt *stmt; | ||
- | |||
- | if ( sqlite3_prepare( | ||
- | | ||
- | " | ||
- | -1, // If than zero, then stmt is read up to the first nul terminator | ||
- | &stmt, | ||
- | | ||
- | ) | ||
- | != SQLITE_OK) { | ||
- | printf(" | ||
- | return 1; | ||
- | } | ||
- | |||
- | printf(" | ||
- | |||
- | if (sqlite3_bind_double( | ||
- | stmt, | ||
- | 1, // Index of wildcard | ||
- | 4.2 | ||
- | ) | ||
- | != SQLITE_OK) { | ||
- | printf(" | ||
- | return 1; | ||
- | } | ||
- | |||
- | if (sqlite3_bind_int( | ||
- | stmt, | ||
- | 2, // Index of wildcard | ||
- | 42 | ||
- | ) | ||
- | != SQLITE_OK) { | ||
- | printf(" | ||
- | return 1; | ||
- | } | ||
- | </ | ||
- | |||
- | |||
- | ===== Escape all User Supplied Input ===== | ||
- | |||
- | **<color red> | ||
- | |||
- | See: https:// | ||
- | |||
- | |||
- | === Alternative Method === | ||
- | |||
- | **TODO** : Check if this works? | ||
- | |||
- | < | ||
- | |||
- | For example, | ||
- | |||
- | <code mysql> | ||
- | $mysql[' | ||
- | |||
- | $mysql[' | ||
- | </ | ||
- | |||
- | will make the injection attempt successful, whereas | ||
- | |||
- | <code mysql> | ||
- | $mysql[' | ||
- | |||
- | $mysql[' | ||
- | </ | ||
- | |||
- | will make the injection attempt fail. | ||
- | |||
- | Isn't the latter method (str_replace(array(' | ||
- | |||
- | |||
- | |||
- | ===== Safe Examples ===== | ||
- | |||
- | The following examples are safe: | ||
- | |||
- | <code php> | ||
- | mysql_set_charset($charset); | ||
- | mysql_query(" | ||
- | $var = mysql_real_escape_string('" | ||
- | mysql_query(' | ||
- | </ | ||
- | |||
- | ...because we've explicitly selected an SQL mode that doesn' | ||
- | |||
- | <code php> | ||
- | mysql_set_charset($charset); | ||
- | $var = mysql_real_escape_string("' | ||
- | mysql_query(" | ||
- | </ | ||
- | |||
- | ...because we're quoting our string literal with single-quotes. | ||
- | |||
- | <code php> | ||
- | $stmt = $pdo-> | ||
- | $stmt-> | ||
- | </ | ||
- | |||
- | ...because PDO prepared statements are immune from this vulnerability (and ircmaxell' | ||
- | |||
- | <code php> | ||
- | $var = $pdo-> | ||
- | $stmt = $pdo-> | ||
- | </ | ||
- | |||
- | ...because PDO's quote() function not only escapes the literal, but also quotes it (in single-quote ' characters); | ||
- | |||
- | <code php> | ||
- | $stmt = $mysqli-> | ||
- | $param = "' | ||
- | $stmt-> | ||
- | $stmt-> | ||
- | </ | ||
- | |||
- | ...because MySQLi prepared statements are safe. | ||
- | |||
- | |||
- | |||
- | |||
- | ===== References ===== | ||
- | |||
- | http:// | ||
- | |||
- | https:// |
sql_injection/primary_defenses.1476365135.txt.gz · Last modified: 2020/07/15 09:30 (external edit)