Tuesday 15 August 2017

Taming Outlook meeting requests (part 2)

Yesterday I published a post about Taming Outlook meeting requests, in which I talked through the writing of a script which checked for working hours and calendar availability, before automatically accepting any meeting which met the requirements.  And then I left you hanging... Sorry about that!

Well here I am to pick up where I left off.  We're going to look at actually running the script!

First of all, you'll need to slightly drop your security settings, whilst you're developing.  We'll come back to fix this at the end, once we're happy the script is working.  The setting I'm referring to can be found in Outlook (I'm using 2016) here...

File > Options > Trust Center > Trust Center Settings > Macro Settings



Hopefully you've either got this set to "Disable all...", which would be the most secure (but possibly a little limiting" or "Notifications for digitally signed..." which is the second most secure.  We haven't digitally signed our script (yet!) so we'll to set this to "Notifications for all macros".  

Please be careful to only click yes on prompts for this script, or others that you know about, and don't blame me if you click yes on something dodgy!

Now we need to go and create a new mail rule.  This can usually be done by choosing "Rules" from the ribbon menu and then "Create a rule", but hopefully this is something you're familiar with, feel free to skip out and Google this is you're not.  

Now you've started creating a new rule, you'll want to select the conditions "on this computer only" (because the script only exists on this computer) and "which is a meeting invitation or update" (so that it doesn't process your script for every single email, something like this...




Then click the "Next" button and you'll want to select the action "run a script".  If you're in a newer version of Outlook (anything from 2013 onwards, I believe) then you may find that you don't have this option, in which case, you'll want to add the following registry entry and then restart Outlook...

Entry: HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Security
Dword: EnableUnsafeClientMailRules
Value: 1

You can also download the .reg file from my site, if you would like.  It should be noted that this is for Outlook 2016, and it might be different (not version 16.0) for other versions.

Once this is down and you've restarted, you should be able to select the "run a script" option.  


At the bottom you then have a link to click which says "a script", which will allow you to select the script you've written and compiled in part 1.


If you're following this blog then this is almost certainly going to only give you one script to select, but if not, look for the one that matches the name you used - for me this is "Project1.ThisOutlookSession.AutoAcceptMeetings", and click "OK".

You can then complete the wizard, adding an exceptions you want (none for me) and then setting the name (something massively original like "Auto Accept Meetings") and when it should run, etc.

You can now test, either on existing meeting requests in your inbox, or by waiting for the next one to come in!  If you've left those Debug.Print lines in then it might be worth hitting Alt+F11 in Outlook to get back to the script editor, and then pressing Ctrl+G in there.  This will bring out an "Immediate" panel at the bottom, and this is like a console, which will display all of the debug messages when they happen.

Once you've got your script working, it's time to go back and sort out your security.  The way that we do this, is by self signing the script.  There are a number of guides online about how to do this, but I'm going to quickly walk you through the steps I took on my Windows 10 machine.

Firstly, find "SelfCert.exe" - this is the Office application that we will be using to create the certificate.  For me, this was located here...

C:\Program Files (x86)\Microsoft Office\Office16\SELFCERT.EXE

Run this application and it should look like this...



There's a box at the bottom to enter your certificate name, so just enter whatever you like and click "OK".  You should get a message to say it's been successfully created.

Now go back into the script editor by pressing Alt+F11 in Outlook, and from the "Tools" menu select the "Digital Signature" menu item.  In this screen you can use the "Choose" button to find your certificate and select it, and then click "OK".  That's it, signed!

Now exit Outlook and run it again "as administrator".  You should then run your rule, manually if it doesn't happen automatically, and you'll get a popup asking you if it's ok to run the macro.  Personally I went with "Trust all documents from this publisher", as I am the publisher, and I trust myself.  You can now set your Trust Center settings back, so that only signed macros are prompted for.

You can now exit Outlook and run it again normally.  You should have Outlook meeting request bliss now, as your rule will run, which will run your script, and automatically accept all those meetings requests for you.  As you've trusted yourself, you shouldn't get any more prompts.  

Ahhhh, that's better!

If you're interested, you can download the .cls file from my site, which contains all the code.

Monday 14 August 2017

Taming Outlook meeting requests (part 1)

If you're anything like me, and to be honest, for your sake I hope you're not, then you spend a lot of your time either in meetings, or in Outlook processing meeting requests!

Wouldn't it be nice if I could get Outlook to sort out meeting requests automatically?  Oh, but it can, I hear you cry.  But have you seen the options available?



Do I want to automatically accept everything?  Well ideally people would check my diary before inviting me to meetings, but inevitably they don't, so no, I don't want a tangled mess of double/triple bookings to sort out, thank you very much!

Do I want to automatically decline meeting requests that conflict?  No, what if someone invites me to an important meeting that conflicts with a casual and easily movable catch up meeting?  

Do I want to decline recurring meeting requests?  Why would anyone want to do this?  

There has to be a better way!!  And there is, if you're prepared to break out a little VBA (Visual Basic for Applications).  Having spent quite a lot of time doing VB Script for MediaMonkey add-ons (please excuse the rather aged site should you click that link), I was more than happy to dust off my rusty skills and give it a go.

To get started, in Outlook (and I'm using Outlook 2016, so I apologise if there are any differences in your version), click Alt+F11 to bring up the built in editor...




In the Project tree in the left pane, select "Project1 (VbaProject.OTM)", then "Microsoft Outlook Objects" and finally "ThisOutlookSession".  This will present a code window on the right which is blank, ready for you to add your code.

Outlook will call your sub procedure (more on this later!) and pass in a single parameter, the Meeting Item object for the meeting.  So let's start with a simple one that checks the object is a meeting request...

  Sub AutoAcceptMeetings(oRequest As Outlook.MeetingItem)
    On Error GoTo ErrorHandler

    If oRequest.MessageClass <> "IPM.Schedule.Meeting.Request" Then
      Debug.Print "Not a meeting request"
      Exit Sub
    End If

    Debug.Print "This is a meeting request"
    Exit Sub

  ErrorHandler:
    Debug.Print Err.Description & " [" & Err.Number & "]"
  End Sub


So we've named our sub procedure "AutoAcceptMeetings".  It takes in the Meeting Item object and checks that it's Message Class property matches what we expect, otherwise we exit out early.  We've also added in some error handling, to make debugging easier!  Whenever an error is thrown, we'll skip straight down to "ErrorHandler" and output the description and error number.

But we want a bit more than this, so instead of outputting that this is a meeting request, let's instead get the appointment that's associated with the meeting, and check that it falls within my working hours.  I don't want meetings to be automatically accepted if they're early in the morning or late in the evenings!

    Dim oAppointment As Outlook.AppointmentItem
    Set oAppointment = oRequest.GetAssociatedAppointment(True)

    Dim sStart As String
    sStart = Format(oAppointment.Start, "hh:mm:ss")
    If sStart < #9:30:00 AM# Or sStart > #4:30:00 PM# Then
      Debug.Print "Meeting outside of work hours"
      Exit Sub

    End If

This first gets the Appointment Item object and then gets the Start property, which is the date and time that the meeting starts.  From this we get just the time part as a string, using the Format function.  We can then check if this is before 9.30am or after 4.30pm.  Notice in VBA we can use the hash (#) character around the time to cast it as a time, which makes the comparison work as we want.

That's great, but what I really want to do, is check my availability in my calendar, to see if I've already got a meeting in place.  Before we can do this, we need to access our own account and check that it's an Exchange account - this part will only work with an Exchange account, and not with another type, such as SMTP.  

    Dim oRecipient As Outlook.Recipient
    Set oRecipient = Session.CreateRecipient("email@example.com")
    oRecipient.Resolve
    If Not oRecipient.Resolved Then
      Debug.Print "Recipient could not be resolved"
      Exit Sub
    End If
    If oRecipient.AddressEntry.Type <> "EX" Then
      Debug.Print "Recipient is not Exchange type"
      Exit Sub
    End If

This works by using the application Name Space object stored in the global variable "Session", and then using the Create Recipient method to get a new Recipient object for our specified email address (my own).  We can then use the Resolve method to check against the address book, and error out if this fails.  We can then check the Address Entry Type property to check if it's an Exchange account.

Now it's time to check for my availability, using the Free Busy method...

    Dim nInterval As Long
    Dim sFreeBusy As String
    Dim nPosition As Long
    Dim nDuration As Long
    Dim sTest As String
    nInterval = 15
    sFreeBusy = oRecipient.FreeBusy(oAppointment.Start, nInterval, True)
    nPosition = (TimeValue(sStart) * (1440 / nInterval)) + 1
    nDuration = oAppointment.Duration
    sTest = Mid(sFreeBusy, nPosition, (nDuration / nInterval))
    If InStr(1, sTest, "2") Then
      Debug.Print "Meeting conflicts with another appointment"
      Exit Sub
    End If
    If InStr(1, sTest, "3") Then
      Debug.Print "Meeting conflicts with out of office"
      Exit Sub
    End If
    If InStr(1, sTest, "4") Then
      Debug.Print "Meeting conflicts with working elsewhere"
      Exit Sub

    End If

This bit is potentially a bit confusing, so I'll explain what each variable is being set to in turn...

  • nInterval - this is the level of detail that I want my calendar in, for me I decided that 15 minutes was the right interval/level of detail, and therefore I set this to 15
  • sFreeBusy - we call the Free Busy method, with the day of the appointment (the Appointment Item Start property), the interval and then True to indicate that we want more detail - if this last parameter is set to false then we only get free/busy and not information such as tentative and out of office.  This method returns a string of characters, one for each 15 minute interval, indicating availability.
  • nPosition - we need to calculate the start position in the string which matches up with the start time of the appointment, so that we can check availability specifically for this period of the day
  • nDuration - we need to know the duration so that we can calculate the end position in the string which matches up with the end time of the appointment
  • sTest - this uses VBA Mid function to just take the section of the string which matches up with the appointment, using nPosition, nDuration and nInterval
Now that we've got our string, we can check to see if any of the following characters match with the olBusyStatus values that we're interested in...
  • 2 - Busy
  • 3 - Out of office
  • 4 - Working elsewhere
In any of these cases, I don't want the meeting to be accepted automatically.  However for Free (0) or Tentative (1) then we can go ahead accept the meeting.  So let's do that next...


    Dim oResponse As Outlook.MeetingItem
    Set oResponse = oAppointment.Respond(olMeetingAccepted, True)
    oResponse.Send
    Debug.Print "Meeting accepted"

    oRequest.UnRead = False
    oRequest.Delete
    Debug.Print "Meeting request deleted"
    Exit Sub

There are two sections here; the first creates a new Meeting Item by calling the Appointment Item Respond method (passing in True hides the dialog box) and we then send it using the Send method, and the second deletes the original meeting request by marking it as read (by setting the UnRead property to False - a lovely double negative) and then deleting it (using the Delete method).  If you're still reading this then you probably are something like me, and therefore don't like having any emails marked as unread in your Deleted Items, so marking it as read is very important!

Now obviously there's a lot more that we can do here.  If you click any one of those links into the relevant MSDN page you'll see that there are loads more properties and methods we can use to automate different tasks, but for now, this seems pretty complete to me.

The script is now ready to be compiled, so click on the "Debug" menu and choose the "Compile Project1" menu item.  You're ready to go!


This post has already become massively long, so I'm going to leave you hanging here, and then show you how to use this script in my next post.

Tuesday 11 July 2017

Using pump instead of pipe

On thing that I quickly discovered whilst using Gulp to automate my development process, was the piping the results through from one plugin to another could often go wrong, and it was often hard to trap the reason and get proper error messaging out.

Then I discovered some example code which pointed me to pump.  Pump is...
a small node module that pipes streams together and destroys all of them if one of them closes

Essentially it allows you to restructure your Gulp task in a way that handles multiple plugins much better, and it fails more gracefully.

So I needed to include a copy of the new plugin at the top of my Gulp file...
var pump = require("pump");

Then I took the task which I wrote about in my last post...
gulp.task("js",function(cb) {
  browserify(jsFiles).bundle(),
    .pipe(source("script.js"))
    .pipe(gulp.dest("build/js"));
});

And re-wrote it to look like this...
gulp.task("js",function(cb) {
  pump([ 
    browserify(jsFiles).bundle(),
    source("script.js"),
    gulp.dest("build/js")
      ],cb); 
    });

As you can see, instead of using the "pipe" method to pass the files/content from one plugin to another, I am now passing an array of calls into pump and this is doing the hard work for me.

I have found that this makes the error messaging much more useful when I have syntax errors or plugins fail to work as expected.  It's certainly saving me a lot of time when it comes to debugging and fixing my Gulp file.

Monday 10 July 2017

Adding Browserify into the mix

Having already written my first gulp task, I was ready to get a bit more advanced.  My first task was to concatenate javascript files, including putting my local version of jQuery in front of my own javascript, so there was a single file to download, with all the required plugins before.  However, I'd already heard of Browserify, and wanted to see if this would work with Gulp (short answer - yes!).

Browserify is a tool which lets you "require" plugins in your javascript, and then processes these at build time (for example, in a Gulp task) to automatically include the plugins as well.  This would achieve the same end result as my current concatenation task, but without me having to keep and manually update local copies of the plugin files, as I could get Browserify to do the job of finding and including them.

I went through a few different iterations, all of which worked but were a bit messy for one reason or another (including using the gulp-browserify plugin, which is deprecated!) but I'm going to skip over those and skip straight to my final implementation.

So I needed to include a copy of extra plugins at the top of my Gulp file...
var browserify = require("browserify");
var source = require("vinyl-source-stream");

Then I updated my task to look this this...
gulp.task("js",function(cb) {
  browserify(jsFiles).bundle(),
    .pipe(source("script.js"))
    .pipe(gulp.dest("build/js"));
});

Notice that I am no longer using Gulp.Src to pick up the javascript files from the array, Browserify is now handling this, and I'm using the "bundle" method to concatenate them, instead of using gulp-concat.  During this "bundle" method, the javascript is also being processed to find any "require" statements.

For me, I needed to require jQuery and a few other bits, so at the top of my project javsacript file, I added the following lines...

var jQuery = require("jquery");
window.$ = window.jQuery = jQuery;
require("jquery.easing");
require("bootstrap/js/scrollspy");

As you can see, you can simply use the "require" method by itself, or you can assign the exported method to a variable.  In the case of jQuery, it needs to be stored globally in order for Bootstrap to load properly.  

I also went from including all of the Bootstrap library from a CDN to just including the component that I required, which saves on filesize!

It's now really easy to include new libraries and plugins into my javascript, by simply installing them using NPM, and including them using Browserify, as part of my automated Gulp task.

Sunday 9 July 2017

First Gulp task - concatenation

I recently started using a new development process, using Gulp to automate development tasks.  I thought I'd start off easy, so I'd concatenate my existing javascript files into a single file.

You might ask, why would you want to do this?  And my answer would be, you want to reduce the number of items fetched when loading your page, for optimal performance.

You might ask, why don't you do this manually once and then always work on the concatenated file?  And my answer would be, some of the files are plugins or libraries, which I want to keep separate so that I can easily update them.  I only want them to be combined in the build version.

So, questions out of the way, here's how it went down.

Firstly, you need to make sure you include the relevant bits at the top of your Gulp file.  I included 3 to start with...
var gulp = require("gulp");
var util = require("gulp-util");
var concat = require("gulp-concat");

Gulp and gulp-util are essentially your basics, needed in every Gulp file.  I then found gulp-concat on NPM (Node Package Manager, regardless of what the top left corner of the site says!) and it looked like it would do the trick, so I included that too.

This is one of the big advantages to Gulp.  Because it runs in Node, you can use NPM to find and install packages, which allows you to maximise the code not written, but using some that someone else has written for you!

So now I create a simple Gulp task...
gulp.task("js",function(cb) {
  gulp.src(jsFiles)
    .pipe(concat("script.js"))
    .pipe(gulp.dest("build/js"));
});

I'd recommend reading up more on Gulp, but essentially this is taking an array of filenames stored in "jsFiles" and using it as the source, piping this through to the gulp-concat plugin which takes a parameter to name the resultant output file, and then setting the destination to be my build directory.  When I run this, it sucks in all the files, concatenates them, and then spits out a single file to my build directory.

I then modified the HTML so that instead of linking to lots of different javascript files in the <head> tag, I now reference just one javascript file, and I also do it at the end of the <body>.  I've also added a "defer" attribute, because it really doesn't need to be loaded upfront - this reduces the time till the First Meaningful Paint.

Saturday 8 July 2017

New development process with Git and Gulp

Whilst working on my new project, Privacy Tools, I decided that I really needed to sort out my development process.  Not that there was anything particularly wrong with it, it's just very old school and manual.

I literally write every line of code (HTML, PHP, CSS and Javascript) by hand, in Notepad++.  This doesn't even syntax highlight particularly well, let alone auto-complete.  And then the build process is also manual, going to websites like Closure Compiler to minify my javascript.  Rather labourious, especially for someone who doesn't get much time to code these days, and therefore needs every minute to go into writing code!

So I started by finally using Git!  I don't think anyone in the development community could have possibly not heard of Git, even if they haven't used it.  I'd played about with it, done some tutorials (courtesy of LinkedIn Learning), but never used it properly.  I figured now was the time, and I'm genuinely loving it.  It's great to know that everything is properly stored away, and I can easily track changes and get back to earlier versions of a file (or set of files) when needed.  It's also stopping me from wandering off and starting a new bit whilst I'm still in the middle of another, as this really confuses the commits - now I'm working on one distinct chunk, getting that working, committing it with a lovely comment, and then on to the next one.  Lovely!

However, that doesn't really improve my workflow or efficiency (unless I need to revert back to an earlier file version).  Where I've really had fun is with Gulp.  Gulp is...
a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something.

And it really does do that!  I did have a look at some others, such as the increasingly popular Webpack, but what I really like about Gulp is that the automation is written in javsacript, so can be completely customisable.  You can easily minify javascript files and stylesheets, concatenate files, and pretty much any else that you can think of.  

I'm not going to go into too much detail on this post, but my next one will show you the details of my first Gulp file.  I've already started adding more and more as I've gone along, so I'm sure there'll be many more posts to follow.

Friday 7 July 2017

Project - Privacy Tools

One of the main reasons that I decided to have my own website, was to host some of the different projects that I've been working on. Some are in good working order (but never finished!) such as WCG Online, and others are in their infancy, like the one I'm talking about today.

Privacy Tools


Privacy matters - here's why!

A lot of internet users don't realise just how much websites can track them and invade their privacy. 
You can have security without privacy but you can't have privacy without security.  I have created the tools below to help show what websites can determine about you.

And that's the gist really, it is designed to be a set of tools to help educate users on internet privacy and how they can be more careful about the information that they give away.

It currently looks at things like your navigator details and history, canvas information, battery level, display and position information, and connection details.  It also looks at the major social networking sites to see which ones you're currently logged into.

As I say, this is very much a work in progress, and will continue to improve over the coming months.  

Check it out on my Projects page or directly at Privacy Tools.


Wednesday 26 April 2017

Going HTTPS with Cloudflare

Having already made my WCG Online project secure (as detailed in a previous post), it's been on my list for a while to do the same with my main website.  But I'd heard good things about Cloudflare and wanted to give them a try.

Well I'm glad I did, it was so easy and straight forward.  Here's how it went...

  1. Go to Cloudflare to sign up
  2. Enter your chosen email address and a new password
  3. Add your website domain, so for me this was www.rik.onl
  4. Click "Scan for DNS" and you'll get a little video to watch whilst it's processing
  5. Click "Continue" once it's down
  6. You'll then see a list of your DNS records, and which ones Cloudflare are planning to redirect.  Make sure this list is correct! 
  7. Click "Continue" once you're happy
  8. You then need to choose a plan - I was just testing so I went with Free website, but this will depend on what your website is used for and what level of service you require
  9. Click "Continue" when you've selected
  10. You'll then see a couple of new name servers - you'll need to go to your current web hosting platform and change the name servers over to the Cloudflare ones you've been given.
  11. Click "Continue" once you've done this
That's it, you're done.  Give it some time for everything to update, and you'll have an HTTPS version of your site ready to go!

You'll see a number of different sections within Cloudflare that are available to configure.


Overview

That's where you should arrive by default.  This gives you a summary of the status of your account.

Analytics

Initially there won't be anything in here, but over time it will start to show the traffic that Cloudflare has handled and how much of your own web hosting bandwidth has been saved by their caching.

DNS

This shows you the DNS records again, and the nameservers, and from here you can make changes if needed, depending on your plan level.

Crypto

This is where the HTTPS magic happens!  By default it is set to "Full", which is good, and will give you the nice green "Secure" message in the address bar (on Chrome, at least).  You can also enable HTTP Strict Transport Security (HSTS) and Automatic HTTPS Rewrites, both of which I would definitely recommend to help ensure users find and continue to use the HTTPS version of your website.  For HSTS you get some sub-options, including the Maximum Age (max-age) which they recommend 6 months, which sounds about right to me.  You can also apply the setting to subdomains, preload (if you wish, but I haven't) and and the "no-sniff" header (X-Content-Type-Options: nosniff), which I would recommend.

Firewall

This section can be used to try and filter out "bad" traffic.  You can do things like rate limit per IP address, and change the challenge page settings, which is where a user gets sent if they are determined as "bad", so they get a chance to prove themselves to actually be "good".  I left all of this as default.

Speed

Here you get the option to do things to speed up your website, such as minifying (I selected Javascript, CSS and HTML).  A lot of these options require a paid plan, but it's worth having a look and seeing what works for you.

Caching

You can set a few different cache options in here, but there are also a couple of really important buttons in here.
  1. Development Mode - if you are in the process of making and testing changes to your website, you're going to want to enable this, so that the cache is bypassed.
  2. Purge Cache - once you've made the change to your website, you're going to want to then purge the Cloudflare cache, to ensure the changes come through, before you disable the Development Mode.
Page Rules

This allows you to change certain settings for different pages, so you can make exceptions.  I've not set any of these, but I think this could prove really useful moving forwards!

Network

Here you can make certain network decisions, such as whether web sockets should be able to link to your original server, and IPv4 vs. IPv6, etc..

Traffic

Here you can control and manage your traffic, as well as seeing any firewall events that have been triggered.

Customize

This allows you to change certain error messages (such as HTTP 429 - Too Many Requests), but none of the options here are available on the free plan.

Apps

You can enable extra functionality here, including...
  • A Better Browser - warns users if their browser is old
  • Google Analytics
  • Google Webmaster Tools
  • Infolinks - monetize your site with ads
  • New Relic Browser - provides insight into the experience your site users are having
  • OpenDyslexic - overrides site fonts with OpenDyslexic
  • Trumpet - lets you put a dismissable message at the top of your site
  • VigLink - helps you earn money from your site's outbound links automatically
Scrape Shield

Cloudflare can scrape your content in order to make certain changes, to help protect you.  For example, it can obfuscate email address, and prevent hotlinking.  


And that's it, all done!  You're ready to go, and they've done all the hard work for you.

There's also an API for all of these settings, so you can make changes automatically if you want to.  However, I've not looked at this side of things yet. 



So now I can run my newly secure website through securityheaders.io and see how I'm doing.  And I get an A!

The only reason that I don't get an A+ is that I'm missing is HTTP Public Key Pinning (HPKP).  This is generally a good thing to do, but can cause problems when using Cloudflare as you're never sure when they're going to update the certificates.  They've suggested waiting until they implement this natively, so I'm going to go with that advice.  To be honest, I don't have a login page or any ecommerce, so no one's going to gain anything by doing a rather complicated attack on my certificates, so there's no risk here for me.

From my experience, I can certainly recommend Cloudflare to anyone, especially if they're looking to make their website secure.

    Wednesday 8 March 2017

    Response headers - adding Content-Security-Policy

    I recently wrote an update as I continue to work on my response headers, in which I said that I was working on adding content-security-policy, with the help of Scott Helme, who has written a great blog post on this.  He has also created an excellent site called report-uri.io which has a number of tools, including one to help you build your CSP.

    As a refresher, this is used to control what access different content on your site has.  For example, you can control what javascript and stylesheets can be included, inline or from different domains, etc.  This can help to limit the attack surface for things like cross-site scripting attacks.

    So there are lots of parts, but here's how I went about it...


    Default Source (default-src)

    This is the default setting, so to be really secure, I went with "none".  This essentially means don't allow any content at all.  It is then overridden for each content type explicitly.

    Script Source (script-src)

    This is applied to all the javascript files and script tags on the page.  I started with "self", as I wanted everything from my site ("www.rik.onl") to work.  Then I looked through my code and added in the CDN domains that I was using to load the libraries I'm using... "https://code.jquery.com https://maxcdn.bootstrapcdn.com https://cdnjs.cloudflare.com" (space delimited).

    Style Source (style-src)

    This is applied to all stylesheet files and style tags on the page.  Again, I started with "self", and then needed to add "https://maxcdn.bootstrapcdn.com".

    Image Source (image-src)

    You guessed it, this is applied to all the images on the page.  This time I needed "self" and also "https://www.gravatar.com".

    Font Source (font-src)

    I don't load any fonts from my own site here, so I just need to add "https://maxcdn.bootstrapcdn.com".

    Connect Source (connect-src)

    This is applied to things like AJAX requests, web sockets and event sources.  I don't have any of these currently, so I've left this out, which means it will default to the Default Source, which is "none".  I could also explicitly set this to "none".

    Media Source (media-src)

    This is applied to audio and video tags.  I don't have any, so I've left this out.

    Object Source (object-src)

    This is applied to object, applet and embed tags.  I don't have any, so I've left this out.

    Child Source (child-src) and Frame Source (frame-src - deprecated)

    These are applied to frame and iframe tags.  I don't have any, so I've left these both out.  You should use Child Source instead of Frame Source though, as the later is deprecated.

    Worked Source (worker-src)

    This is applied to things like service workers, which I don't have, so I've left this out.

    Frame Ancesters (frame-ancesters)

    This is applied to frame and iframe tags, stating which parents may embed a page.  It replaces the "X-Frame-Options" header, but again, I've left it out.

    Form Action (form-action)

    This is applied to form tags, stating which location they can post to.  I don't have a form, so I've left it out.  In case you've forgotten, this means it defaults to the "Default Source", which is "none".

    Upgrade Insecure Requests (upgrade-insecure-requests) and Block All Mixed Content (block-all-mixed-content)

    My site currently runs on HTTP (non-secure), so I've left these out, but I'll definitely be looking to add them in moving forwards.

    Reflected Cross-Site Scripting (reflected-xss)

    Reflected cross-site scripting is bad!  I've set this to "block".  You can set this to "filter", which means the browser will try to cleanse the script, but I think it's safer to block it completely, if XSS is detected.

    Manifest Source (manifest-src)

    This is applied to manifest files, so I've set this to "self", as I have a manifest for icons.

    Plugin Types (plugin-type)

    This tells the browser which plugins they can invoke, such as the Adobe PDF Viewer.  As I don't think my site needs any, I've left this out.

    Referrer (referrer)

    This directive tells the browser when (and when not) to send the referer [sic] header when fetching content from another domain.  In my last blog post I decided to set the "Referrer-Policy" header to be "no-referrer-when-downgrade", and so I've set this directive to match that.

    Report URI (report-uri)

    This is where the browser will post violation reports to, so you can keep an eye on what content is breaking.  This could be because you've mis-configured your policy, or it could be because someone is trying to hack your site!  Scott Helme's excellent site report-uri.io will allow you to configure a URI which you can set here.


    So now it's ready for testing!  

    To test, instead of setting "Content-Security-Policy", set "Content-Security-Policy-Report-Only".  This means that the browser will report all of the violations, but will continue to load the content anyway, which is perfect until you're sure it's right.  

    This is what I came up with...

    default-src 'none'; script-src 'self' https://code.jquery.com https://maxcdn.bootstrapcdn.com https://cdnjs.cloudflare.com; style-src 'self' https://maxcdn.bootstrapcdn.com; img-src 'self' https://www.gravatar.com; font-src https://maxcdn.bootstrapcdn.com; upgrade-insecure-requests; block-all-mixed-content; reflected-xss block; manifest-src 'self'; referrer no-referrer-when-downgrade; report-uri https://rik.report-uri.io/r/default/csp/reportOnly;

    I then reloaded my site, checked the console, and saw a number of violations!

    This is because I'd forgotten a couple of included scripts that are loaded by Google Analytics.  In fact, they also use an inline script tag, but I really don't want to add "unsafe-inline" to the Script Source, otherwise all inline scripts could be run.  

    Luckily you can fix inline script and style tags that you want to allow by calculating a SHA-256 hash of the contents and including this in the Script Source.  And Scott's got a hash generator tool as well, so that's easy.

    Having fixed these problems, and reloading my site with no violations reported, I'm now ready to start enforcing.  This means that I need to set the "Content-Security-Policy" header, and if you're using report-uri.io then you need to update your Report URI directive as well.

    Note that if you want to support IE11, you also need to set "X-Content-Security-Policy" to the same value.  It's not supported in older versions of IE at all.

    Now that I've added these extra headers, you can check out my live report.  Last I checked, I got an A+ 😃

    Saturday 4 March 2017

    Response headers - an update

    I previously wrote about what response headers I was sending back from my website - now I have an update.

    Part of the problem was that extra headers were being sent, which I didn't particularly want to be sent.  So I've been working on getting rid of them.

    X-Hostname

    I believe this is added by my web host, but I managed to remove it by modifying my .htaccess file with the following...

      Header unset X-Hostname

    X-Powered-By

    This is added by PHP, but it was easily removed by modifying my php.ini file with the following...

      expose_php = Off

    Server

    I could not change this, unfortunately, due to the fact that I'm currently using shared hosting, and therefore don't have access.  But for others, whilst it can't be removed, it can be changed to minimise it's output, by adding the apache directives...

      ServerTokens ProductOnly
      ServerSignature Off

    So now I've tidied that up a bit, I wanted to look at what else I should be adding.  I found an excellent site for this by Scott Helme called securityheaders.io.  You simply scan your site, and follow the advice it gives you.  

    It warned me about the "Server" header, but I've already worked out I'm going to have to live with that.  Other headers it suggested that I add included...

    Referrer-Policy

    This is used to define what referrer information gets sent when someone clicks on a link on your site that goes to another site, or even a page within your own site.  Scott Helme has written a great blog post on this, in which he recommends going with "no-referrer-when-downgrade", which sounds good enough for me.  My site is currently shipped over HTTP, but when I move it to HTTPS (yes, this is the plan!) then it will ensure referrer information isn't passed on to HTTP sites.

    Content-Security-Policy

    This is used to control what access different content on your site has.  For example, you can control what javascript and stylesheets can be included, inline or from different domains, etc.  Again, Scott Helme has written another great blog post on this.  He has also created an excellent site called report-uri.io which has a number of tools, including one to help you build your CSP.  This is a bit more involved though, so I think I'll cover this in a separate post.