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 [2020/07/22 17:53] – old revision restored (2016/10/12 09:23) 207.244.157.10 | help:sql_injection_sqli [2020/07/22 17:55] (current) – old revision restored (2016/10/13 15:20) 207.244.157.10 | ||
---|---|---|---|
Line 4: | Line 4: | ||
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: | ||
+ | |||
+ | [[SQL Injection: | ||
[[SQL Injection: | [[SQL Injection: | ||
Line 9: | Line 13: | ||
[[SQL Injection: | [[SQL Injection: | ||
- | ===== Example attacks ===== | + | [[SQL Injection:What is SQL Injection|What |
- | + | ||
- | **Scenario #1**: The application uses untrusted data in the construction of the following vulnerable | + | |
- | + | ||
- | <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 | + | |
- | + | ||
- | 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:: | + | |
- | + | ||
- | + | ||
- | ==== SQLite ==== | + | |
- | Use **sqlite3_prepare()** | + | [[SQL Injection: |
- | ===== References ===== | ||
- | http:// |
help/sql_injection_sqli.1595440419.txt.gz · Last modified: 2020/07/22 17:53 by 207.244.157.10