security:sql_injection_sqli:primary_defenses
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
security:sql_injection_sqli:primary_defenses [2020/04/16 21:05] – created peter | security:sql_injection_sqli:primary_defenses [2020/07/15 09:30] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Security - SQL Injection (SQLi) - Primary Defenses ====== | ====== Security - SQL Injection (SQLi) - Primary Defenses ====== | ||
+ | ===== Primary Defenses ===== | ||
+ | |||
+ | * Use Prepared Statements (Parameterized Queries) | ||
+ | * Change default passwords | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ===== 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 DBI ==== | ||
+ | |||
+ | 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:: | ||
+ | |||
+ | ==== Python DB-API ==== | ||
+ | |||
+ | This example uses Python DB-API with SQLite and paramstyle=' | ||
+ | |||
+ | <code sql> | ||
+ | import sqlite3 | ||
+ | conn = sqlite3.connect(': | ||
+ | c = conn.cursor() | ||
+ | |||
+ | _users = [(' | ||
+ | (' | ||
+ | (' | ||
+ | c.executemany(' | ||
+ | |||
+ | params = (' | ||
+ | c.execute(' | ||
+ | c.fetchone() | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== C# ADO.NET ==== | ||
+ | |||
+ | This example uses C# and ADO.NET. | ||
+ | |||
+ | <code sql> | ||
+ | using (SqlCommand command = connection.CreateCommand()) | ||
+ | { | ||
+ | command.CommandText = " | ||
+ | |||
+ | command.Parameters.AddWithValue(" | ||
+ | command.Parameters.AddWithValue(" | ||
+ | |||
+ | using (SqlDataReader dataReader = command.ExecuteReader()) | ||
+ | { | ||
+ | // ... | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 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; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ===== Change default passwords ===== | ||
+ | |||
+ | It is highly recommended to set a default password for the database as well to prevent SQL injection in SQL Server, from which the database administrator can utilize other functionalities of the database. | ||
+ | |||
+ | ^Name^User^Password^ | ||
+ | |Oracle|Sys|Oracle| | ||
+ | |MySQL|Root|null| | ||
+ | |MS SQL Server|Sa|null| | ||
+ | |DB2|Dlfm|ibmdb2| | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ===== References ===== | ||
+ | |||
+ | http:// | ||
+ | |||
+ | https:// | ||
+ | |||
+ | http:// |
security/sql_injection_sqli/primary_defenses.1587071115.txt.gz · Last modified: 2020/07/15 09:30 (external edit)