help:sql_injection_sqli
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
help:sql_injection_sqli [2016/10/12 08:23] – peter | help:sql_injection_sqli [2020/07/22 17:55] (current) – old revision restored (2016/10/13 15:20) 207.244.157.10 | ||
---|---|---|---|
Line 5: | Line 5: | ||
Injection flaws occur when an application sends untrusted data to an interpreter. | Injection flaws occur when an application sends untrusted data to an interpreter. | ||
- | [[SQL Injection:Example attacks|Example attacks]] | + | [[SQL Injection:Basic types of SQL injections|Basic types of SQL injections]] |
- | ===== Example attacks ===== | + | [[SQL Injection: |
- | **Scenario #1**: The application uses untrusted data in the construction of the following vulnerable | + | [[SQL Injection:Example attacks|Example |
- | + | ||
- | <code java> | + | |
- | String query = " | + | |
- | </ | + | |
- | + | ||
- | **Scenario #2**: Similarly, an application’s blind trust in frameworks may result in queries that are still vulnerable, (e.g., Hibernate Query Language (HQL)): | + | |
- | + | ||
- | <code sql> | + | |
- | Query HQLQuery = session.createQuery(“FROM accounts WHERE custID=' | + | |
- | </ | + | |
- | + | ||
- | In both cases, the attacker modifies the ‘id’ parameter value in her browser to send: ' or ' | + | |
- | + | ||
- | For example: | + | |
- | + | ||
- | This changes the meaning of both queries to return all the records from the accounts table. | + | |
- | + | ||
- | **Scenario #3**: Code to do an insert into the database could also be vulnerable. | + | |
- | + | ||
- | <code sql> | + | |
- | $sql = " | + | |
- | execute_sql($sql); | + | |
- | </ | + | |
- | + | ||
- | The first line creates a string containing an SQL INSERT statement. The content of the **$studentName** variable is glued into the SQL statement. The second line sends the resulting SQL statement to the database. The pitfall of this code is that outside data, in this case the content of $studentName, | + | |
- | + | ||
- | First let's see what the SQL statement looks like if we insert a student named John: | + | |
- | + | ||
- | <code sql> | + | |
- | INSERT INTO Students (Name) VALUES (' | + | |
- | </ | + | |
- | + | ||
- | This does exactly what we want: it inserts John into the Students table. | + | |
- | + | ||
- | Now we insert some injection code by setting $studentName to **< | + | |
- | + | ||
- | <code sql> | + | |
- | INSERT INTO Students (Name) VALUES (' | + | |
- | </ | + | |
- | + | ||
- | This inserts Robert into the Students table. However, the INSERT statement is now followed by a **DROP TABLE** statement which removes the entire Students table. Ouch! | + | |
- | + | ||
- | + | ||
- | ===== 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-> | + | |
- | </ | + | |
- | + | ||
- | === 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:: | + | |
+ | [[SQL Injection: | ||
- | ==== SQLite ==== | + | [[SQL Injection: |
- | Use **sqlite3_prepare()** | + | [[SQL Injection: |
- | ===== References ===== | ||
- | http:// |
help/sql_injection_sqli.1476260582.txt.gz · Last modified: 2020/07/15 09:30 (external edit)