CRX Access Control Basics

CRX is a content repository built by Day Software, now Adobe. It's a fascinating product, consisting of a few major pieces of software working together to provide a whole new way of building web apps.

At its core is Jackrabbit, a Java content repository with no web interface. On top of JackRabbit is Sling, a web framework that uses JackRabbit for storage and processes web requests using various scripting languages.

One of the great features about Sling is the ease of which you can build content driven applications, with little or no coding. On a default CRX installation, you can create an HTML form that will POST content right into the repository without having to write a line of code on the server. Sling's tagline is "Bringing Back the Fun", and it seems fitting.

But this post (actually this series of posts) is about security. On the Internet you don't want users POSTing content right into the repository without some careful consideration of the contents of that content and where it's being stored.

Let's take a look at the CRX blog example.

I'm going to assume for this post that you've installed CRX and you have run through the blog example and have it working on a CRX server on localhost. If you haven't run through the blog example, here's the end result - the blog app and content - as a CRX package that you can import through Package Share:

CRX gives you a lot of features for free - for example, a JSON interface to retrieving content. With the blog sample installed on a CRX instance running on localhost, this link shows the blog posts for March 2010; this link shows the same content in JSON format. And this link does the same for XML.

Another feature you get for free is the ability to manage content using a REST interface, through the SlingPostServlet.

With no extra coding or configuration, you can post new blog entries to the server, update them or remove them. For example, here's how you'd post a new blog entry (using curl):

curl -v -Ftitle="3rd March 2010" -Fbody="It's lovely today." http://localhost:7402/content/myBlog/2010/March/03March2010

If you're not familiar with curl, it's a simple command-line tool that lets you submit HTTP requests. In this case you're asking curl to POST a form with two values, 'title' and 'body', to the URL supplied. -v just asks it to be verbose, so you can see the header values.

This will post a new blog entry into March 2010. You can see the Location: header in the curl output that shows the location of your newly posted content:


Want to remove a blog post? No problem, with :operation=delete.

curl -v -F":operation=delete" http://localhost:7402/content/myBlog/2010/March/03March2010


Obviously we want to lock this down - we can't have random Internet users deleting our blog posts. Use the CRX Content Explorer to access the User Manager, and add a new user. Call the user 'blogger', password 'password'. We'll use this as the user that's allowed to post to the blog, and lock the system down so it's not modifiable by anyone else.

To do this, in Content Explorer, select the /content/myBlog node. This is the root of the content for the blog, and is a good place to lock the whole thing down. On the tool bar, click Security, then Access Control Editor.

The ACL editor shows you that there are no "local" access control policies, so the "effective" policy is inherited from the root. A default CRX installation grants "everyone" the right to do anything except edit the access control lists.

A nice feature of CRX is the ability to test ACLs. In the ACL editor, click the Test button. You'll see a page that shows the effective privileges for the node you were editing, with some controls at the bottom for picking a path, and picking a user.

With a path of "/content/myBlog" and a principal of "anonymous", you should see this:


Anonymous is way too powerful. Use the ACL editor to remove everything but jcr:read by going back to the ACL editor, clicking the checkbox next to the ACL policy in the list at the top, then click Set selected policies. Click New ACE, and create an entry to deny jcr:all to anonymous. Now create an ACE to grant jcr:read to anonymous. Apply, then test.


With this change, a link to a blog post should work fine, but attempting to use curl to make a new post should fail:

// this should fail with an HTTP 403 response (Access Forbidden)
curl -v -F":operation=delete" http://localhost:7402/content/myBlog/2010/March/03March2010

This keeps everyone from posting content - even the blogger. That's no good, so let's create a blogger user (call it "blogger", password="password"), and grant blogger write access to /content/myBlog.


Now, if we attempt to delete while logged in as the blogger user:

curl -v -F":operation=delete" http://blogger:password@localhost:7402/content/myBlog/2010/March/03March2010

The operation succeeds.

> POST /content/myBlog/2010/March/03March2010 HTTP/1.1
> Authorization: Basic YmxvZ2dlcjpwYXNzd29yZA==
> User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3
> Host: localhost:7402
> Accept: */*
> Content-Length: 151
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=----------------------------41e17a7038f6
< HTTP/1.1 100 Continue 
< HTTP/1.1 200 OK 
< Connection: Keep-Alive 
< Server: Day-Servlet-Engine/4.1.12 
< Content-Type: text/html;charset=UTF-8 
< Date: Thu, 09 Jun 2011 03:22:56 GMT 
< Transfer-Encoding: chunked  

Another feature of the blogging app is commenting. From any page within the content hierarchy, you can link to a comment form that will let you write a comment. The comment will be uploaded to the server and placed alongside the blog entry.

The example shows how easy it is to add this feature, but there are a number of security holes opened by allowing anonymous users to post content directly into the repository. When we locked down the site so that only the blogger could post content, we also locked out commenters.

In the next post, I'm going to talk about how to allow anonymous users to post content in a safer manner.