<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Roy Duineveld</title>
	<atom:link href="https://royduineveld.nl/feed/" rel="self" type="application/rss+xml" />
	<link>https://royduineveld.nl</link>
	<description>Waarom moeilijk doen als het makkelijk kan?</description>
	<lastBuildDate>Tue, 27 Jan 2026 07:48:00 +0000</lastBuildDate>
	<language>nl</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.9.26</generator>
	<item>
		<title>[EN] My hacking history</title>
		<link>https://royduineveld.nl/my-hacking-history/</link>
		<comments>https://royduineveld.nl/my-hacking-history/#comments</comments>
		<pubDate>Sun, 08 Oct 2017 16:55:47 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Ideeën & Concepten]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=1075</guid>
		<description><![CDATA[<p>I started with programming when I was 12 years old. My uncle gave me some Visual Basic 3 books and a CD with Visual Basic 6.0. My English wasn&#8217;t that...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/my-hacking-history/">[EN] My hacking history</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>I started with programming when I was 12 years old. My uncle gave me some Visual Basic 3 books and a CD with Visual Basic 6.0. My English wasn&#8217;t that good back then but with the examples in the books I managed to create my first program. If I remember good it was a program to create alert boxes, choose an icon and text, press the button and there was the alert! You&#8217;ve to start somewhere&#8230; after that I&#8217;ve created a lot of useless programs with progress bars, reinvented the wheel by creating my own notepad application etc. </p>
<p>I was pretty fascinated by viruses because the computer of my dad was infected with the Sasser worm which caused the computer to reboot continuously. After the virus was removed I thought; let&#8217;s create a virus! So I&#8217;ve created a bunch of funny viruses but didn&#8217;t spread it outside my home.</p>
<h2>High school</h2>
<p>At my high school all the computers where kind of locked down. We didn&#8217;t have a start menu back then in Windows 2000 with some program every student only had a folder with programs we where allowed to use.</p>
<h3>Getting in</h3>
<p>I can create a virus so I can crack a school computer right? After a quick Google search I found a program which creates a bootable floppy with a administrator reset program. Next day at school I&#8217;ve booted from the floppy, removed the administrator password, restarted and yeah! I&#8217;m in! Locally but still&#8230; finally a normal desktop and I can install whatever I want. Guess what&#8230; The next day in the lunch break I was playing Counter Strike 1.6 with my buddies on school computers! After playing it every lunch break in a week the network administrator at our school noticed it. Instead of shutting it down he joined us from his office!</p>
<h3>Going further</h3>
<p>The lockers at my high school where automated with a chip. Every student with a locker does have a chip to open it. What if I could open all the lockers? I&#8217;ve tried hard but back then I&#8217;d no idea how those chips where working so let&#8217;s try social engineering. &#8220;I lost my chip&#8221;, so let&#8217;s go to the reception and ask if they can open my locker. I&#8217;d to say my name and class, they verified it with my school picture and with 1 click on the screen my locker was open. Alright&#8230; if they can do it, why I can&#8217;t do it? I&#8217;ve watched the receptionist a couple of days and written down the times there was nobody. In that case the glass screen was closed but not locked, there was a usb hub on the desk so let&#8217;s fix a USB drive with a remote desktop program on it. But then I&#8217;ve to install it, make sure it start on boot etc. What other options are there? A <code>autorun.inf</code> works on CD&#8217;s, why not on a USB drive? Hell yeah! That&#8217;s working! Windows 2000 was a great place for viruses. You can imagine how that story ends; the next day a lot of lockers where open when I arrived early at school. I controlled the computer at the reception from my computer at home. Awesome!</p>
<h3>Let&#8217;s have some more fun</h3>
<p>Do you remember <code>net send</code>? In Windows 2000 it was enabled by default! <code>net send * Hello school!</code>, after that every computer in the whole school received that message. Pretty funny until I noticed the hostname where the message was send from was in that message. Within 5 minutes the network administrator stood next to me. I&#8217;d some fun sending messages to individual computers to irritate my class mates until the network administrator finally found the option to disable it.</p>
<p>What other things can we do? Change the boot screen of Windows? I created another bootable floppy which replaces the <code>ntoskrnl.exe</code> with a modified one and then reboots the computer. So the next morning when all the computers which contains the modified file in a classroom where booting; my awesome boot screen was visible!</p>
<h3>Never changed your test results?</h3>
<p>Yes, even that I&#8217;ve done at the beginnen with an old test results system at school. Later they moved to a new more advanced system which I did not manage to get into. The old system was running on a server at school which was available through a network shared folder. I just had to search through the school network for open network shares so it wasn&#8217;t that hard.</p>
<h2>My hacking website</h2>
<p>Meanwhile I&#8217;d created several &#8220;hacking tools&#8221;, like a trojan horse, a mail bomber, etc. but I&#8217;ve created it just for fun, I&#8217;m not using them. That&#8217;s why I&#8217;ve created a website back then to sell my programs. I gave them some fancy names, played around in Photoshop to make some cool banners for them and done. It was possible to buy the programs by calling to a payed number (for some programs you&#8217;d to call multiple times) which costs €1,30 per call. I think I was getting about €0,80 from it per call but it was better then bringing the newspapers around.</p>
<p>On my website I&#8217;d for example a program called &#8220;MSN Terror&#8221; it was a program where you can fill in someones MSN address and when you start it the program will kind of brute force the login. Because Microsoft had a brute force protection after a several tries the account was blocked for some minutes. So when you keep doing that you&#8217;re unable to login with that MSN account.</p>
<h3>Hello competitors</h3>
<p>Back then I was not the only one who was creating and selling &#8220;hacks&#8221;, a guy who named himself MR-X with his MR-X Shop was doing the same. There was also a website Frukky which was selling hacks he found on the internet. The problem was; Frukky bought hacks at my and MR-X his website and was selling it for less on his own website. So a kind of &#8220;war&#8221; started. What MR-X did was implementing a backdoor in his new hacks so everyone who bought it and used it was infected but it was just for Frukky. I&#8217;ve no idea if he used it and I&#8217;ve still no idea who those guys where. When someone knows who these guys are, I&#8217;d like to get in touch with them to chat about those days. If you search on the internet some hacks are still available, but they don&#8217;t have their own website anymore. If you come across hacks of mine, please let me know. I always used my real name.</p>
<h2>University</h2>
<p>When I was done with my high school, which I didn&#8217;t finish by the way. I&#8217;d a company to run, when I was 13 I started a webhosting company and I thought there was nothing more useful to learn at my high school at the age of 15. After some visits at the attendance officer I was allowed to go to the university. In Dutch: &#8220;MBO niveau 4&#8221; where I started the ICT manager education. Within a few weeks there I arranged exemption for almost all IT related classes because I could prove I already knew it. At some classes we&#8217;d to open a computer and tell the teacher where the components are located, really? Also we&#8217;d some programming classes where we&#8217;d to build a website with HTML tables&#8230; come on, you can imagine the education level.</p>
<h3>Let&#8217;s have some fun!</h3>
<p>In the past I&#8217;ve created my own trojan horse (still in VB6) which I spread around the school by infecting USB drives (still with the <code>autorun.inf</code> trick) which all students where using to store there documents. They where reporting to my command and control server so I&#8217;ve created a grid of all the computers with the IP addresses. That way I can target individual computers to irritate class mates. I&#8217;d no intention to do any damage to all infected computers. It started with shutting down some computers of people I didn&#8217;t like that much. MSN was pretty popular those days so with the trojan I was able to change they&#8217;re status but I&#8217;d the most fun with opening cd drives. One day a teacher was talking to a student and his arm was hanging on a computer. I looked up the IP address, connected to the computer and opened the drive where the hand of the teacher was hanging. You&#8217;d to see his face, it was hilarious.</p>
<h3>The end of school</h3>
<p>About 3 months after starting the education I quit and started full time working. I was not allowed by the attendance officer to work full time from home for my own business. So I started working at a local computer store and ran my own business in the weekends. I did get an offer to finish the education within 1.5 year instead of 4 and skip the first year of the continuing education (in Dutch: &#8220;HBO&#8221;) but I declined it. So I&#8217;ve only finished my elementary school.</p>
<h2>My biggest hacks</h2>
<h3>GHED</h3>
<p>When I started with web development I needed webhosting to put my website online. There are some companies which offer free webhosting but most of them inject ads. So after searching around I came across <a href="http://www.gratishostingendomein.info/" rel="noopener" target="_blank">GHED</a>: &#8220;Gratis hosting en domein&#8221;, in English: &#8220;Free hosting and domain&#8221;. At the time of writing they still exists but I just read they stop in 2018. You can earn points by clicking on ads, filling out surveys, etc with affiliate marketing. You can trade those points for webhosting or a domain. </p>
<p>You&#8217;ll get webhosting at a big hosting company but years ago they had their own dedicated server with all the free hosting accounts on it. That server wasn&#8217;t protected that well so I used a c99 shell script to look around on the server which was running on the Direct Admin control panel. I came across a unprotected phpMyAdmin folder, I modified the login script to send me an email when someone uses it and I was waiting. Apparently that one wasn&#8217;t used by anyone so let&#8217;s do some social engineering: I&#8217;ve created a thread on their forum with a link to the phpMyAdmin installation with the question if that&#8217;s the right one to use. Guess what&#8230; a administrator logged in with the admin credentials, removed the folder from the server and replied with the correct link.</p>
<p>So I received an email with the username and password, logged in with a proxy to the Direct Admin control panel and there we are! I could do anything I want on that server with hundreds of websites hosted on it. </p>
<h3>Recent hacks?</h3>
<p>I&#8217;m a ethical hacker now so I report things I find and don&#8217;t abuse it. After shutting down my hacking website I focused on web development which I&#8217;m still doing. I&#8217;m not doing that much with hacking anymore but recently I published these related blog articles:</p>
<p>&#8211; <a href="https://royduineveld.nl/hacking-public-git-repositories/" rel="noopener" target="_blank">Hacking public GIT repositories</a><br />
&#8211; <a href="https://royduineveld.nl/magento-cacheleak-exploit/" rel="noopener" target="_blank">Magento 1 cacheleak exploit</a></p>
<p>And did I only create hacks in VB6? No&#8230; I also created invoice programs, a radio station program, <a href="https://royduineveld.nl/cd-dvd-magic/" rel="noopener" target="_blank">CD / DVD Magic</a> and much more.</p>
<h2>Wrapping up</h2>
<p>Some things I regret, other things where so much fun but keep in mind: hacking and abusing it is not allowed by the law! A lot of things I did at school can get you suspended or can bring you in serious trouble! It sounds like a fun story but I&#8217;ve been suspended at my high school for a week and one time the police was contacted for stuff I&#8217;ve done. It&#8217;s getting a little bit long and I haven&#8217;t told everything I wanted yet, maybe I&#8217;ll update this post later with more stories.</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/my-hacking-history/">[EN] My hacking history</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/my-hacking-history/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[EN] Laravel 5.5 validated() method on Form Requests</title>
		<link>https://royduineveld.nl/laravel-5-5-validated-method-on-form-requests/</link>
		<comments>https://royduineveld.nl/laravel-5-5-validated-method-on-form-requests/#respond</comments>
		<pubDate>Sat, 07 Oct 2017 14:36:05 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=1065</guid>
		<description><![CDATA[<p>A while ago when Laravel 5.5 wasn&#8217;t released yet Jeffrey Way started a &#8220;What&#8217;s New in Laravel 5.5&#8221; serie on Laracasts. In the second lesson Streamlined Request Validation Jeffrey introduced...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/laravel-5-5-validated-method-on-form-requests/">[EN] Laravel 5.5 validated() method on Form Requests</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>A while ago when Laravel 5.5 wasn&#8217;t released yet Jeffrey Way started a &#8220;<a href="https://laracasts.com/series/whats-new-in-laravel-5-5" target="_blank" rel="noopener">What&#8217;s New in Laravel 5.5</a>&#8221; serie on <a href="https://laracasts.com/" target="_blank" rel="noopener">Laracasts</a>. In the second lesson <a href="https://laracasts.com/series/whats-new-in-laravel-5-5/episodes/2" target="_blank" rel="noopener">Streamlined Request Validation</a> Jeffrey introduced us to the validated data array when validating a request:</p>
<pre class="brush: plain; title: ; notranslate">
public function store()
{
    $validatedData = request()-&gt;validate([
        'title' =&gt; 'required',
        'body' =&gt; 'required',
    ]);
}
</pre>
<p>Normally you would do something like <code>request()->all()</code> which isn&#8217;t always save if you&#8217;re not setup the mass assignment variables on the model right. For example when you&#8217;ve disabled mass assignment by adding <code>protected $guarded = [];</code> to a model. A better way is to use <code>request()->only()</code> with the fields you need. With the above example that would be: <code>request()->only(['title', 'body'])</code> but that&#8217;s feels like duplication right? You&#8217;ve already defined the fields in the validation array. From Laravel 5.5 <code>$validatedData</code> will output an array with the validated fields, the same as <code>request()->only()</code> would do.</p>
<h2>What about Form Requests?</h2>
<p>The first thing I was thinking when I&#8217;ve watched that lesson; is there something similar when using <a href="https://laravel.com/docs/5.5/validation#form-request-validation" rel="noopener" target="_blank">Form Requests</a>? I&#8217;m not a big fan of validating data in my controller. A comment under the lesson brought me here: <a href="https://github.com/sebastiaanluca/laravel-validator" rel="noopener" target="_blank">laravel-validator</a> which introduced that functionality to Laravel 5.4 with:</p>
<pre class="brush: plain; title: ; notranslate">
public function store(FormRequest $request)
{
    Page::create($request-&gt;valid());
}
</pre>
<p>But Laravel 5.5 introduced the <code>validated()</code> <a href="https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Http/FormRequest.php#L168:L180" rel="noopener" target="_blank">method</a> (currently still undocumented) so that package isn&#8217;t needed anymore and <a href="https://github.com/sebastiaanluca/laravel-validator/issues/1#issuecomment-326118084" rel="noopener" target="_blank">confirmed by the author</a>. I did find a <a href="https://github.com/laravel/framework/issues/21186" rel="noopener" target="_blank">bug</a> with it but was fixed already in Laravel 5.5.4.</p>
<h2>What if not all the data should be saved to the model?</h2>
<p>In a project I&#8217;m currently working on I&#8217;m using <a href="https://github.com/spatie/laravel-medialibrary" rel="noopener" target="_blank">Spatie&#8217;s Medialibrary package</a> so I&#8217;ve rules to validate images and lately I&#8217;m using <code>protected $guarded = [];</code> on all my models to disable the mass assignment checks. Let&#8217;s say we&#8217;ve a page with a <code>title</code>, <code>body</code> and <code>image</code>. In our <code>rules()</code> method on our <code>PageRequest</code> we&#8217;ve these rules:</p>
<pre class="brush: plain; title: ; notranslate">
return [
    'title' =&gt; 'required',
    'body'  =&gt; 'required',
    'image' =&gt; 'image'
];
</pre>
<p>When we save the page with <code>Page::create($request->validated());</code> we&#8217;ll get a database error because there is no <code>image</code> column. Spatie&#8217;s Medialibrary saves all the media in a seperated table so we don&#8217;t have a <code>image</code> column on our <code>pages</code> table. After we&#8217;ve saved the page we&#8217;ll process the image.</p>
<p>We&#8217;ve a few option to fix this:</p>
<ol>
<li>Instead of using <code>protected $guarded = [];</code> we could specify the fillable ones with <code>protected $fillable = ['title', 'body'];</code>.</li>
<li>We could use <code>Page::create($request->only('title', 'body'));</code> here instead of the <code>validated()</code> function.</li>
<li>We exclude the image: <code>Page::create(collect($request->validated())->except(['image'])->toArray());</code></li>
</ol>
<p>I&#8217;m using option 3, it may look like the longest / most complex option but if you&#8217;re getting more and more fields you&#8217;ll see it&#8217;s the easiest because it&#8217;s just a blacklist of fields you don&#8217;t want. You could refactor this back to your model so the blacklist is defined there.</p>
<blockquote><p>What option do you prefer?</p></blockquote>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/laravel-5-5-validated-method-on-form-requests/">[EN] Laravel 5.5 validated() method on Form Requests</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/laravel-5-5-validated-method-on-form-requests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[EN] Laravel theme fallback views based on the user</title>
		<link>https://royduineveld.nl/laravel-theme-fallback-views-based-on-the-user/</link>
		<comments>https://royduineveld.nl/laravel-theme-fallback-views-based-on-the-user/#comments</comments>
		<pubDate>Thu, 21 Sep 2017 14:52:05 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=1050</guid>
		<description><![CDATA[<p>In almost any CMS or e-commerce system there is any kind of theme system. For example in WordPress and Magento it&#8217;s possible to create a theme. In some cases you...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/laravel-theme-fallback-views-based-on-the-user/">[EN] Laravel theme fallback views based on the user</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>In almost any CMS or e-commerce system there is any kind of theme system. For example in WordPress and Magento it&#8217;s possible to create a theme. In some cases you want to use a default or existing theme and change some parts in a &#8220;child theme&#8221;. With Laravel there are some great packages to achieve this, but what if the loaded theme should be based on the current authenticated user?</p>
<p>While figuring out how I could accomplish this I came across <a href="https://sebastiandedeyne.com/posts/2017/theme-based-views-in-laravel-using-vendor-namespaces" target="_blank" rel="noopener">this great article</a> from <a href="https://sebastiandedeyne.com/about" target="_blank" rel="noopener">Sebastian de Deyne</a> from <a href="https://spatie.be" target="_blank" rel="noopener">Spatie</a>. He registers a view namespace in a service provider with <code>$this-&gt;loadViewsFrom()</code> which allows you to specify the theme directories and a namespace. But from a service provider I don&#8217;t have access to the current authenticated user because that part is loaded later, see <a href="https://laravel.com/docs/5.5/lifecycle" target="_blank" rel="noopener">Laravel&#8217;s request lifecycle</a>. When I looked at the <code>loadViewsFrom()</code> function I noticed a <code>addNamespace()</code> function on the views which can be used from (for example) the view facade: <code>View::addNamespace()</code>.</p>
<p>So what&#8217;s the best place to register the view namespace? I&#8217;d like to base it on the current user so I need to make sure the user is logged in. What about a middleware? Let&#8217;s create the middleware:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;

class UseThemeForUser
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        View::addNamespace('theme', [
            resource_path('views/themes/' . Auth::user()-&gt;theme),
            resource_path('views/themes/default'),
        ]);

        return $next($request);
    }
}
</pre>
<p>But this should only be executed for the frontend when a user is logged in. Let&#8217;s first register the middleware as a route middleware in <code>app/Http/Kernel.php</code> as <code>theme</code>. Now we can use this middleware in our routes file (for example with a route group) or controller. In my case I&#8217;ve created a dedicated frontend routes file and registered it in my route service provider together with the <code>web</code> and <code>auth</code> middleware because our <code>theme</code> middleware depend on those.</p>
<p>In all our controller and views which depend on a theme we can use for example <code>theme::home</code> to load our <code>/views/themes/custom/home.blade.php</code> file if the users theme is set to <code>custom</code>, or if that one doesn&#8217;t exists it will fallback to the <code>/views/themes/default/home.blade.php</code> template.</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/laravel-theme-fallback-views-based-on-the-user/">[EN] Laravel theme fallback views based on the user</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/laravel-theme-fallback-views-based-on-the-user/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>[EN] From shared webhosting to your own VPS</title>
		<link>https://royduineveld.nl/from-shared-webhosting-to-your-own-vps/</link>
		<comments>https://royduineveld.nl/from-shared-webhosting-to-your-own-vps/#respond</comments>
		<pubDate>Sat, 28 Jan 2017 16:43:17 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Serverpilot]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=1012</guid>
		<description><![CDATA[<p>My websites where hosted on a shared webhosting environment for years. As mentioned in earlier blog posts I migrated everything last year to my own VPS at Digital Ocean. The reason...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/from-shared-webhosting-to-your-own-vps/">[EN] From shared webhosting to your own VPS</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>My websites where hosted on a shared webhosting environment for years. As mentioned in earlier blog posts I migrated everything last year to my own VPS at <a href="https://m.do.co/c/7c917c6ea074" target="_blank">Digital Ocean</a>. The reason for this is more flexibility and better performance. But what steps did I take and how I set it up?</p>
<h2>Digital Ocean and Serverpilot</h2>
<p>I&#8217;ve created a droplet at <a href="https://m.do.co/c/7c917c6ea074" target="_blank">Digital Ocean</a> for $10 a month with 1GB memory, 1 core and a 30GB SSD disk. I can recommend to name your server with a hostname, so instead of &#8220;royduineveld&#8221;, name it &#8220;royduineveld.nl&#8221;, it&#8217;s important to do so for a <a href="https://serverpilot.io/community/articles/how-to-set-ptr-records-and-reverse-dns.html" target="_blank">PTR record</a>. I&#8217;d like to use <a href="https://www.serverpilot.io/?refcode=adcc6f19541d" target="_blank">Serverpilot</a> so I&#8217;ve created a account and followed the steps. A alternative for Serverpilot is Laravel Forge, I&#8217;ve created a <a href="https://royduineveld.nl/laravel-forge-vs-serverpilot/">comparison</a> between them.</p>
<h2>Server setup</h2>
<p>A lot of things are configured for you by Serverpilot but some things you&#8217;ll have to do yourself, just read through <a href="https://serverpilot.io/community/" target="_blank">the docs</a> and see what&#8217;s handy for you. Things I&#8217;ve done:</p>
<h3>General setup</h3>
<ul>
<li><a href="https://serverpilot.io/community/articles/how-to-change-the-timezone-of-your-server.html" target="_blank">Change the timezone</a>, it&#8217;s important to restart your server after changing it so other services like MySQL and crontab adapt it: <code>sudo dpkg-reconfigure tzdata</code> and <code>sudo reboot</code></li>
</ul>
<h3>Security steps</h3>
<ul>
<li><a href="https://serverpilot.io/community/articles/how-to-use-ssh-public-key-authentication.html" target="_blank">Setup SSH public key authentication</a>, it&#8217;s more secure and easier then using passwords: <code>echo "MY SSH KEY" &gt;&gt; /srv/users/serverpilot/.ssh/authorized_keys</code></li>
<li><a href="https://serverpilot.io/community/articles/how-to-disable-ssh-password-authentication.html" target="_blank">Disable SSH password authentication</a>, you can always access your server from the console at Digital Ocean. Edit <code>/etc/ssh/sshd_config</code> and set <code>PasswordAuthentication no</code> and restart the service: <code>sudo service ssh restart</code></li>
</ul>
<h3>Monitoring</h3>
<p>I like <a href="https://newrelic.com" target="_blank">New Relic</a> for monitoring, I&#8217;ve setup APM and Browser to monitor my PHP applications, Synthetics to get notified when something goes down and Server to keep track on statistics.</p>
<ul>
<li>Install New Relic Server (replace the license key):
<pre class="brush: plain; title: ; notranslate">
echo deb http://apt.newrelic.com/debian/ newrelic non-free &gt;&gt; /etc/apt/sources.list.d/newrelic.list
wget -O- https://download.newrelic.com/548C16BF.gpg | apt-key add -
apt-get update
apt-get install -y newrelic-sysmond
nrsysmond-config --set license_key=&quot;YOUR LICENSE KEY&quot;
/etc/init.d/newrelic-sysmond start
</pre>
</li>
<li><a href="https://serverpilot.io/community/articles/install-newrelic.html" target="_blank">Install New Relic APM</a> for PHP 7.0 (select option 2 in the installer, provide your license key when asked and replace your server name)
<pre class="brush: plain; title: ; notranslate">
wget -O - https://download.newrelic.com/548C16BF.gpg | sudo apt-key add -
sudo bash -c &quot;echo deb http://apt.newrelic.com/debian/ newrelic non-free &gt; /etc/apt/sources.list.d/newrelic.list&quot;
sudo apt-get update
sudo apt-get install -y newrelic-php5
sudo NR_INSTALL_PATH=/opt/sp/php7.0/bin newrelic-install
sed -i &quot;/^newrelic.appname =/s/=.*/= \&quot;YOURSERVERNAME\&quot;/&quot; /etc/php7.0-sp/conf.d/newrelic.ini
sudo service php7.0-fpm-sp restart
</pre>
<p>Afterwards create a <code>.user.ini</code> in your app directories: <code>~/apps/APPNAME/public/.user.ini</code> with the application name in it: <code>newrelic.appname = "APPNAME"</code></li>
</ul>
<h3>Application specific</h3>
<p>Some applications of mine are using <a href="https://redis.io/" target="_blank">Redis</a> as cache backend, I&#8217;m also using the <a href="http://gulpjs.com/" target="_blank">Gulp</a> build tool in some projects which requires <a href="https://nodejs.org/" target="_blank">Node.js</a> and for Magento 1 projects <a href="https://github.com/netz98/n98-magerun" target="_blank">Magerun</a> is a very handy tool.</p>
<h4><a href="https://serverpilot.io/community/articles/how-to-install-redis.html" target="_blank">Redis Server</a> with <a href="https://serverpilot.io/community/articles/how-to-install-the-php-redis-extension.html" target="_blank">Redis for PHP 7.0</a></h4>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get install -y redis-server
sudo apt-get install -y gcc make autoconf libc-dev pkg-config
sudo pecl7.0-sp install redis
sudo bash -c &quot;echo extension=redis.so &gt; /etc/php7.0-sp/conf.d/redis.ini&quot;
sudo service php7.0-fpm-sp restart
</pre>
<h4>Node.js with NPM</h4>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get update
sudo apt-get install -y nodejs
sudo apt-get install -y npm
sudo apt-get install -y nodejs-legacy
chown serverpilot:serverpilot /srv/users/serverpilot/tmp/
</pre>
<p>I&#8217;m using Node.js to run Gulp on my server, on your local machine you probably installed Gulp globally, don&#8217;t do that on your server! After running <code>npm install</code> you can run Gulp with <code>node_modules/.bin/gulp</code> or for example with <a href="https://github.com/laravel/elixir" target="_blank">Laravel Elixir</a> you can run <code>npm run prod</code></p>
<h4>Magerun</h4>
<pre class="brush: plain; title: ; notranslate">
wget https://files.magerun.net/n98-magerun.phar -O /usr/local/bin/magerun
chmod +x /usr/local/bin/magerun
chown serverpilot:serverpilot /usr/local/bin/magerun
</pre>
<h3>Backups</h3>
<p>I&#8217;ve written another article about server backups and how I&#8217;ve set it up, see: <a href="https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/">Backup your server with AutoMySQLBackup and Duplicity</a>.</p>
<h3>Email</h3>
<p>For email I&#8217;ve also written a dedicated article: <a href="https://royduineveld.nl/free-email-forwarding/">Free email forwarding</a>.</p>
<h2>Conclusion</h2>
<p>Moving from shared hosting to a VPS gives you flexibility, you can do whatever you want with your server. But it requires some server/linux knowledge, when you&#8217;re using Serverpilot a lot is taken care of, including updates! But when something goes wrong you need to fix it yourself.</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/from-shared-webhosting-to-your-own-vps/">[EN] From shared webhosting to your own VPS</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/from-shared-webhosting-to-your-own-vps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[EN] Magento 1 cacheleak exploit</title>
		<link>https://royduineveld.nl/magento-cacheleak-exploit/</link>
		<comments>https://royduineveld.nl/magento-cacheleak-exploit/#respond</comments>
		<pubDate>Mon, 09 Jan 2017 17:06:45 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=998</guid>
		<description><![CDATA[<p>In the last two years Magento did release a lot of patches for (security) issues with Magento 1. If you apply those patches shortly after the release you&#8217;re pretty save. But...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/magento-cacheleak-exploit/">[EN] Magento 1 cacheleak exploit</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>In the last two years Magento did release a lot of patches for (security) issues with Magento 1. If you apply those patches shortly after the release you&#8217;re pretty save. But your server configuration is important too! For example when you switch the Apache webserver for Nginx, <code>.htaccess</code> files don&#8217;t work anymore so you&#8217;ve to make sure you&#8217;ve configured it safely.</p>
<p>You can find <code>.htaccess</code> files across multiple folders with a default Magento 1 installation, not only in the root. For example the <code>.htaccess</code> file in the <code>/var</code> directory blocks access to all files in there. Don&#8217;t forget to block access to that directory in your Nginx configuration too! If you don&#8217;t block access people can access log files and even cached files. You may think that&#8217;s not dangerous but it is, with this exploit you can get the MySQL database credentials.</p>
<ol>
<li>First, check if a website is vulnerable at <a href="https://magereport.com" target="_blank">magereport.com</a></li>
<li>Try to access the <code>resource_config.json</code> file by visiting <code>http://website.com/var/resource_config.json</code></li>
<li>Copy and modify the media directory path from the <code>resource_config.json</code> file to something like: <code>/home/users/username/website.com/app/etc</code> (make sure it points to <code>/app/etc</code>)</li>
<li>Create a MD5 hash from that path, for example with <a href="http://www.md5.cz/" target="_blank">http://www.md5.cz/</a> or generate it from the terminal: <code>php -r "echo md5('PATH');"</code> en replace the path, that will generate something like: 68095313d2b99db25e7ebcd5bc8d9642</li>
<li>Use the first 3 characters from that hash, with my example that will be 680</li>
<li>Visit <code>http://website.com/var/cache/mage--2/mage---XXX_CONFIG_GLOBAL</code> and replace the XXX with those 3 characters</li>
<li>Now you can search in the global configuration, you can find among other things the MySQL credentials here</li>
<li>Try to connect to the database remotely or search for a PhpMyAdmin or Adminer script</li>
</ol>
<p>From that point the possibilities are endless as I&#8217;ve written before in my <a href="https://royduineveld.nl/hacking-public-git-repositories/">Hacking public GIT repositories</a> post, from defacing to change payment provider credentials and further.</p>
<p>Why now publish this exploit? I&#8217;ve &#8220;created&#8221; it in 2015 but back then a lot of webshops where vulnerable. Meanwhile the percentage is pretty low so I thought; let&#8217;s share it with the world.</p>
<blockquote><p>Do you need a security audit for your webshop? <a href="https://royduineveld.nl/contact/">Contact me</a>!</p></blockquote>
<p>&nbsp;</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/magento-cacheleak-exploit/">[EN] Magento 1 cacheleak exploit</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/magento-cacheleak-exploit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[EN] Backup your server with AutoMySQLBackup and Duplicity</title>
		<link>https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/</link>
		<comments>https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/#comments</comments>
		<pubDate>Thu, 15 Dec 2016 08:38:31 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Serverpilot]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=950</guid>
		<description><![CDATA[<p>You&#8217;ve setup your server (for example with Serverpilot or Laravel Forge on Digital Ocean), but have you thought about backups? If you&#8217;re using MySQL it would be nice to have daily backups...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/">[EN] Backup your server with AutoMySQLBackup and Duplicity</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>You&#8217;ve setup your server (for example with <a href="https://royduineveld.nl/laravel-forge-vs-serverpilot/">Serverpilot or Laravel Forge</a> on <a href="https://m.do.co/c/7c917c6ea074" target="_blank" rel="noopener">Digital Ocean</a>), but have you thought about backups? If you&#8217;re using MySQL it would be nice to have daily backups right? And what if your server crashes? Maybe an off-site backup of everything? If you&#8217;re thinking &#8220;but I&#8217;ve checked the backup option with my droplet on <a href="https://m.do.co/c/7c917c6ea074" target="_blank" rel="noopener">Digital Ocean</a>&#8221; right now, don&#8217;t rely on that! There are some horror stories about corrupt droplets and those backups are created weekly!</p>
<h2>AutoMySQLBackup</h2>
<p>This tool is pretty simple and can be installed with: <code>sudo apt-get install automysqlbackup</code> You&#8217;re done! Backups will be made daily, automatically of all your databases and will be stored in <code>/var/lib/automysqlbackup</code> If you&#8217;d like to manually run it: <code>sudo automysqlbackup</code></p>
<p>Restoring from one of those backups is as easy as extracting and importing the <code>.sql</code> file with: <code>mysql -u username -p databasename &lt; databasefile.sql</code>, provide your password when asked and your done.</p>
<h2>Duplicity</h2>
<p>You could copy a lot of directories from your server daily with SCP to another server, or first zip them but then you&#8217;ve to create multiple cronjobs to handle all of this. And what about old backups and thought about disk space on the backup server? Duplicity can take care of all of this. Don&#8217;t have a backup server? Duplicity supports a lot of storage platforms and transfer protocols like: Amazon S3, Backblaze, Dropbox, Google Drive, SSH/SCP, WebDav, etc. and it&#8217;s creating diff files instead of doing complete backups everyday to save diskspace. Let&#8217;s install it with: <code>sudo apt-get install duplicity</code></p>
<h3>Full backups</h3>
<p>Let&#8217;s start with a full backup. I&#8217;ve got a <a href="https://www.transip.nl/stack/" target="_blank" rel="noopener">TransIP STACK</a> account, it&#8217;s a Dutch hosting provider which gives 1TB of free storage which you can access with WebDav so I store my server backups there. Also my webserver is managed by Serverpilot so my directories I&#8217;d like to backup are:</p>
<ul>
<li><code>/srv</code>, all my websites, applications and logs are stored here</li>
<li><code>/var/lib/automysqlbackup</code>, all daily, weekly and monthly backups of all my MySQL databases</li>
</ul>
<p>I&#8217;m not going to use any encryption in this example so to create a full backup with Duplicity from the above directories to my TransIP STACK storage:</p>
<pre class="brush: plain; title: ; notranslate">
sudo duplicity full 
--ssl-no-check-certificate 
--no-encryption 
--include /srv 
--include /var/lib/automysqlbackup 
--exclude '**' 
/ 
webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups
</pre>
<p>Create a monthly cronjob (as <code>root</code> user else you can&#8217;t access the AutoMySQLBackup files) from it and you&#8217;re done!</p>
<h3>Incremental backups</h3>
<p>When you&#8217;ve created your first full backup, Duplicity can create incremental backups with the full backup as reference. This means: when a full backup is ready, Duplicity will compare everything with that backup and creates diff files from the changes. And yes Duplicity is smart enough to restore backups from it, you don&#8217;t have to run multiple diff files over the full backups or something. Setting up a incremental backup is as easy as replacing <code>full</code> with <code>incremental</code> in the example above. Create a daily cronjob, done!</p>
<h3>Removing old backups</h3>
<p>Now you&#8217;ve setup 2 cronjobs, but it keeps running and won&#8217;t delete anything. I&#8217;d like to keep 3 months of backups, this can be done with this command:</p>
<pre class="brush: plain; title: ; notranslate">
duplicity remove-all-but-n-full 3 
--ssl-no-check-certificate 
webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups
--force
</pre>
<h3>Restoring backups</h3>
<p>But what if something goes wrong and you&#8217;d like to restore a backup? You can use the <code>restore</code> command:</p>
<pre class="brush: plain; title: ; notranslate">
duplicity restore 
--ssl-no-check-certificate 
--no-encryption 
webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups .
</pre>
<p>Which will restore the latest backup to the directory your currently in. You can even restore one file with the <code>--file-to-restore</code> parameter or choose with <code>--time</code> from which backup you&#8217;d like to restore. If you want to know which backups you can restore, run the <code>collection-status</code> command.</p>
<h2>How I&#8217;ve setup my backups</h2>
<p>AutoMySQLBackup is installed and running and with <code>crontab -e</code> as my <code>root</code> user I&#8217;ve setup my Duplicity cronjobs:</p>
<pre class="brush: plain; title: ; notranslate">
0 1 * * * duplicity --full-if-older-than 1M --ssl-no-check-certificate --no-encryption --include /srv --include /var/lib/automysqlbackup --exclude '**' / webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups &gt;&gt; /dev/null
0 5 1 * * duplicity remove-all-but-n-full 3 --ssl-no-check-certificate webdavs://username:password@username.stackstorage.com/remote.php/webdav/backups --force &gt;&gt; /dev/null
</pre>
<p>A little bit different from what I&#8217;ve said earlier. By default the <code>duplicity</code> command creates a full backup and when there is one already it creates an incremental one. With <code>--full-if-older-than</code> we can specify when a new full backup should be made. This method is better than creating two cronjobs; one for a full backup the first of the month and the other one incremental the other days. What if the full backup fails? We&#8217;re getting two months of incremental backups, etc.</p>
<p>Do you want to know more? Run the <code>man duplicity</code> command for the manual or check the <a href="http://duplicity.nongnu.org/duplicity.1.html" target="_blank" rel="noopener">duplicity manual online</a>.</p>
<blockquote><p>And don&#8217;t forget: backup, backup, backup!!!</p></blockquote>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/">[EN] Backup your server with AutoMySQLBackup and Duplicity</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/backup-your-server-with-automysqlbackup-and-duplicity/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[EN] Free email forwarding</title>
		<link>https://royduineveld.nl/free-email-forwarding/</link>
		<comments>https://royduineveld.nl/free-email-forwarding/#comments</comments>
		<pubDate>Thu, 06 Oct 2016 14:52:13 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[Mailgun]]></category>
		<category><![CDATA[Serverpilot]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=925</guid>
		<description><![CDATA[<p>I recently moved my personal websites from a shared hosting provider to my own VPS at Digital Ocean (use that link, create a account and you&#8217;ll get $10 free credit!). I&#8217;m...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/free-email-forwarding/">[EN] Free email forwarding</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>I recently moved my personal websites from a shared hosting provider to my own VPS at <a href="https://m.do.co/c/7c917c6ea074" target="_blank">Digital Ocean</a> (use that link, create a account and you&#8217;ll get $10 free credit!). I&#8217;m doing this a while now for customers but never found the time to do it for my own websites. As <a href="https://royduineveld.nl/laravel-forge-vs-serverpilot/">mentioned earlier</a> I&#8217;m using <a href="https://www.serverpilot.io/?refcode=adcc6f19541d" target="_blank">Serverpilot</a> (use that link too, create a account and you&#8217;ll also get $10 free credit!) to configure and maintain my servers. But at the shared hosting provider, everything was taken care of, including email. At my own server I&#8217;ve to do it myself (but gives me way more flexibility and options). <a href="https://www.serverpilot.io/?refcode=adcc6f19541d" target="_blank">Serverpilot</a> installs <a href="http://www.postfix.org/" target="_blank">Postfix</a> to send email but doesn&#8217;t configure anything else like a SPF and DKIM records so it&#8217;s possible that emails are marked as spam. But what about receiving emails?</p>
<h2>Use G suite, or previously: Google Apps</h2>
<p>For 99% of my customers G suite is the way to go and they love it. But I&#8217;ve a personal Google account with a Gmail address and why should I pay for G suite just for mail? I don&#8217;t need the rest. At the shared hosting company I just had some email forwarders setup in the Direct Admin control panel. That&#8217;s what I want! Just some simple mail forwarders&#8230;</p>
<h2>Setup mail forwarders with Postfix</h2>
<p>I&#8217;ll spare you the details, but configuring Postfix to forward emails isn&#8217;t that hard. But we&#8217;re still missing the SPF and DKIM records to make sure our emails will not be marked as spam, that&#8217;s taking some more time but I don&#8217;t like to repeat these steps on multiple servers and for multiple domains&#8230;</p>
<h2>Use a email service to send emails</h2>
<p>To make sure our emails will not be marked as spam we could use a email service like <a href="https://sendgrid.com/" target="_blank">SendGrid</a>, <a href="https://www.mandrill.com/" target="_blank">Mandrill</a>, <a href="https://aws.amazon.com/ses/" target="_blank">Amazon SES</a>, etc. I&#8217;ve created a account at SendGrid, configured the DNS records, installed the WordPress plugin for my blog and it&#8217;s working perfectly! Not one email was marked as spam! But that&#8217;s for sending emails, I still need those email forwarders&#8230;</p>
<h2>Catch-all email forwarding with Improvmx</h2>
<p><a href="http://improvmx.com/" target="_blank">Improvmx</a> makes it possible to create a catch-all email forwarder very easily. Change your DNS MX records, fill-in your domain and email address and you&#8217;re done! How awesome is that? But what if I want to be explicit about my email forwarders or don&#8217;t want a catch-all one&#8230;</p>
<h2><a href="http://improvmx.com/"><img class="aligncenter size-medium wp-image-927" src="https://royduineveld.nl/wp-content/uploads/2016/10/improvmx-300x154.png" alt="Improvmx" width="300" height="154" srcset="https://royduineveld.nl/wp-content/uploads/2016/10/improvmx-300x154.png 300w, https://royduineveld.nl/wp-content/uploads/2016/10/improvmx-768x395.png 768w, https://royduineveld.nl/wp-content/uploads/2016/10/improvmx-1024x526.png 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a>Mailgun!</h2>
<p>With <a href="https://mailgun.com/" target="_blank">Mailgun</a> it&#8217;s also possible to <a href="https://documentation.mailgun.com/quickstart-receiving.html" target="_blank">receive emails</a>, not as a inbox (which I don&#8217;t need) but as a forwarder or parser! So I&#8217;ve deleted my SendGrid account, removed the SendGrid WordPress plugin, created a Mailgun account, added my domain, configured the DNS records, installed the <a href="https://nl.wordpress.org/plugins/mailgun/" target="_blank">Mailgun WordPress plugin</a> and created some &#8220;routes&#8221; to forward my email. Awesome!</p>
<p><a href="https://royduineveld.nl/wp-content/uploads/2016/10/mailgun-route.png" target="_blank"><img class="aligncenter wp-image-931 size-medium" src="https://royduineveld.nl/wp-content/uploads/2016/10/mailgun-route-300x162.png" alt="Mailgun route" width="300" height="162" srcset="https://royduineveld.nl/wp-content/uploads/2016/10/mailgun-route-300x162.png 300w, https://royduineveld.nl/wp-content/uploads/2016/10/mailgun-route-768x415.png 768w, https://royduineveld.nl/wp-content/uploads/2016/10/mailgun-route-1024x553.png 1024w, https://royduineveld.nl/wp-content/uploads/2016/10/mailgun-route.png 1500w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2>Things to keep in mind</h2>
<p>By using a email service you&#8217;ll have to make sure you configure it at all your applications/websites. For WordPress there is a <a href="https://nl.wordpress.org/plugins/mailgun/" target="_blank">Mailgun plugin</a>, <a href="https://laravel.com/docs/5.3/mail#driver-prerequisites" target="_blank">Laravel supports it out of the box</a> and for other platforms who don&#8217;t have anything like that you can use the <a href="https://documentation.mailgun.com/quickstart-sending.html" target="_blank">SMTP credentials provided by Mailgun</a>. Also the title of the article is &#8220;free email forwarding&#8221;, yes it&#8217;s free up to 10.000 email a month, you need more? Check out the <a href="http://www.mailgun.com/pricing" target="_blank">Mailgun pricing</a>. And a quick tip for testing your mail forwarder: <a href="http://send-email.org" target="_blank">http://send-email.org</a>.</p>
<h2>Alternatives</h2>
<p>Once in a while I receive emails with suggestions for other services, I&#8217;m trying to list them here:</p>
<ul>
<li><a href="https://forwardmx.io/" target="_blank">forwardmx.io</a> (paid)</li>
<li><a href="https://forwardemail.net/" target="_blank">forwardemail.net</a> (free and open source)</li>
<li><a href="https://cloudflare.com" target="_blank">cloudflare.com</a> (free)</li>
</ul>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/free-email-forwarding/">[EN] Free email forwarding</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/free-email-forwarding/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>[EN] Hacking public GIT repositories</title>
		<link>https://royduineveld.nl/hacking-public-git-repositories/</link>
		<comments>https://royduineveld.nl/hacking-public-git-repositories/#respond</comments>
		<pubDate>Sat, 24 Sep 2016 17:30:27 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[GIT]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=903</guid>
		<description><![CDATA[<p>It&#8217;s a bad idea to expose your GIT directory, but why? What are the consequences? And how can you prevent this? Let&#8217;s start with prevention It&#8217;s pretty easy to prevent...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/hacking-public-git-repositories/">[EN] Hacking public GIT repositories</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>It&#8217;s a bad idea to expose your GIT directory, but why? What are the consequences? And how can you prevent this?</p>
<h2>Let&#8217;s start with prevention</h2>
<p>It&#8217;s pretty easy to prevent this, just make sure your GIT directory is not in your virtualhost. For example with <a href="https://github.com/laravel/laravel" target="_blank">Laravel</a>; there is a &#8220;/public&#8221; directory where you should point your virtualhost to, so with Laravel you&#8217;re not &#8220;vulnerable&#8221;. When you&#8217;re using WordPress you <a href="https://royduineveld.nl/wordpress-use-version-control-or-not/" target="_blank">could use Bedrock</a> and in general for everything else or if Bedrock isn&#8217;t an option; block it with your webserver! In case of Magento you can test if your GIT directory is accessible with <a href="https://www.magereport.com/" target="_blank">Magereport</a>.</p>
<h3>Apache configuration or .htaccess file</h3>
<pre class="brush: plain; title: ; notranslate">RedirectMatch 404 /\.git</pre>
<h3>nginx configuration</h3>
<pre class="brush: plain; title: ; notranslate">
location ~ /.git/ {
      deny all;
}
</pre>
<h2>The possibilities</h2>
<p>When the GIT directory is accessible you can read the GIT configuration file pretty easily, just put &#8220;/.git/config&#8221; after a websites url. Cool, we can see the remotes and stuff but what else can we do? <strong>We can download the whole repository!</strong></p>
<h3>Downloading a public facing GIT repository</h3>
<p>On Github there is a tool called <a href="https://github.com/kost/dvcs-ripper" target="_blank">DVCS Ripper</a> which includes a script to rip GIT repo&#8217;s. Download the &#8220;rip-git.pl&#8221; file and run it:</p>
<pre class="brush: plain; title: ; notranslate">
./rip-git.pl -s -v -u http://www.example.com/.git/
</pre>
<p>Regarding the size of the repository it could take some time, if there is only code in the repo it&#8217;s probably done within a few minutes but in other cases where for example all product images (which is a bad idea btw) are stored in the repo it can take hours. The longest I&#8217;ve waited is about 4 hours with a repo of multiple gigabytes.</p>
<h3>What next? What&#8217;s so dangerous?</h3>
<p>Is it not dangerous enough that somebody can download your complete sources? They can setup a copy of your website, search for bugs in your code and exploit them or in case the developer was really stupid there is a configuration file in the repo with database passwords! I&#8217;m not joking, I&#8217;ve experienced this with multiple websites and even big webshops! To make it even better (or worser?) some sites had a PhpMyAdmin installation running at &#8220;/phpmyadmin&#8221;. The possibilities are endless from that point. Some things I could do:</p>
<ul>
<li><strong>Deface the website</strong> and put <a href="https://www.youtube.com/watch?v=Sagg08DrO5U" target="_blank">Gandalf Sax</a> on it</li>
<li>Create a administrator account to <strong>access the backend</strong></li>
<li>Change some payment provider credentials so <strong>all payments go to my bankaccount</strong></li>
<li>Dump the database and <strong>sell it</strong> with the sources <strong>to a competitor or on the black market</strong></li>
<li><strong>Leak usernames and passwords</strong></li>
</ul>
<h2>How to find public GIT repo&#8217;s?</h2>
<p>As mentioned before just try &#8220;/.git/config&#8221; after the url, but also Google can help you with this. Just search this on Google:</p>
<pre class="brush: plain; title: ; notranslate">.git intitle:&quot;Index of&quot;</pre>
<p>And you&#8217;re going to find a lot of websites, but all of them do have directory listing enabled in their webserver (so you can browse through folders without a index html or php file). Google doesn&#8217;t index for example the /.git/config file directly so there are a lot more websites with this problem!</p>
<h3>What big websites do have this problem?</h3>
<p>I&#8217;ve created a simple script to loop through the top 1 million websites of the Alexa ranking, you can find this script in a <a href="https://gist.github.com/royduin/cb21d6c91cfe1a39297e40a168b5c957" target="_blank">Github Gist</a>. After running this script multiple hours and scanning the top 10.000 websites I&#8217;ve found 73 public facing GIT repo&#8217;s. Some of them aren&#8217;t dangerous because it&#8217;s a open-source website like the website of <a href="https://angularjs.org/" target="_blank">AngularJS</a> which is on that list, but most of them are not intended to be public. I&#8217;m not going to put some names here until it&#8217;s fixed and the owner of the website agree to publish the name here but I came across sites like:</p>
<ul>
<li>Webshops</li>
<li>News sites</li>
<li>File sharing sites</li>
<li>Cloud providers</li>
<li>Online advertising companies</li>
<li>Online video streaming sites</li>
<li>Stock image sites</li>
<li>Proxy providers</li>
</ul>
<p>And to emphasize again, those are in the top 10.000 websites of the world! Later I came across <a href="https://en.internetwache.org/dont-publicly-expose-git-or-how-we-downloaded-your-websites-sourcecode-an-analysis-of-alexas-1m-28-07-2015/" target="_blank">this website</a>, they&#8217;ve crawled the whole 1 million Alexa list and created some fancy charts from it.</p>
<h2>Conclusion</h2>
<p><strong>DON&#8217;T PUBLICLY EXPOSE YOUR GIT DIRECTORY!</strong> Or any other version control system like HG, Bazaar, SVN, CVS, etc.</p>
<h2>Updates</h2>
<p>I&#8217;ve contacted some companies with a publicly exposed git directory and here are the results:</p>
<ul>
<li><a href="http://www.translated.net/" target="_blank">translated.net</a>: after downloading the source code I also found a XSS vulnerability. Meanwhile everything is fixed in co-operation with the company.</li>
<li><a href="http://the-watch-series.to/" target="_blank">the-watch-series.to</a>: I&#8217;ve downloaded the sources but didn&#8217;t find the time to inspect it, after contacting the support team the issue is fixed and the code is removed.</li>
</ul>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/hacking-public-git-repositories/">[EN] Hacking public GIT repositories</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/hacking-public-git-repositories/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[EN] Creating your own WordPress import</title>
		<link>https://royduineveld.nl/creating-your-own-wordpress-import/</link>
		<comments>https://royduineveld.nl/creating-your-own-wordpress-import/#comments</comments>
		<pubDate>Mon, 30 May 2016 16:49:11 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=879</guid>
		<description><![CDATA[<p>For WordPress there are thousands of import plugins, but the most of them can only import posts, the other one is specific for a theme or custom post type, etc. there...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/creating-your-own-wordpress-import/">[EN] Creating your own WordPress import</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>For WordPress there are <a href="https://wordpress.org/plugins/search.php?q=import" target="_blank">thousands</a> of import plugins, but the most of them can only import posts, the other one is specific for a theme or custom post type, etc. there are to many to try. But the most advanced and besides the easiest plugin is <a href="http://www.wpallimport.com/" target="_blank">WP All Import</a>! That plugin can import almost everything and if not, there are some <a href="http://www.wpallimport.com/add-ons/" target="_blank">add-ons</a> as well. The downside is, it&#8217;s not free. The plugin costs only $99 and includes lifetime updates, support and may be used on unlimited websites! A great deal if you ask me. Not convinced yet? Just <a href="http://www.wpallimport.com/try/" target="_blank">try it</a> out before you buy! But this post is about creating your own WordPress import right? Absolutely! I needed a very specific import which didn&#8217;t fit in WP All Import plugin so I&#8217;ve build a custom import and here is how.</p>
<h2>Creating a plugin</h2>
<p>The first step is to create a plugin, this is very simple. Create a new folder in the &#8220;/wp-content/plugins&#8221; folder and name it for example: &#8220;import-demo&#8221;. Inside that folder create a file &#8220;import-demo.php&#8221; and place some plugin <a href="https://developer.wordpress.org/plugins/the-basics/header-requirements/" target="_blank">header information</a> inside it:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/*
Plugin Name: Import demo
Plugin URI: https://royduineveld.nl
Description: A demo import for my blog
Version: 1.0
Author: Roy Duineveld
Author URI: https://royduineveld.nl
*/
</pre>
<p>After that you could create some admin menu&#8217;s to start the import manually, but I&#8217;ll skip that for now because I&#8217;m going to run the import from the WordPress cron system.</p>
<h2>Getting started with the import</h2>
<p>First we need a source, for example a csv or xml file. In this post I assume that you&#8217;ve got a xml file:</p>
<pre class="brush: php; title: ; notranslate">
$xml = simplexml_load_file(file_get_contents('http://domain.com/your-xml-file.xml'));
</pre>
<p>After that, just loop through the items and create a post for each entry:</p>
<pre class="brush: php; title: ; notranslate">
$postCreated = array(
	'post_title' 	=&gt; $item-&gt;title,
	'post_content' 	=&gt; $item-&gt;content,
	'post_excerpt' 	=&gt; $item-&gt;excerpt,
	'post_status' 	=&gt; 'publish',
	'post_type' 	=&gt; 'post', // Or &quot;page&quot; or some custom post type
);
$postInsertId = wp_insert_post( $postCreated );
</pre>
<p>After that we may need some post options / meta&#8217;s, for example some fields for the <a href="https://nl.wordpress.org/plugins/wordpress-seo/" target="_blank">Yoast SEO plugin</a>:</p>
<pre class="brush: php; title: ; notranslate">
$postOptions = array(
	'_yoast_wpseo_title'	=&gt; $item-&gt;title,
	'_yoast_wpseo_metadesc'	=&gt; $item-&gt;metadescr,
);
foreach($postOptions as $key=&gt;$value){
	update_post_meta($postInsertId,$key,$value);
}
</pre>
<h2>Adding a featured image</h2>
<p>To add a featured image to a post we&#8217;ve to &#8220;sideload&#8221; it and to do it within our import plugin we&#8217;ve to &#8220;hack&#8221; a little bit to catch the attachment id and save it on the post:</p>
<pre class="brush: php; title: ; notranslate">
add_action('add_attachment','featuredImageTrick');
media_sideload_image($item-&gt;image, $postInsertId, $item-&gt;title);
remove_action('add_attachment','featuredImageTrick');
</pre>
<p>With this action hook we need that &#8220;featuredImageTrick()&#8221; function:</p>
<pre class="brush: php; title: ; notranslate">
function featuredImageTrick($att_id){
    $p = get_post($att_id);
    update_post_meta($p-&gt;post_parent,'_thumbnail_id',$att_id);
}
</pre>
<p>And we need to make sure we&#8217;ve included some WordPress core files:</p>
<pre class="brush: php; title: ; notranslate">
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
</pre>
<h2>Run your import with the WordPress cron</h2>
<p>WordPress does have a cron system build in which doesn&#8217;t require to setup a cron task on your hosting environment within your control panel or with the &#8220;crontab&#8221; command on Linux systems. But how is this working? WordPress runs a asynchronously request when someone visits your WordPress website (so the page request doesn&#8217;t slow down for the visitor). A function checks the time and the scheduled tasks and runs the task if needed. That&#8217;s for example how the WordPress checks for version updates and that&#8217;s how the auto updates are handled. We can hook into to this with a simple function. But first we&#8217;ve to wrap everything we&#8217;ve written into a function and we&#8217;ve to created a WordPress action for it:</p>
<pre class="brush: php; title: ; notranslate">
add_action('import_demo', 'importIt');
function importIt(){
    // The code until now
}
</pre>
<p>After that we can register two hooks to activate the cron task and deactivate it when the plugin is disabled or removed:</p>
<pre class="brush: php; title: ; notranslate">
register_activation_hook(__FILE__, 'activateCron');
function activateCron() {
	wp_schedule_event(strtotime('tomorrow midnight'), 'daily', 'import_demo');
}

register_deactivation_hook(__FILE__, 'deactivateCron');
function deactivateCron() {
	wp_clear_scheduled_hook('import_demo');
}
</pre>
<p>With this setup our import will run at or after 0:00, it depends on when a visitor is coming. If you need to make sure that the cron is running at the specified time you can setup a real cronjob by disabling the default cron behavior with this in your &#8220;wp-config.php&#8221; file:</p>
<pre class="brush: php; title: ; notranslate">
define('DISABLE_WP_CRON', true);
</pre>
<p>After that you can setup a cronjob, for example to run it every 5 minutes:</p>
<pre class="brush: plain; title: ; notranslate">
*/5 * * * * php /path/wp-cron.php
</pre>
<p>This doesn&#8217;t mean your import is running every 5 minutes, this is like a visitor is coming to your website every 5 minutes. So at midnight it will trigger the import.</p>
<h2>Remove previous posts before import</h2>
<p>Currently we&#8217;ve build the import to add posts every night, but it&#8217;s not overwriting existing ones from the previous import. The easiest way is to just delete those posts and re-import them. To do this we&#8217;ve to identify those imported posts first. Just add a extra post option / meta:</p>
<pre class="brush: php; title: ; notranslate">
$postOptions = array(
	'imported' =&gt; true
);
</pre>
<p>And after that we can identify them and remove the post with its featured image:</p>
<pre class="brush: php; title: ; notranslate">
currentPosts = get_posts(array( 
	'post_type' 		=&gt; 'post',
	'post_status' 		=&gt; 'publish',
	'meta_key'			=&gt; 'imported', // Our post options to determined
	'posts_per_page'   	=&gt; 1000
));

foreach($currentPosts as $post){
	if($thumbId = get_post_meta($post-&gt;ID,'_thumbnail_id',true)){
		wp_delete_attachment($thumbId,true);
	}
	wp_delete_post( $post-&gt;ID, true);
}
</pre>
<h2>Wrapping it up</h2>
<p>Congrats! You&#8217;ve build your own import for WordPress. <a href="https://gist.github.com/royduin/69ff94b68c28419dbd9a8e07dcf13187" target="_blank">You can find the complete &#8220;import-demo.php&#8221; on Github Gist</a>. Good luck!</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/creating-your-own-wordpress-import/">[EN] Creating your own WordPress import</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/creating-your-own-wordpress-import/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[EN] WordPress, use version control or not?</title>
		<link>https://royduineveld.nl/wordpress-use-version-control-or-not/</link>
		<comments>https://royduineveld.nl/wordpress-use-version-control-or-not/#respond</comments>
		<pubDate>Sun, 06 Mar 2016 17:02:15 +0000</pubDate>
		<dc:creator><![CDATA[Roy Duineveld]]></dc:creator>
				<category><![CDATA[Tips & Trucs]]></category>
		<category><![CDATA[GIT]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">https://royduineveld.nl/?p=853</guid>
		<description><![CDATA[<p>Should you use version control when you&#8217;re working on a WordPress website or not? It depends on what you&#8217;re going to do. Are you creating a custom theme or plugins?...</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/wordpress-use-version-control-or-not/">[EN] WordPress, use version control or not?</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Should you use version control when you&#8217;re working on a WordPress website or not? It depends on what you&#8217;re going to do. Are you creating a custom theme or plugins? Or are you just using some plugins from the <a href="https://wordpress.org/plugins/" target="_blank">WordPress theme directory</a> with a free or premium theme from <a href="http://themeforest.net/?ref=royduin" target="_blank">Themeforest</a>?</p>
<h2>When you&#8217;re not using version control</h2>
<p>You&#8217;ve downloaded the WordPress archive, extracted it, uploaded it to your hosting environment, ran the installation, installed some useful plugins and put your purchased theme from <a href="http://themeforest.net/?ref=royduin" target="_blank">Themeforest</a> in the &#8220;/wp-content/themes&#8221; directory. Congrats you&#8217;ve installed WordPress! You can update Wordpress and it&#8217;s plugins easily through the backend and critical WordPress updates are even installed automatically. To update your premium theme from the backend you could use the <a href="https://github.com/envato/envato-wordpress-toolkit" target="_blank">Envato WordPress Toolkit</a> so you don&#8217;t have to do this manually. Fantastic, what a great system! No I&#8217;m not joking, it really is! How simple can it be?</p>
<h2>Version control your custom theme or plugin</h2>
<p>But what if you&#8217;re creating your own theme (for example with <a href="https://roots.io/sage/" target="_blank">Sage</a>) or plugin? You could just upload it every time you&#8217;ve changed something but how do you keep track of your changes? What if you&#8217;ve to undo your latest change or working on it with a team? That&#8217;s where version control systems like GIT can be handy! Put your custom theme or plugin in version control, work on it by committing your changes so you can do a rollback later if needed and work easily together with your team by using online services like <a href="https://github.com/" target="_blank">Github</a> or <a href="https://bitbucket.org/" target="_blank">BitBucket</a>. On your server you &#8220;cd&#8221; into the correct directory and &#8220;clone&#8221; your GIT repository with your theme or plugin. Great! Problem solved.</p>
<h2>Version control everything</h2>
<p>But what if you&#8217;ve multiple custom themes or plugins? Then you&#8217;ve to &#8220;cd&#8221; into all those directories and &#8220;pull&#8221; the latest versions, why don&#8217;t you just put everything (WordPress and all plugins and themes you&#8217;re using) in version control? No problem, fixed in no-time! Init GIT, add all the files and do a initial commit.</p>
<h3>WordPress auto updates</h3>
<p>But why is the auto updater not working anymore? Because <a href="https://github.com/WordPress/WordPress/blob/08e7c845cc06fc5c9498ce056b6db201259b7a1f/wp-admin/includes/class-wp-upgrader.php#L2733-L2793" target="_blank">WordPress checks if it&#8217;s under version control</a> because files should not automatically change when they&#8217;re under version control. As you can see there in the code we can override this by defining a &#8220;automatic_updates_is_vcs_checkout&#8221; filter to ignore this check. But is that really what you want? Then we&#8217;ve uncommitted changed files after a update, so don&#8217;t! When you&#8217;ve WordPress under version control you should take care of all types of updates by yourself. Update WordPress on your local environment, &#8220;commit&#8221; the changes and &#8220;pull&#8221; them on the production environment.</p>
<h3>Installations and updates from the backend</h3>
<p>WordPress doesn&#8217;t auto update anymore because it&#8217;s under version control, but we still can update WordPress by clicking the update button. Also we (or your end user / customer if they&#8217;ve a admin account) can install plugins and update everything else (plugins, themes and translations) from the backend. When we do one of these things we&#8217;ve again uncommitted changed files, so it&#8217;s advised to disallow these things by <a href="https://codex.wordpress.org/Editing_wp-config.php#Disable_Plugin_and_Theme_Update_and_Installation" target="_blank">defining the &#8220;DISALLOW_FILE_MODS&#8221; constant</a>. Now it&#8217;s &#8220;impossible&#8221; to get changed files on your production environment. So a &#8220;git status&#8221; shouldn&#8217;t return any changes, if it is returning files changes something (like a plugin or theme) isn&#8217;t following the WordPress standards or you&#8217;ve got hacked. I don&#8217;t wanna scare you, but when you&#8217;ve got hacked your happy with version control because you can see what files are changed and with a &#8220;git checkout .&#8221; all the changed files by the hacker are undone.</p>
<h2>Use <a href="https://roots.io/bedrock/" target="_blank">Bedrock</a>!</h2>
<p>You&#8217;ve got all your WordPress websites under version control. But think about it, isn&#8217;t their much duplication? WordPress itself is in all those GIT repositories and probably on every WordPress website you&#8217;re using almost the same plugins. When there is a WordPress, theme or plugin update you&#8217;ve to open all those sites locally and update, commit, push and pull. Can this be done cleaner with less duplication in repositories and less work when there is an update? Absolutely! Use <a href="https://roots.io/bedrock/" target="_blank">Bedrock</a>!</p>
<h3>What is Bedrock?</h3>
<p>Bedrock is a boilerplate for WordPress which is using a better folder structure, handles dependency management with <a href="https://getcomposer.org/" target="_blank">Composer</a> and makes it very easy to configure with <a href="https://github.com/vlucas/phpdotenv" target="_blank">Dotenv</a> and environment specific configuration files. With Bedrock you&#8217;ve a very solid base when you&#8217;d like to put your WordPress site under version control.</p>
<h3>How is Bedrock making it easier?</h3>
<h4>Dependency management</h4>
<p>All your dependencies like WordPress itself and mostly plugins are defined in your &#8220;composer.json&#8221; file, <a href="https://github.com/roots/bedrock/blob/master/composer.json#L39" target="_blank">just one line with the name and the version number</a> you&#8217;d like. After running &#8220;composer install&#8221; (or &#8220;composer update&#8221; in case you&#8217;ve changed something in the &#8220;composer.json&#8221; file) all the dependencies will be downloaded (or grabbed from the Composer cache in case the dependency is already downloaded before) but <a href="https://github.com/roots/bedrock/blob/master/.gitignore" target="_blank">ignored for version control</a> so they aren&#8217;t in your GIT repository. So if you want to update WordPress or something else, change the version number in the &#8220;composer.json&#8221; file, run &#8220;composer update&#8221; and commit the two changed files (the composer.json and composer.lock file). On your production environment you just have to do a &#8220;git pull&#8221; and a &#8220;composer install&#8221; and your update is live. How clean can your GIT repository be?</p>
<h4>Dotenv and environment specific configurations</h4>
<p>One of the other great things about bedrock is the <a href="https://github.com/roots/bedrock/tree/master/config" target="_blank">build-in configuration</a> which is using <a href="https://github.com/vlucas/phpdotenv" target="_blank">Dotenv</a>. The configuration files take care of defining constants per environment, like the earlier mentioned &#8220;DISALLOW_FILE_MODS&#8221; constant and Dotenv takes care or environment specific configuration settings. <a href="https://github.com/roots/bedrock/blob/master/.env.example" target="_blank">Just one simple &#8220;.env&#8221; file</a> which contains all the configuration options.</p>
<h3>What are the downsites of using Bedrock?</h3>
<p>All translations <a href="https://github.com/roots/bedrock/issues/30#issuecomment-109758292">needs to be committed</a> in your GIT repository by default which causes &#8220;duplication&#8221; again, you&#8217;ve to do <a href="https://github.com/roots/bedrock/issues/58">some additional stuff</a> go get multisite working and not really a &#8220;Bedrock problem&#8221; but relevant if you&#8217;ve bought some premium stuff from <a href="http://themeforest.net/?ref=royduin" target="_blank">Themeforest</a>: it&#8217;s to <a href="http://stackoverflow.com/questions/34295318/install-premium-wordpress-theme-with-composer">devious to install premium stuff with Composer</a>.</p>
<h2>What do you use?</h2>
<p>For &#8220;simple&#8221; WordPress websites without custom code I don&#8217;t use version control, I think it&#8217;s a little bit overkill and I like the auto update feature in that case. But for projects with a custom theme or plugin I&#8217;m using Bedrock and I&#8217;ve my theme or plugin in the same repository because most of the time it&#8217;s specific for that website. What do you do in which scenario and why?</p>
<p>Het bericht <a rel="nofollow" href="https://royduineveld.nl/wordpress-use-version-control-or-not/">[EN] WordPress, use version control or not?</a> verscheen eerst op <a rel="nofollow" href="https://royduineveld.nl">Roy Duineveld</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://royduineveld.nl/wordpress-use-version-control-or-not/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>