SQL Injection: complete walkthrough (not only) for PHP developers
Upcoming SlideShare
Loading in...5
×
 

SQL Injection: complete walkthrough (not only) for PHP developers

on

  • 24,052 views

Learn what is SQL injection, how to use prepared statements, how to escape and write secure stored procedures. Many PHP projects are covered - PDO, Propel, Doctrine, Zend Framework and MDB2. Multiple ...

Learn what is SQL injection, how to use prepared statements, how to escape and write secure stored procedures. Many PHP projects are covered - PDO, Propel, Doctrine, Zend Framework and MDB2. Multiple gotchas included.

Statistics

Views

Total Views
24,052
Views on SlideShare
19,393
Embed Views
4,659

Actions

Likes
19
Downloads
641
Comments
3

27 Embeds 4,659

http://php.dzone.com 2080
http://blog.kotowicz.net 1097
http://scipion.es 636
http://it-republik.de 362
http://entwickler.com 122
http://www.slideshare.net 90
http://www.sfexception.com 77
http://entwickler.de 54
http://www.wilsolutions.com.br 39
http://css.dzone.com 18
http://wissen 17
http://www.scoop.it 16
http://localhost 8
http://translate.googleusercontent.com 8
http://www.dinkeskaltim.com 7
http://phpmagazin.de 7
http://sql.dzone.com 4
http://architects.dzone.com 4
http://wissen:8686 3
http://www.linkedin.com 3
http://translate.yandex.net 1
http://www.developpez.net 1
http://twitter.com 1
https://twitter.com 1
http://sqlcon.net 1
http://webcache.googleusercontent.com 1
https://www.linkedin.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

13 of 3 Post a comment

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • good one
    Are you sure you want to
    Your message goes here
    Processing…
  • thank u
    Are you sure you want to
    Your message goes here
    Processing…
  • good
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    SQL Injection: complete walkthrough (not only) for PHP developers SQL Injection: complete walkthrough (not only) for PHP developers Presentation Transcript

    • SQL injection: complete walktrough (not only) for PHP developers Krzysztof Kotowicz PHP Developer http://web.eskot.pl OWASP Medycyna Praktyczna krzysztof@kotowicz.net 10.03.2010 Copyright © The OWASP Foundation Permission is granted to copy, distribute and/or modify this document under the terms of the OWASP License. The OWASP Foundation http://www.owasp.org
    • Plan  What is SQL injection?  Why is it so dangerous (demo)?  How to defend? • Prepared statements • Escaping • Stored procedures • Additional methods  Summary OWASP 2
    • Discussed databases (RDBMS)  MySQL  Oracle  MS SQL Server  To some extent: • PostgreSQL • SQLite OWASP 3
    • Discussed PHP projects  PDO – PHP data objects • Common interface for various RDBMS  Doctrine 1.2 • ORM (Object Relational Mapper) used e.g. in Symfony framework  Propel 1.4 • ORM, like Doctrine • Used in Symfony  Zend Framework 1.10 • Popular framework MVC for PHP  MDB2 2.4.1 • Database abstraction layer (DBAL) • Distributed through PEAR OWASP 4
    • What is SQL injection? OWASP 5
    • SQL injection – short definition It is a kind of web application attack, where user- supplied input coming from: URL: www.example.com?id=1 Forms: email=a@example.com Other elements: e.g. cookie, HTTP headers is manipulated so that vulnerable application executes SQL commands injected by attacker. OWASP 6
    • Example – login form SELECT * FROM users WHERE login = '{$login}' and password_hash = MD5('{$password}') $login = "' or 1=1 -- "; "anything"; $password = "dowolne"; // zamierzalismy osiagnacthis(kod dane) you wanted to achieve to (code data) SELECT * FROM users WHERE login = '' or 1=1 -- ' and password_hash = MD5('dowolne') MD5('anything') // but server interprets it as SELECT * FROM users WHERE login = '' or 1=1 -- ' and password_hash = MD5('anything') User logs in without knowing the login nor password OWASP 7
    • Why is it so dangerous? DEMO OWASP 8
    • What are the possible threats?  Unauthorized access to application  Access to whole database / databases on the server  Denial of service  Database modification  Read / write files on server's filesystem  Code execution OWASP 9
    • A few facts  Injection vulnerabilities are the 1st on OWASP Top 10 2010 RC  SQLi is responsible for 40–60% cases of data breach [1] [2]  Modern attack techniques are advanced and automated • Vulnerability is not only in WHERE part • Sometimes it is enough to break a query  Vulnerabilities are found on a daily basis, even in new applications OWASP 10
    • How to defend? OWASP 11
    • How to defend against SQL injection?  Source of vulnerability is mixing code with data SELECT * FROM users WHERE login = 'login' Defense methods Separating code from data prepared statements stored procedures Escaping OWASP 12
    • How to defend? Prepared statements OWASP 13
    • Prepared statements – how to use? 1. Preparing SQL command (string) Put placeholders where data should be WHERE a = ? ... WHERE a = :col 2. Send command to server PREPARE 3. Attach data to command 4. Execute command EXECUTE 5. Fetch results 3, 4, 5 could be repeated... 6. Clear the command OWASP 14
    • Prepared statements - example  PDO // prepare command $stmt = $dbh->prepare("INSERT INTO SUMMARIES (name, sum) VALUES (:name, :sum)"); // attach data variables - WITH ITS TYPES! $stmt->bindParam(':name', $name, PDO::PARAM_STR); $stmt->bindParam(':sum', $sum, PDO::PARAM_INT); // bind values $name = 'something'; $value = 1234; // execute command $stmt->execute(); $stmt = null; //free memory OWASP 15
    • Prepared statements - advantages  Commands are completely separated from data they operate on  Injection is not possible  Command is compiled only once - potential speedup $stmt->bindParam(':name', $name, PDO::PARAM_STR); $stmt->bindParam(':sum', $sum, PDO::PARAM_INT); // petla po danych... foreach ($do_bazy as $name => $value) { $stmt->execute(); } OWASP 16
    • Prepared statements - caveats  Not all commands may be parametrised  You cannot put parameters everywhere -- error SELECT * FROM :table SELECT :function(:column) FROM :view -- not what you expect SELECT * FROM table WHERE :column = 1 SELECT * FROM table GROUP BY :column  Just using PS does not enforce using parameters in them  Sometimes they're emulated (it's a good thing!) OWASP 17
    • Prepared statements in Doctrine  Uses PDO (emulated for Oracle) and prepared statements  Uses own DQL language instead of SQL $q = Doctrine_Query::create() ->select('u.id') ->from('User u') ->where('u.login = ?', ‘mylogin'); echo $q->getSqlQuery(); // SELECT u.id AS u__id FROM user u // WHERE (u.login = ?) $users = $q->execute(); OWASP 18
    • Prepared statements in Doctrine cont.  It can still bite you $q = Doctrine_Query::create() ->update('Account') ->set('amount', 'amount + 200') ->where("id > {$_GET['id']}");  Correct this to: ->where("id > ?", (int) $_GET['id']);  NEVER put input data directly into SQL commands OWASP 19
    • Prepared statements in Propel  Uses PDO, like Doctrine // through Criteria $c = new Criteria(); $c->add(AuthorPeer::FIRST_NAME, "Karl"); $authors = AuthorPeer::doSelect($c); // through custom SQL (sometimes it's more convenient) $pdo = Propel::getConnection(BookPeer::DATABASE_NAME); $sql = "SELECT * FROM complicated_sql JOIN some_big_join USING something WHERE column = :col)”; $stmt = $pdo->prepare($sql); $stmt->execute(array('col' => 'Bye bye SQLi!'); OWASP 20
    • Prepared statements in Zend Framework  PDO (+ mysqli + oci8 + sqlsrv) // prepare + execute $stmt = $db->prepare('INSERT INTO server (key, value) VALUES (:key,:value)'); $stmt->bindParam('key', $k); $stmt->bindParam('value', $v); foreach ($_SERVER as $k => $v) $stmt->execute(); // prepare + execute in one step $stmt = $db->query('SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?', array('goofy', 'FIXED')); while ($row = $stmt->fetch()) echo $row['bug_description']; OWASP 21
    • Prepared statements in MDB2  Based on different database drivers (mysql, oci8, mssql, ...)  Emulates PS, if database doesn't support them $types = array('integer', 'text', 'text'); $stmt = $mdb2->prepare('INSERT INTO numbers VALUES (:id, :name, :lang)', $types); $data = array('id' => 1, 'name' => 'one', 'lang' => 'en'); $affectedRows = $stmt->execute($data); $stmt->free(); OWASP 22
    • Prepared statements - summary  They offer very good protection (if used properly)  Easy to use, small changes in code  Good support in frameworks  They have their limits  Sometimes they have to be used with other defense methods OWASP 23
    • How to defend? Escaping data OWASP 24
    • Escaping – how does it work?  Data and commands are still kept in a single variable, but we try to separate them inline  Numbers • Cast to (int) / (float) – don't use is_numeric [1]!  Texts are surrounded with single quotes : ' .. WHERE col = 'TEXT DATA' AND ... • If quote is inside the text, you need a way to distinguish it from the ending quote • Prepend a special character e.g. "" to a quote • Escaping rules depend on context! OWASP 25
    • Escaping – context addslashes() Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte). / Source: php.net manual / $user = addslashes($_GET['u']); $pass = addslashes($_GET['p']); $sql = "SELECT * FROM users WHERE username = '{$user}' AND password = '{$pass}'"; $ret = exec_sql($sql);  Are you safe? OWASP 26
    • NO OWASP
    • Escaping – context cont.  Different RDBMS have different ways of escaping data (it also depends on configuration)  addslashes() works just like MySQL only „by chance” RBDMS PHP function i've got quotes PDO $pdo->quote($val, $type) n/a (it depends) MySQL (mysql) mysql_real_escape_string i've got quotes MySQL (mysqli) mysqli_real_escape_string i've got quotes Oracle (oci8) n/d - str_replace() i''ve got quotes SQLite sqlite_escape_string i''ve got quotes MS SQL (mssql) n/d - str_replace() i''ve got quotes PostgreSQL pg_escape_string() i''ve got quotes OWASP 28
    • Escaping – context cont. // SELECT * FROM users WHERE username = // '{$user}' AND password = '{$pass}' $_GET['u'] = "anything'"; $_GET['p'] = " or 1=1 -- "; // MySQL sees it as : SELECT * FROM users WHERE username = 'anything'' AND password = ' or 1=1 -- ' // SQLite / MS SQL / Oracle / PostgreSQL: SELECT * FROM users WHERE username = 'anything'' AND password = ' or 1=1 -- '  Don't use addslashes(), use PHP functions for your RBDMS  Are you safe now? OWASP 29
    • ALMOST OWASP
    • Escaping gotchas – charsets  Errors discovered in 2006 in PostgreSQL and MySQL [1] [2]  In some multibyte charsets despite escaping you can cause SQL injection  is „swallowed” by multibyte character  Example: • BF 27 [ ¬ ' ]  BF 5C 27 [ ¬ ' ] • First 2 bytes are character ¿ in GBK charset • Server will see ¿' OWASP 31
    • Escaping gotchas – charsets  Some Asian charsets are vulnerable  Luckily - not UTF-8!  In PostgreSQL '' escaping was used (instead of ')  In mysql_real_escape_string() escaping is done with respect to current connection charset • Doesn't always work! [1] [2]  Charset also defines context OWASP 32
    • Escaping gotchas – object names  Colum, table, database etc. names • No common good rule to escape them • Different reserved words, different maximum name lengths etc. If you need to get those names from the user - use whitelisting (blacklisting if you really can't do otherwise) OWASP 33
    • Escaping gotchas – object names cont.  Example - sorting by column  There's a vuln. in $order, but you can't escape there $cat_id = (int) $_GET['cid']; $order = $_GET['column']; $stmt = $pdo->prepare("SELECT * FROM products WHERE cid = :cid ORDER BY $order"); $stmt->bindParam(':cid', $cat_id, PDO::PARAM_INT); if ($stmt->execute()) { ... } OWASP 34
    • Escaping gotchas – object names cont.  Whitelisting $columns = array( // list of allowed columns 'product_name','cid','price', ); if (!in_array($order, $columns, true)) $order = 'product_name'; // default column  Blacklisting // only a-z and _ $order = preg_replace('/[^a-z_]/', '', $order); // max 40 characters $order = substr($order, 0, 40); OWASP 35
    • Escaping in PDO  PDO::quote($value, $type, $len)  Length and type are sometimes ignored! • Cast numbers to (int), (float) • Texts – cut them manually $quoted = $pdo->quote($input, PDO::PARAM_STR, 40); OWASP 36
    • Escaping in Doctrine  Careful with Doctrine quote()! $q = Doctrine_Query::create(); // not like this!!! $quoted = $q->getConnection()->quote($input, 'text'); $q->update('User')->set('username', $quoted); // quote() only changes ' to '' - exploit (MySQL): $input = 'anything' where 1=1 -- '; // escape through PDO - getDbh(): $quoted = $q->getConnection() ->getDbh() ->quote($input, PDO::PARAM_STR); // 'anything ' where 1=1 -- ' OWASP 37
    • Escaping in Propel  Through PDO::quote() $pdo = Propel::getConnection(UserPeer::DATABASE_NAME); $c = new Criteria(); $c->add(UserPeer::PASSWORD, "MD5(".UserPeer::PASSWORD.") " ." = " . $pdo->quote($password), Criteria::CUSTOM); OWASP 38
    • Escaping in Zend Framework  Functions quote(), quoteInto() $name = $db->quote("O'Reilly"); // 'O'Reilly' // simplified escaping for a single value $sql = $db->quoteInto("SELECT * FROM products WHERE product_name = ?", 'any string'); OWASP 39
    • Escaping in MDB  quote() // quote() function - give type $query = 'INSERT INTO table (id, itemname, saved_time) VALUES (' . $mdb2->quote($id, 'integer') .', ' . $mdb2->quote($name, 'text') .', ' . $mdb2->quote($time, 'timestamp') .')'; $res = $mdb2->exec($query); OWASP 40
    • Escaping - summary  Looks easy - search and replace  Just looks • You need to know the context (database, charset) • There are invalid implementations  Encourages invalid practices • concatenating strings to form a SQL command • ignoring numeric parameters  Use only if • You program for a single RDBMS • There is no other way OWASP 41
    • How to defend? Stored procedures OWASP 42
    • Stored procedures  SQL command(s) is moved to database server and stored there under a name  Client executes a procedure with input and output parameters  In output parameters client receives results  Data is formally separated from code  It's NOT enough OWASP 43
    • Stored procedures cont.  Example for MS SQL – a vulnerable procedure CREATE PROCEDURE SP_ProductSearch @prodname varchar(400) AS DECLARE @sql nvarchar(4000) SELECT @sql = 'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName LIKE ''' + @prodname + '''' EXEC (@sql) ...  It's just like eval()! OWASP 44
    • Stored procedures cont.  Same vulnerability in Oracle CREATE OR REPLACE PROCEDURE SP_ProductSearch(Prodname IN VARCHAR2) AS sqltext VARCHAR2(80); BEGIN sqltext := 'SELECT ProductID, ProductName, Category, Price FROM Product WHERE ProductName LIKE ''' || Prodname || ''''; EXECUTE IMMEDIATE sqltext; ... END; OWASP 45
    • Stored procedures – Dynamic SQL  Vulnerability lies in Dynamic SQL • Data is again mixed with code in one variable  How to defend? • Separate the code from data • Escape OWASP 46
    • Stored procedures in MS SQL  Separating code from data • use sp_executesql with parameter list CREATE PROCEDURE SP_ProductSearch @prodname varchar(400) = NULL AS DECLARE @sql nvarchar(4000) SELECT @sql = N'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName LIKE @p' EXEC sp_executesql @sql, N'@p varchar(400)', @prodname OWASP 47
    • Stored procedures in MS SQL cont.  Escaping character data Object name QUOTENAME(@v) Text <= 128 chars QUOTENAME(@v,'''') Text > 128 chars REPLACE(@v,'''','''''')  Example: SET @cmd = N'select * from authors where lname=''' + REPLACE(@lname, '''', '''''') + N''''  Escape only when you must! (use sp_executesql with parameters) OWASP 48
    • Stored procedures in Oracle  Oracle - use EXECUTE IMMEDIATE .. USING CREATE OR REPLACE PROCEDURE SP_ProductSearch(Prodname IN VARCHAR2) AS sqltext VARCHAR2(80); BEGIN sqltext := 'SELECT ProductID, ProductName, Category, Price WHERE ProductName=:p'; EXECUTE IMMEDIATE sqltext USING Prodname; ... END;  Escaping - DBMS_ASSERT package OWASP 49
    • Stored procedures in MySQL  Support for Dynamic SQL only through prepared statements  It's actually harder to make vulnerable procedure  Just use placeholders OWASP 50
    • Stored procedures in MySQL cont.  PREPARE / EXECUTE USING / DEALLOCATE PREPARE DELIMITER $$ CREATE PROCEDURE get_users_like ( IN contains VARCHAR(40)) BEGIN SET @like = CONCAT("%", contains, "%"); SET @sql = "SELECT * FROM users WHERE uname LIKE ?"; PREPARE get_users_stmt from @sql; EXECUTE get_users_stmt USING @like; DEALLOCATE PREPARE get_users_stmt; END$$ DELIMITER ; OWASP 51
    • Stored procedures in MySQL cont.  Or, even simpler DELIMITER $$ CREATE PROCEDURE get_users_like ( IN contains VARCHAR(40)) BEGIN SET @like = CONCAT("%", contains, "%"); SELECT * FROM users WHERE uname LIKE @like; END$$ DELIMITER ;  Escaping – QUOTE() function OWASP 52
    • Stored procedures in PHP  Different support level, depending on RDBMS  Common API (e.g. PDO) only for simple calls • No return from procedure • Returns scalar value in OUT parameter  Different API (or none at all) for advanced calls • e.g. cursors, fetching records sets  Almost no support in frameworks  Still some errors... OWASP 53
    • Stored procedures in PDO  Calling a procedure // MySQL $sql = "CALL get_users_like(:contains)"; // MS SQL – EXEC get_users_like :contains $stmt = $pdo->prepare($sql); $ret = $stmt->execute(array('contains' => $input)); foreach($stmt->fetchAll() as $users) { var_dump($users); } unset($s); OWASP 54
    • Stored procedures in Doctrine/Propel/Zend Framework  Doctrine - no support (use PDO) $pdo = Doctrine_Manager::connection()->getDbh();  Propel – likewise $pdo = Propel::getConnection(UserPeer::DATABASE_NAME);  Zend Framework – likewise $pdo = $db::getConnection(); OWASP 55
    • Stored procedures in MDB2  You need to manually escape all parameters! $mdb2->loadModule('Function'); $multi_query = $mdb2->setOption('multi_query', true); if (!PEAR::isError($multi_query)) { $result = $mdb2->executeStoredProc('get_users_like', array($mdb2->quote($contains, 'text'))); do { while ($row = $result->fetchRow()) { var_dump($row); } } while ($result->nextResult()); } OWASP 56
    • Stored procedures - gotchas  Data length CREATE PROCEDURE change_password @loginname varchar(50), @old varchar(50), @new varchar(50) AS DECLARE @command varchar(120) SET @command= 'UPDATE users SET password=' + QUOTENAME(@new, '''') + ' WHERE loginname=' + QUOTENAME(@loginname, '''') + ' AND password=' + QUOTENAME(@old, '''') EXEC (@command) GO OWASP 57
    • Stored procedures - summary  Moving SQL logic to server takes time  Code is not easily ported to other RDBMS  You need to use prepared statements or escaping to write safe stored procedures anyway  If done poorly, you're even more vulnerable • Both SP code and statement calling SP could be vulnerable • SP usually has greater permissions than code calling it  Bad support in PHP and frameworks OWASP 58
    • Stored procedures - summary SPs have many advantages outside our scope  Could be used with different clients (Java/.NET + PHP)  Could have better berformance  and many more... Conclusion: You can write secure stored procedures, but they usually increase the application cost considerably It is vital to write stored procedures protected against SQL injection OWASP 59
    • How to defend? Additional methods OWASP 60
    • Validation and filtering  Validate all external data  Validate before processing  Filter INPUT - escape OUTPUT  Different validation rules for each parameter - check e.g. • Type • Scalar / array • Min / max values • Character data length! [1] OWASP 61
    • Additional methods Complementary to all previously mentioned!  Principle of least privilege when connecting to DB  Removing unused functions, accounts, packages shipped with database  Routinely updating the system and database software  Correct PHP and database configuration • magic_quotes_* = false • display_errors = false  Good database design OWASP 62
    • Summary  Pay attention to SQL injection - even a single mistake could cost you!  Prefer complete solutions - e.g. frameworks  Filter and validate all input data  Remeber about data types and lengths  Prefer whitelisting to blacklisting - the latter will fail one day!  Use prepared statements whenever you can  Try to avoid escaping  In stored procedures double check your Dynamic SQL OWASP 63
    • Links  Discussed projects • sqlmap.sourceforge.net • php.net/manual/en/book.pdo.php • www.doctrine-project.org • propel.phpdb.org/trac • framework.zend.com • pear.php.net/package/MDB2  About SQL injection • www.owasp.org/index.php/SQL_Injection • unixwiz.net/techtips/sql-injection.html • delicious.com/koto/sql+injection  Hack me • threats.pl/bezpieczenstwo-aplikacji-internetowych • tinyurl.com/webgoat • mavensecurity.com/dojo.php  krzysztof@kotowicz.net http://blog.kotowicz.net OWASP 64