CakeDC Blog

TIPS, INSIGHTS AND THE LATEST FROM THE EXPERTS BEHIND CAKEPHP

Marius Wilms - The CakePHP Media Plugin

If Marius had more than an hour to talk about the Media Plugin, he most certainly would have taken it. To go over the features and functionality of the entire plugin would have been many hours as there is a lot there. A brief touch on the features provided by the plugin was discussed, with some examples.

Requirements are in the high end, but considering the state of PHP and the upcoming version of CakePHP, developers should be moving forward in terms of their PHP version and library support anyway. The Media plugin requires CakePHP 1.2.x.x and PHP 5.2.0+. It enables the transfer, manipulation and embedding of files in many varied ways.

You can find the media plugin at: http://github.com/davidpersson/media

Marius' focus was on doing media manipulation and embedding "properly", and identified that while there are lots of user contributions floating around the net, none of them were meeting his needs and were flexible enough. One of the main points he made here was that if done incorrectly, potential security risks arise due to command line interaction and file saving. Validation was one particular section of the code that made this a tricky plugin to develop, but allowed tests to be implemented to ensure security.

Some common points that we hear all the time came through, and they make sense for CakePHP as well as any web application for security reasons:

 

  1. Don't trust users supplied filenames
  2. Don't store files in an accessible webroot, rather have them accessible to scripts.
  3. Make the upload location (and local filenames) unguessable (like referencing files by UUIDs)
The media plugin contains about 8 new rules for file validation purposes to ensure that submitted data meets the application needs. Beyond validation, it handles all kinds of uploads, HTTP Post, Remote HTTP and local file inclusion.
A console is included to initialize the default directory structure, and as such, could be included as part of a deployment script with the CakePHP console.examples.

To ensure flexibility of use, a behavior is included to allow attachment to any number of models, and generioc storage and linking provided to ease integration into existing apps.

Marius concluded his talk with a plea for feedback. There are plenty of people using the plugin, but more feedback is required to ensure its the best it can be, and that all bugs  (if any) are squashed. Checkout the code at: http://github.com/davidpersson/media
Latest articles

Migrate CsrfComponent to CsrfProtectionMiddleware

The CsrfComponent was deprecated since CakePHP version 3.5.0. On CakePHP 4, we now have a new middleware to help us protect applications against Cross Site Request Forgery attacks. In this article, we are going to show the different ways to enable and disable Cross Site Request Forgery between the controller and the new middleware.  

Enable CSRF

Do these changes:
  • In your Application::middleware add $middlewareQueue->add(new CsrfProtectionMiddleware());
  • Remove $this->loadComponent('Csrf') from your controllers.
The configuration keys from CsrfComponent cookieName, expiry, secure and field are also available in the middleware. If you used any of these, you should be able to continue using the middleware.  

Disable CSRF

Is not recommended to disable CSRF, but sometimes you really need to. With the component you could have something like this in your controller:   Now with the middleware, we can use the method skipCheckCallback to disable Csrf based on a custom logic:     That’s it, we have migrated CSRF protection from CsrfComponent to CsrfProtectionMiddleware.  

CakePHP Upgrade to 4 - Piece by Piece

Let's imagine you have a huge application in CakePHP 2.x (or 1.x) and you're planning to upgrade to the latest CakePHP 4.x. After doing some estimations, you realize the upgrade process is out of your scope, because you don't have the budget or developer availability to do it in 1 shot. At this point, some companies would abort the upgrade and keep working on 2.x for "some more time" until "this last release is delivered" or until "budget is available next fall", digging deeper and deeper into the rabbit hole…   There's an alternative you could follow if this is your case: proceed with the upgrade of a smaller portion of your application and let the 2 versions coexist for some time.   Warning: This is NOT for every project or company. Please carefully think about this decision as it has overhead you'll need to handle.   So, if your application has a portion that could be extracted, with a small set of dependencies from other areas of your application, or if you are creating a new feature with a limited set of dependencies with the rest of your application, this approach would be good for you.   In order to allow both applications to coexist, we are going to keep the CakePHP 1.x application as the main one, and use CakePHP 4.x as a subfolder inside of the first one. It's important to note that in order to share sessions between both applications you'll need to use a storage you can actually share, like database or cache based sessions (redis, etc). Then, you can use a configuration like this one (see below) to add a new upstream to handle your new application. Note: the upstream could be located in another server of your network, using a different PHP version etc.   We've used nginx as an example, but you can use the same approach in other web servers like Apache.   In our example we're going to use all paths starting with /api  to be managed by our new CakePHP 4.x application. upstream cake4 {      # Note this could be any server/port in your network where the cake4 application is installed          server 127.0.0.1:9090; }   # This is our CakePHP 2.x server server {     server_name example.com;       root   /var/virtual/example.com/app/webroot;     index index.php;       # All requests /api are forwarded to our CakePHP 4.x application location /api {         proxy_pass http://cake4;             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header Host $host;             proxy_http_version 1.1;         proxy_set_header Upgrade $http_upgrade;             proxy_set_header Connection "Upgrade";     }       location / {             try_files $uri $uri/ /index.php?$args;     }       location ~ \.php$ {           try_files $uri =404;           include fastcgi_params;                fastcgi_pass unix:/run/php/php7.4-fpm.sock;           fastcgi_index index.php;             fastcgi_intercept_errors on;         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;     } }   # This is our CakePHP 4.x server server {     listen 9090;     server_name example.com;       root   /var/virtual/cake4-example.com/webroot;     index index.php;       location / {         try_files $uri $uri/ /index.php?$args;     }       location ~ \.php$ {         try_files $uri =404;             include fastcgi_params;         fastcgi_pass unix:/run/php/php7.4-fpm.sock;             fastcgi_index index.php;         fastcgi_intercept_errors on;             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;     } }   As you can see, we have 3 blocks defined in our configuration file:

  • upstream cake4 {...} to forward requests to the CakePHP 4.x application
  • server {... 2.x ...} using a location /api to forward all these calls to the CakePHP 4.x server
  • server {... 4.x ...} using a specific port (9090) to handle requests in CakePHP 4.x
  Using this approach, you can break your application into 2 parts, and start moving features by path to CakePHP 4. You'll need to handle the changes in 2 projects for a while, and pay this overhead,  but this could be better to maintain than a CakePHP 2.x application sitting on an old PHP version. Happy baking!  

Dependency Injection with CakePHP

Dependency Injection is some of the bigger buzzwords in PHP frameworks.  Historically, CakePHP application logic didn’t support that, until the version 4.2 was released last December. You can do that on your own and have a few plugins for that. This is a new chapter of the framework, let's see how to bake it.  

Use Case

First, let’s talk about a classic Use case on real applications. Our application will include an address form, such as the shipping address for an online order, or provide information about User, Company, etc. Autocomplete can help users supply the details.   We will use the Geocoding API from Google Maps Platform, making a HTTP request for API with json output format and address parameter: https://maps.googleapis.com/maps/api/geocode/json?address=89104&key=****** And here we go, we will get this result:  

Baking a Address Service

After seeing the Use case, all we need on our backend is to make a HTTP request for API and return the JSON result for the frontend to populate related fields.   1. First, let’s exposing our application for accept “.json” requests:   2. Now, we can bake a Address Controller and let’s request an empty result: $ ./bin/cake bake controller Address --actions index   Now our app requests /address.json will return an empty JSON.   3. Let’s bake (manually) the Address Service:   Basically I’m using Cake\Http\Client to make the API request. Also I read Geocode.key from Cake\Core\Configure, we don't want to expose our key on public requests (add the key on config/boostrap.php).   4. Let’s rewrite our Controller:   5. Finally, let’s add our Service on Application.php:   That’s all bakers! Now our endpoint /address.json will support query parameters and return the result of the API request.  

The cost of shiny

I’m here selling an idea and I don't start with the cons. Unfortunately, the Dependency Injection container is an experimental feature that is not API stable yet.  The support is a bit limited, CakePHP will inject services into: constructors of Controllers and Commands and Controller actions. The core team hopefully stabilizes the feature on version 4.3, or at most 4.4. They need your help testing and finding cases, and feedback always is welcome.   I hope this post can be useful for you and your projects.  See you next time!  

We Bake with CakePHP