So my Appalachian Trail hike finally feels real, up until now it had just felt like an idea but now with
everything on my todo list ticked, it’s actually going to happen.
Last Friday was my last day of work for a good 6 months, tomorrow I hand back the keys to my flat and then
bright and early Wednesday morning I’m off to Atlanta. I’ve then given myself a day for last minute panicking -
especially if I find my tent and backpack aren’t waiting at the post office as agreed - before heading up the
AT Approach Trail up Springer Mountain on Friday.
In preparation of the many posts with pictures of trees and maybe even a bear, I’ve tweaked my site design. The blog is now
front and centre and there’s a new hike map menu item where you can see my progress along the trail
(or an exciting trip to Stretford if you check it before I’ve cleared the test data).
So today after 11 years of working at Mediaburst I handed in my notice.
It’s been a great place to work - that’s why I’ve been there so long - but since they were taken over a year
ago things just haven’t been the same.
So around October last year I started thinking about what should come next and stumbled accross an
AMA
on Reddit
by a guy who’d just finished walking the full length of Appalachian Trail
in America. My initial thought was something along the lines of
“How the hell does anyone find a spare 6 months just to go for a walk?” and I thought nothing much more of it at the time.
However at the back of my mind the thought kept popping up of how interesting it sounded to go on a stupidly long walk. And,
to cut a long story short, by early November I’d somehow convinced myself that I wanted to go on a 2,200 (ish) mile walk.
So in early December I headed off to London to ask the US Embassy very nicely if they’d give me a B2 (tourist) Visa -
and they did :)
In mid March I’ll be jumping on a plane to Atlanta, Georgia, for day 1 of 6 or 7 months walking.
So here’s a few of the questions I’ve been asked so far…
What’s the Appalachian Trail?
Given that most people have never heard of it this seems a good place to start.
The Appalachian Trail is a national scenic trail in the USA. It’s a touch under 2,200 miles long and runs from a
car park just north of Atlanta in Georgia, to a mountain in the middle of nowhere in Maine
(I looked on a map and didn’t recognise anywhere nearby).
What’s a thru-hike?
Thru-hiking is where you start at one end of the tail and carry on until you reach the other end. It doesn’t matter
which end you start at - although I’m going to follow the trend and head north.
Why?
I don’t really know how to answer this one. After 11 years in the same job I fancy something new and sheer laziness
has meant I’m not a homeowner yet so this seems like a good time. Quite how that something ended up being a
2000+ mile walk is beyond me.
So back in March I posted about how I’d had an idea,
I then forgot about it and didn’t post anything else. That idea, unfortunately, didn’t go anywhere :(
I’d applied to British Antarctic Survey
to work as a wintering communications manager - that would’ve been around 18 months on an Antarctic research station
as a radio operator and general I.T. dogsbody. Unluckily for me there were a couple of guys with more relevant experience than
me in the final interview round and they’re now busy freezing at the South Pole instead.
So moving on to a more positive note, and the new idea mentioned in my title:
I’m off for a walk (quite a long one). Futher details to follow.
ASP.NET 5 and MVC 6 are currently release candidates so the information in this post may change.
Gettting Ninject working with ASP.NET 5 (also known as vNext) is really simple. Unfortunately it took me the best part of a day to to work this out as a lot of the documentation is either a work in progress or out of date (beta 4 renamed quite a few packages and methods).
The ASP.NET framework now supports DI out of the box in the Microsoft.Framework.DependencyInjection package. However if you want to use Autofac or Ninject - maybe because you already use them throughout the rest of your project - you’ll need an adapter.
Standard Ninject binding
The Ninject adapter package isn’t yet available on NuGet so you’ll need to add the ASP.NET 5 “master” branch feed from MyGet to your Visual Studio NuGet package sources.
In Visual Studio under Tools → Options → NuGet Package Manager add the feed
https://www.myget.org/F/aspnetmaster/
Add the Microsoft.Framework.DependencyInjection.Ninject and Ninject packages to your project.json file (or use NuGet).
Set up Ninject in Startup.cs
// You probably need to change this return type - defaults to voidpublicIServiceProviderConfigureServices(IServiceCollectionservices){// Add your various services such as MVC as normalservices.AddMvc();// Create a new Ninject kernel for your bindingsvarkernel=newStandardKernel();// Set up your bindings for DIkernel.Bind<IFakeRepo>().To<FakeRepo>().InRequestScope();// Add all the ASP.NET services to your Ninject kernelkernel.Populate(services);// Use Ninject to return an instancereturnkernel.Get<IServiceProvider>();}
Convention based binding
At work we tend to use Ninject convention based binding as it saves manually listing all the Interface/Repository combinations (it can get a bit tedious). You essentially tell Ninject what namespaces to look in and it will bind all classes to their default interfaces.
Add the ninject.extensions.conventions NuGet package
Set up convention based binding in Startup.cs
publicIServiceProviderConfigureServices(IServiceCollectionservices){services.AddMvc();varkernel=newStandardKernel();// This bit is the convention based binding// // Any repository will do here it's just to find the correct DLL.kernel.Bind(k=>k.FromAssemblyContaining<FakeRepo>().SelectAllClasses()// List multiple types here to look in more than one namespace.InNamespaceOf(typeof(FakeRepo)).BindDefaultInterfaces().Configure(c=>c.InRequestScope()));kernel.Populate(services);returnkernel.Get<IServiceProvider>();}
Last year while porting a PHP web app to .NET I found that none of the existing users could log in although both sites used bcrypt for password hashing.
This is the first of two posts digging in to the issue, starting with what was wrong with the hashes and moving on to how to work around it.
Up until version 5.5 PHP was distinctly lacking a simple idiot proof password hashing function (it now has password_hash). This meant every developer ended up rolling their own based on the crypt documentation.
In the case of the project I was porting it used the following password hashing function (with a few bits removed for clarity).
The $2a$ string at the beginning of the salt instructs crypt to use blowfish hashing (bcrypt) and then the 10 is a cost factor, in this case the blowfish hash will be run 210 times.
It then expects 22 (not 21 as generated in the example above) characters in the range “./0-9A-Za-z”, anything else is an invalid salt that should cause the hash to fail.
It turns out on PHP 5.3 if you pass in an short salt such as the one generated above that consists of 21 valid characters and then a $ symbol it will return a hash anyway. The problem being these hashes won’t work with other implementations and I had a few thousand paying customers who wouldn’t be able to log in once the rewrite went live.
I’m not sure how the person who wrote the password hashing function ended up with the salt a character short with an extra $ on the end, I can only assume he mistakenly thought there should be a $ between the salt and hash, whereas in actual fact the fixed length salt makes any sort of separator unnecessary.
What was PHP getting wrong?
The PHP documentation states “Using characters outside of this range in the salt will cause crypt() to return a zero-length string”, given the string wasn’t zero length this obviously required more investiation.
A bit of experimenting didn’t find anything obvious so there wasn’t really much option other than diving in to the PHP source. The relevant files are crypt.c and crypt_blowfish.c in the /ext/standard directory.
Nothing jumped out in crypt.c but crypt_blowfish.c contains a a couple of lines with the comment /* PHP hack */ which instantly set alarm bells ringing and gave me somewhere to focus.
First there’s the BF_safe_atoi64 macro that converts ASCII characters in to integers, if it encounters a $ symbol it breaks out of the calling loop.
There’s also BF_decode which is used to convert the ASCII salt to bytes. This uses the BF_safe_atoi64 macro above to convert the characters of the ASCII salt in to bytes and then zero pads the resulting salt out to the required length if it’s too short.
And that looks like the caues of the problem. The $ at the end of the salt is turning in to a zero byte.
Confirming this might’ve involved attaching a debugger and steping through the code, however it’s over 10 years since I last did any C programming and it was taking 10’s of minutes each time to build the PHP source, so I cheated and extracted the relevant parts of the PHP source in to my own source files. I then threw away any references to other hashing algorithms, removed a load of C pre-processor directives (easier than working out where they’re defined) and hacked together this simple test program along with adding a simple printf to each instance of /* PHP Hack */.
You can download the full source if you’d like to take a look. Adding a printf on each instance of /* PHP Hack */ gave the following output:
BF_safe_atoi64 PHP hack
PHP Hack padding binary salt with 0
Returned 0, Salt: $2a$10$Z2RM.D9GFLcbORGVkI07s$, Hash: $2a$10$Z2RM.D9GFLcbORGVkI07s.1H0hAeFXoZloOhosUbIEt6P0nDv0Ih6
So we’ve hit the $ character when converting the ASCII salt to integers and added a single zero byte to the end of the salt giving us an invalid hash.
To conclude
While the hashes were being validated in PHP this wasn’t a problem, PHP crypt always bodges the salt in the same way so comparisons worked as expected. However every other bcrypt implementation I could find bombed out when it found invalid characters in the salt preventing users from logging in.
With the bug now traced I could try to reproduce it in C# and allow existing hashed passwords to be validated, I’ll cover this in a follow up post soon.
As an aside, I’ve no idea why the PHP bcrypt implementation has these hacks to allow invalid salts. There’s an existing bug about how salts are handled when they’re too short, but it’s been floating around since July 2012 without any comments.
Going forward this shouldn’t catch people out so much as the new password hash function generates a correct salt for you.