Apache Roller security model

About this document

  • Version: this model describes the Apache Roller 6.1.x release line.
  • Purpose: it tells operators what Roller protects and what it leaves to them, and tells security researchers what counts as a vulnerability.
  • Reporting: report a suspected vulnerability privately to security@apache.org, as described in SECURITY.md.
  • Vulnerabilities: an issue is a vulnerability when an untrusted user crosses one of the boundaries below in a supported configuration.
  • Questions: ask about the model on the Roller dev list, but never post details of a suspected vulnerability there.

The one rule

Roller is one server that hosts many weblogs for people who do not necessarily trust each other. The whole model follows from one rule:

What you may do in one weblog gives you nothing in another weblog, and nothing on the server.

Only two parties are trusted: the global admin, who runs the site, and the operator, who runs the server. Everyone else is a possible attacker. That includes the admin of a weblog.

Principles

Every change to Roller must keep these rules:

  1. Rights in one weblog give nothing in another weblog or on the server.
  2. Weblog admins are untrusted. Only global admins and the operator are trusted.
  3. Every object named in a request is checked against the weblog the action runs on.
  4. Content that users write never runs as script for other users.
  5. Templates run in a sandbox. They cannot call arbitrary Java code or read files outside their theme.
  6. Request data is data. Roller never runs it as code, and it never resolves external entities in XML it receives.
  7. Every state-changing request from a logged-in user needs a one-time token (the salt).
  8. Users below global admin cannot make the server fetch a URL of their choice.

What Roller is made of

Public, no login Logged-in users Global admins First run only fetches pings Weblog pages, feeds, search Comments Authoring UI (/roller-ui/authoring) Remote publishing APIs XML-RPC, AtomPub Admin UI (/roller-ui/admin) Planet feed aggregator Ping targets Installer + setup token Bundled themes and templates Database, media files, search index Network

Everything in the Roller WAR is in scope, including the bundled themes. Build and test tooling (db-utils/, it-selenium/, testing/, docker/, assembly-release/) is not part of the product and is out of scope. The Docker files are examples for development, not a supported deployment.

The users

Roller has two layers of roles:

  • Global roles apply to the whole site.
  • Weblog roles apply to one weblog. A person can hold a different weblog role in each weblog they belong to, or no role at all.
Outside Roller Site-wide: global roles Inside one weblog: weblog roles creates first admin at setup registers, if allowed creates a weblog is invited to a weblog is invited to a weblog holds every weblog role<br/>in every weblog Operator container, database, config, logs TRUSTED, out of scope Global admin global role 'admin' TRUSTED Registered user global role 'editor' UNTRUSTED Anonymous visitor no account UNTRUSTED Admin permission 'admin' UNTRUSTED Author permission 'post' Limited permission 'edit_draft'

Weblog roles stack: an Admin can do everything an Author can, and an Author can do everything a Limited member can.

UserCan doCannot doTrusted?
Anonymous visitorRead published entries, feeds and search; post comments; register, when registration is onSee drafts, settings or anything unpublished; change anythingNo
Registered user (global editor)Log in; edit own profile; comment; create weblogs, when the site allows itAct in a weblog where they hold no roleNo
Limited (edit_draft)Create and edit draft entries in that weblogPublish; manage comments, categories or media; act in other weblogsNo
Author (post)Everything Limited can do; publish entries; manage comments and categories; manage media, when uploads are onChange weblog settings, members, bookmarks, pings or designNo
Admin (admin)Everything Author can do; weblog settings, members, bookmarks (including OPML import), pings, maintenance; theme and templates, when custom themes are onTouch other weblogs; read server files; run Java code; read other users' private data; run script in other users' browsersNo
Global admin (global admin)Everything in every weblog; users; site settings; Planet feeds; ping targetsGet shell or file access on the hostYes
Network attackerRead or change traffic when the site runs over plain HTTPAnything, once the site runs over HTTPSNo
OperatorOwns the container, JVM, database, configuration files and logs—Yes, out of scope

Why the weblog Admin is untrusted

This is the most important line in the model. On a multi-user site, anyone who can register can create a weblog and become its Admin. If weblog Admins were trusted, every registered user would be trusted.

Roller's defaults reflect this. weblogAdminsUntrusted=true turns on HTML sanitizing, and templates run in a sandbox built for untrusted authors.

A request forged cross-site that makes a logged-in user's browser act is an attack by an anonymous visitor, not by that user.

Where untrusted data enters

InputWho controls it
Any request to public pages, feeds and searchAnyone
Comments: name, email, URL, textAnyone
Entry text, titles and summariesWeblog members
Categories, bookmarks, profiles, weblog settingsWeblog members and Admins
Templates and stylesheetsWeblog Admins, when custom themes are on
Uploaded files: content, name, declared typeWeblog members, when uploads are on
OPML bookmark importWeblog Admins
Remote API requests (XML-RPC, AtomPub)Anyone, before authentication
Feeds fetched by PlanetWhoever runs the feed's host
Responses from ping targetsWhoever runs the ping target
Installer formsAnyone, until setup is complete

Only the operator controls roller-custom.properties, JNDI resources and the database.

The four boundaries

① request data ② allowed ② blocked ③ HTML, templates, uploads ④ blocked ④ allowed Visitor / commenter Public pages, comments, search Member of weblog A Weblog A Weblog B Weblog Admin Sanitizer, sandbox, media policy Pages that every visitor's browser runs Any user below global admin Network Global admin
  1. Visitor → server. All request data is untrusted. Roller escapes what it writes back into pages, never runs request data as code, and reads XML as data only. One request that causes runaway CPU or memory use (for example catastrophic regex backtracking) is a vulnerability. A slow response is not.
  2. Weblog → weblog. A role in weblog A gives nothing in weblog B. This includes every object named by id in a request: entries, comments, media, templates, categories, bookmarks and members. A global admin holds every role in every weblog.
  3. Weblog author → everyone else. Content that members and Admins write runs in other people's browsers. Roller keeps it from attacking visitors, other users or the server:
    • HTML in entries, profiles and settings is sanitized when weblogAdminsUntrusted=true.
    • Comments are always escaped, or limited to a small HTML subset. Comment links and author URLs must be http or https.
    • Templates run in the sandbox.
    • Uploaded files are never served as active content (see below).
  4. Server → network. Users below global admin cannot make the server fetch a URL of their choice. Entry enclosure URLs are stored, not fetched. Global admins can point the server at any URL through Planet feeds and ping targets. That is by design.

Two more rules protect the whole site:

  • Accounts. Passwords are stored as bcrypt hashes. Roller takes the user‘s identity from the login, never from request data. A password change ends the user’s other sessions.
  • Setup. Before setup, the installer needs a one-time token that Roller writes to the server log. Once setup is complete, the installer is closed for good.

How a request is checked

Every request to the authoring UI passes these checks in order. A missing check creates a predictable class of bug.

no yes no yes no yes no yes Request to /roller-ui/authoring/... Logged in with a global role? Redirect to login Holds the required role in THIS weblog? Denied (missing: privilege escalation) Does every object named in the request belong to this weblog? Denied (missing: cross-weblog access) Valid one-time salt on a state change? Rejected (missing: CSRF) Action runs

The remote APIs (XML-RPC, AtomPub) authenticate with a username and password instead of a session and salt. They apply the same role and object checks.

How content is kept safe

Written by untrusted users Entry HTML, profiles, settings Comments Templates Uploads HTML sanitizer (weblogAdminsUntrusted=true) Escaped, or HTML subset; links http/https only Velocity sandbox: no arbitrary Java calls, no reads outside the theme Type taken from file name; active types never inline; nosniff Page sent to visitors

Uploaded files get their type from the file name, not from what the browser claims. Only passive types such as images, audio, video, PDF and plain text are shown inline. Everything else, including HTML and SVG, is sent as a download.

Settings

Optional features: supported when enabled Unsupported on a multi-user site XML-RPC AtomPub Uploads Custom themes Self-registration HTML comments LDAP / OpenID login Remember-me Planet aggregator weblogAdminsUntrusted=false authentication.method=cma passwds.encryption.enabled=false

A bug in an optional feature is a vulnerability when the feature is enabled.

SettingWhat it changes
weblogAdminsUntrustedtrue (default) sanitizes HTML from weblog members. Set false only when every weblog admin is trusted, such as on a solo blog.
themes.customtheme.allowedLets weblog Admins edit templates. Templates stay in the sandbox.
uploads.enabledLets members upload media files.
webservices.enableXmlRpc, webservices.enableAtomPubTurn on the remote publishing APIs. They use the same weblog roles as the UI.
users.registration.enabledLets anyone create an account.
users.comments.htmlenabledAllows a small HTML subset in comments.
authentication.methoddb, ldap, openid or db-openid. cma is not usable.
rememberme.enabledKeeps users logged in with a cookie. Use only over HTTPS.
passwds.encryption.enabledfalse stores plaintext passwords. Not supported.

Libraries

Roller decides how it configures and calls each library. A library bug hit while Roller uses the library correctly belongs to that library's project. Roller passing untrusted input to a library in an unsafe way is a Roller bug.

Not vulnerabilities

Roller does not protect against:

  • The operator or host, or anyone who can edit the configuration or the database.
  • A global admin misusing the admin UI. A request forged against a logged-in global admin is still a vulnerability.
  • Plain HTTP. TLS is the operator's job.
  • Floods, brute-force login attempts and spam. Roller has no rate limit or login lockout. The comment math question is an anti-spam measure, not authentication.
  • Browsers out of vendor support. Script that runs only in such browsers (for example old Internet Explorer modes) is not a vulnerability. A payload that also runs in a current browser is.
  • Forged anonymous comments. A comment forged cross-site carries no identity, so it gains the attacker nothing.
  • Unsupported settings, listed under Settings above.
  • Partial writes when a multi-step operation fails. That is a bug, not a vulnerability.

Operator checklist

  • Serve Roller only over HTTPS, and set the session cookie Secure flag in the servlet container. Roller sets HttpOnly but not Secure.
  • Keep weblogAdminsUntrusted=true unless every weblog admin is trusted.
  • Enable custom themes, uploads, XML-RPC and AtomPub only when you need them.
  • Restrict access to the server log. It holds the setup token during installation and upgrades.
  • Keep the database, data directories and roller-custom.properties private.
  • If the server can reach internal services, put egress rules on Roller's outbound traffic (Planet, pings).
  • Run the current release. Older releases are not patched.

Common mistakes

  • Setting weblogAdminsUntrusted=false on a multi-user site to allow rich HTML.
  • Enabling custom themes and assuming templates are harmless. They are sandboxed, but they run on every page view.
  • Running over plain HTTP with remember-me enabled.
  • Running setup on a host where other people can read the log.

Judging a report

Operator, or a global admin<br/>acting directly Anyone else, or a request<br/>forged against an admin yes no yes no yes no Report Who is the attacker? (see The users) Not a vulnerability Does it need an unsupported setting? Not a vulnerability Does it cross one of the four boundaries? Vulnerability Listed under Not vulnerabilities? Not a vulnerability Unclear: the PMC decides and updates this model