Your SlideShare is downloading. ×
0
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Good practices for PrestaShop code security and optimization
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Good practices for PrestaShop code security and optimization

11,672

Published on

Good practices for PrestaShop code security and optimization - Rémi Gaillard - SITTI group

Good practices for PrestaShop code security and optimization - Rémi Gaillard - SITTI group

Published in: Business
1 Comment
4 Likes
Statistics
Notes
No Downloads
Views
Total Views
11,672
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
1
Likes
4
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. PrestaShop securityimprovements and optimizations<br />
  • 2. <ul><li>Group Sitti (www.sitti.fr)
  • 3. Team of 6 developers & integrators
  • 4. 400 Prestashop installed – ranging from 0.9.6 to 1.3.1
  • 5. Shared hosting – cluster of 10+ machines (load balancers, web servers, file servers, database servers) </li></ul>About us ?<br />
  • 6. 4 Pillars of performance<br /><ul><li>Infrastructure(servers, databases)
  • 7. Our focus: Server-side code (1-st tier, php + sql)
  • 8. Network, transport protocols
  • 9. Client-side code (2-nd tier: html + css + javascript) </li></li></ul><li>(…) Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: 1<br />premature optimization is the root <br />of all evil.<br />Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified.<br />Most important disclaimer (Donald E. Knuth)<br />
  • 10. Your architecture has to be efficient (good planning)<br />You have to code using best practices (don't do **obviously** stupid things)<br />But prefer rather maintability and readibility of code over the speed<br />When speed is not critical (i.e. real time systems, high traffic sites), you can improve it in  later iterations<br />When to optimize?<br />
  • 11. Measure first! You should know bottlenecks.<br />Benchmark different scenarios and configs<br />Going Linux? Test Linux, not Win. There are differences <br />Will have 10000 products in your store? Test your modules with db of 10000, not 5<br />Is a 1% improvement worth of additional work?<br />What about 5%? 10%?<br />Try to estimate coding cost vs. hardware cost<br />Sometimes it's just cheaper to add RAM<br />What to optimize?<br />
  • 12. Small performance gains<br />Using (int) instead of intval() can be even 4 X faster<br />But overall gain is negligable (unless you are Facebook)<br />Code executed once<br />Tools::setCookieLanguage could be improved, but it is executed once<br />Mythical optimisations ( ” vs ' )<br />But ”$a $b $c” … is faster than $a.” ”.$b.” ”.$c<br />Whatshouldn'tbeoptimised<br />
  • 13. Server load:<br />ab, siege, multi-mechanize ...<br />Databaseload:<br />MySql Slow Query Log, mysql proxy, ...<br />EXPLAIN <br />PHP:<br />xdebug, dbg, xhprof ...<br />Network / client side<br />Yslow, firebug, WebKitinspector, dynaTrace AJAX, fiddler, google webmaster tools<br />How to measure?<br />
  • 14. Server:<br />Difficult task, often impossible on shared hostings<br />Ask your admin<br />CPU is rarely a bottleneck, generally indicates problems with suboptimal code<br />RAM is cheap but not unlimited – attention to memory consuming scripts<br />Typical problem: gd + jpg -> 2 Mb on disk, 33 Mb decompressed into memory<br />Ramdisk for often accessed, not critical files (frameworks, configuration, tmp) <br />Most common bottleneck: I/O (filesystem, dbs)<br />Improving infrastructure<br />
  • 15. Every call to fs costs, depending the OS, filesystem and number of files <br />Always use absolute paths in require / include<br />Performance may start to degrade if you have more than 50 000 files in a directory<br />Each product has image, each image has 6 thumbnails<br />Debian + Apache 1.3 (shared hosting, nfs):<br />Filesystem<br /># Files<br />Glob('*') exec. in sec.<br />file_exists / sec.<br />1000<br />4,59<br />36000<br />11000<br />13,30<br />21000<br />65000<br />55,81<br />1475<br />122000<br />142,16<br />718<br />
  • 16. Directory content splitting:<br />img/p/534-189-small.jpg<br />becomes<br />img/p/small/534-189.jpg<br />Reading transparently via .htaccess<br />RewriteRule (.*)/p/([^/]*)home.jpg $1/p/home/$2home.jpg<br />Writingtransparently via class <br /> if (!imageResize($file, $dir.$imageType['name'].'/'.$language['iso_code'].'-default- '.stripslashes($imageType['name']).'.jpg', ...<br />Solution<br />
  • 17. Database!<br /><ul><li>Check your indexes!
  • 18. Avoid to using too many JOINS</li></ul>SELECT * FROM ps_feature` f LEFT JOIN ps_feature_lang` fl ON ( f.`id_feature` = fl.`id_feature` AND fl.`id_lang` = 1) WHERE f.`id_feature` = 1SELECT * FROM ps_feature_lang` fl WHER fl.`id_feature` = 1 AND fl.`id_lang` = 1 <br />Version<br />Tables<br />Columns<br />Without index<br />1.1.0.5<br />88<br />458<br />50<br />1.2.0.5<br />134<br />670<br />50<br />1.3.10<br />135<br />679<br />2 (cool! :)<br />
  • 19. Use VIEWS instead of complicated SELECTS<br />Are you needing ps_connections & ps_connections_page?<br />If you are expecting high traffic, thay can rise 10+ Mb / day<br />Database<br />
  • 20. Big problem - non unique queries<br />1.3.10, simulation of command process:<br />Index – search – authentication – order (11 pages total) <br />3001 SQL queries, but only 1314 uniques! (44%) <br />PHP - SQL<br />
  • 21. Repeatedqueries<br />
  • 22. Non–optimisedqueries<br />
  • 23. Best is use mysql proxy or memcachedNot always possible<br />Do not resolve overhead of unnecessary calls <br />Use internal cacheCan be scoped or globalPrestashop partially uses scoped cacheEasy to implement, tune, and … forget<br />Each method / class is responsable for caching its query results<br />Solutions<br />
  • 24. static public function getCurrency($id_currency){<br /> return Db::getInstance()->getRow('SELECT * FROM `'._DB_PREFIX_.'currency` WHERE `deleted` = 0 AND `id_currency` = '.intval($id_currency));<br /> }<br />static public functiongetCurrency($id_currency){<br /> if (!isset(self::$_cache[$id_currency])) {<br /> self::$_cache[$id_currency] = Db::getInstance()->getRow('SELECT * FROM `'._DB_PREFIX_.'currency` WHERE `deleted` = 0 AND `id_currency` = '.intval($id_currency));<br /> }<br /> return self::$_cache[$id_currency];<br /> }<br />Scoped cache<br />
  • 25. <ul><li>Direct in MySqlclass
  • 26. Catches all output
  • 27. Harder to implement
  • 28. Some queries can be repeated but expecting different result (->cart)
  • 29. Needs kind of "blacklist"
  • 30. Once implemented, makes application maintenance much easier
  • 31. Should be implemented as core feature</li></ul>Global cache<br />
  • 32. <ul><li>Regexp is costly, and complicated</li></ul> return preg_match('/^[a-z0-9!#$%&'*+/=?^`{}|~_-]+[.a-z0- 9!#$%&'*+/=?^`{}|~_-]*@[a-z0-9]+[._a-z0-9-]*.[a-z0-9]+$/ui', $email);<br /><ul><li>Use filters (> PHP 5.2):</li></ul> return filter_var($email, FILTER_VALIDATE_EMAIL);<br /><ul><li>Or test before running costly tests:</li></ul> if (strpos($email, '@')!==false)<br /><ul><li>Use str_replace instead of preg_replace:</li></ul>preg_replace('/"/', '&quot;', $value)<br /> Faster: str_replace('"', '&quot;', $value)<br />Avoiding regexpSome people, when confronted with a problem, think  “I know, I'll use regular expressions.” Now they have two problems. (jwz)<br />
  • 33. <ul><li>Avoid capturing groups</li></ul>return preg_match('/^([^<>{}]|<br />)*$/ui', $text);<br /> return preg_match('/^(?:[^<>{}]|<br />)*$/ui', $text);<br /> ?: = non capturing group (no memory allocation!)<br /><ul><li>You often don't need regexp</li></ul>return trim($table,'a..zA..Z0..9_') == '';<br /> equals to<br /> return preg_match('/^[a-z0-9_-]+$/ui', $table);<br /> but is up to 2 times faster!<br />Avoidingregexp (2)<br />
  • 34. foreach($cart->getProducts() as $product)<br />   if ($orderStatus->logable)<br />      ProductSale::addProductSale(intval($product['id_product']), intval($product['cart_quantity']));<br />Should be:<br />if ($orderStatus->logable)<br />     foreach($cart->getProducts() as $product)<br />            ProductSale::addProductSale(intval($product['id_product']), intval($product['cart_quantity']));<br />(no need to test if in every iteration if it does not change)<br />Use conditions wisely<br />
  • 35. // Send an e-mail to customer<br />if ($id_order_state!= _PS_OS_ERROR_ AND $id_order_state!= _PS_OS_CANCELED_ AND $customer->id)<br />{<br />$invoice = new Address(intval($order->id_address_invoice));<br />$delivery = new Address(intval($order->id_address_delivery));<br />$carrier = new Carrier(intval($order->id_carrier));<br />$delivery_state= $delivery->id_state ? new State(intval($delivery->id_state)) : false;<br />$invoice_state= $invoice->id_state ? new State(intval($invoice->id_state)) : false;<br />$data = array( <br />'{firstname}' => $customer->firstname,<br />'{lastname}' => $customer->lastname,<br />'{email}' => $customer->email,<br />'{delivery_company}' => $delivery->company,<br />'{delivery_firstname}' => $delivery->firstname,<br />'{delivery_lastname}' => $delivery->lastname,<br />'{delivery_address1}' => $delivery->address1,<br />'{delivery_address2}' => $delivery->address2,<br />'{delivery_city}' => $delivery->city,<br />'{delivery_postal_code}' => $delivery->postcode,<br />'{delivery_country}' => $delivery->country,<br />'{delivery_state}' => $delivery->id_state ? $delivery_state->name : '',<br />'{delivery_phone}' => $delivery->phone,<br />'{delivery_other}' => $delivery->other,<br />'{invoice_company}' => $invoice->company,<br />'{invoice_firstname}' => $invoice->firstname,<br />'{invoice_lastname}' => $invoice->lastname,<br />'{invoice_address2}' => $invoice->address2,<br />'{invoice_address1}' => $invoice->address1,<br />'{invoice_city}' => $invoice->city,<br />'{invoice_postal_code}' => $invoice->postcode,<br />'{invoice_country}' => $invoice->country,<br />'{invoice_state}' => $invoice->id_state ? $invoice_state->name : '',<br />'{invoice_phone}' => $invoice->phone,<br />'{invoice_other}' => $invoice->other,<br />{order_name}' => sprintf("#%06d", intval($order->id)),<br />'{date}' => Tools::displayDate(date('Y-m-d H:i:s'), intval($order->id_lang), 1),<br />'{carrier}' => (strval($carrier->name) != '0' ? $carrier->name : Configuration::get('PS_SHOP_NAME')),<br />'{payment}' => $order->payment,<br />Can you spot the problem?<br />
  • 36. '{products}' => $productsList,<br />'{discounts}' => $discountsList,<br />'{total_paid}' => Tools::displayPrice($order->total_paid, $currency, false, false),<br />'{total_products}' => Tools::displayPrice($order->total_paid - $order->total_shipping - $order->total_wrapping + $order->total_discounts, $currency, false, false),<br />'{total_discounts}' => Tools::displayPrice($order->total_discounts, $currency, false, false),<br />'{total_shipping}' => Tools::displayPrice($order->total_shipping, $currency, false, false),<br />'{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $currency, false, false));<br />if (is_array($extraVars))<br /> $data = array_merge($data, $extraVars);<br />// Join PDF invoice<br />if (intval(Configuration::get('PS_INVOICE')) AND Validate::isLoadedObject($orderStatus) AND $orderStatus->invoice AND $order->invoice_number)<br />{<br /> $fileAttachment['content'] = PDF::invoice($order, 'S');<br /> $fileAttachment['name'] = Configuration::get('PS_INVOICE_PREFIX', intval($order->id_lang)).sprintf('%06d', $order->invoice_number).'.pdf';<br /> $fileAttachment['mime'] = 'application/pdf';<br />}<br />else<br /> $fileAttachment= NULL;<br />if ($orderStatus->send_email AND Validate::isEmail($customer->email))<br /> Mail::Send(intval($order->id_lang), 'order_conf', 'Order confirmation', $data, $customer->email, $customer->firstname.' '.$customer->lastname, NULL, NULL, $fileAttachment);<br />$this->currentOrder = intval($order->id);<br />return true;<br />}<br />$this->currentOrder = intval($order->id);<br />return true;<br />
  • 37. We are preparing whole mail, including pdfattachement, even if we are not sending it.<br /> Every times you do it, a little kitten dies<br />Non optimised conditions<br />
  • 38. <ul><li>Consider export / import as heavy operations
  • 39. For flux Beezup we are using ObjectModel
  • 40. It works, but we have 17 sql queries / product to collect all data (product, features, attributes, images...)
  • 41. Ok for 100 products. What about 100 000 ?
  • 42. Risky if we had to generate it on-demand
  • 43. Cron prepares output before robot crawls
  • 44. Robot hits cached xml</li></ul>Use cron to generate cache<br />
  • 45. <ul><li>Sending compressed content saves bandwith
  • 46. For static content use mod_gzip / mod_deflate
  • 47. For php files there is simple patch
  • 48. In init.php change:</li></ul>ob_start();<br /> to:<br />ob_start('ob_gzhandler');<br />And enjoy! (page index.php: before: 21.1 kb, after: 5.3 kb)<br />(yet better is to use zlib.* directives in php.ini)<br /><ul><li>Minify your js & css and combine them in one js/css file</li></ul>Network<br />
  • 49. <ul><li>Use CDN (Content Delivery Network) for js
  • 50. Use Cache (mod_expires, Etags) for static content such as images</li></ul>you can do it in htacces or httpd.conf<br />ExpiresActiveOn<br />ExpiresDefault"access plus 15 days“<br />ExpiresByTypeimage/gif A2592000<br />Network<br />
  • 51. <ul><li>Keep DOM selectors simple. Avoid complicated researches; use ids if possible.
  • 52. Jquery isn't always fastest. Search native methods.
  • 53. Avoid passing HTML / XML as AJAX result. Use JSON instead of. You can reduce amount of data by magnitude of 75% (which if of course faster. Which is of course better). </li></ul>Client-sidestuff<br />
  • 54. Botnets<br />Dishonestcompetitors<br />Fraudulentcustomers<br />…<br />Whyprotect?<br />
  • 55. SQL Injection<br />CSRF<br />XSS<br />Pathtranversal<br />…<br />Different types of attacks<br />
  • 56. Allowsyou to interactwith the database<br />Sanitize all your variables before use in SQL requests!<?php<br /> ......<br /> $order_detail = Db::getInstance()->ExecuteS('<br /> SELECT * <br /> FROM .'_DB_PREFIX_.'order_detail<br /> WHERE id_order='.(int)$_GET['id_order']<br /> AND payment=''.pSQL($_GET['payment']).''');<br />SQL Injection<br />
  • 57. Exploit the site's trust in your identity<br />Use tokens<br />Requiring authentication in GET and POST parameters<br />index.php?tab=AdminOrders&token=e84b3fda0b04b922b3bc27b08d4fe136<br />CSRF<br />
  • 58. Inject HTML code in the page<br />Sanitize all your variables before output!<br /><input type="text" name="lastname" value="{$smarty.post.lastname|htmlentities}" /><br />preg_replace('/.*script/ui', '', $_POST['lastname']);<br />preg_replace('/.*onmousedown|onmousemove|onmmouseup|onmouseover|onmouseout|onload|onunload|onfocus|onblur|onchange|onsubmit|ondblclick|onclick|onkeydown|onkeyup|onkeypress|onmouseenter|onmouseleave/ui', '', $_POST['lastname']);<br />...<br />XSS<br />
  • 59. Access to unauthorized datas<br />Sanitize all your variables before load files!<br />Check the extention of the file<br />include (dirname(__FILE__).'/mails/'. preg_replace(‘/.{2,}/', '.', Tools::getValue('mail')).'html'); <br />Path transversal<br />
  • 60. ?<br />Questions<br />

×