Forum OpenACS Q&A: RFC: Security policy for OpenACS (Security hole in OpenACS 5.1!)

A couple of days ago, Lars and I had a discussion on IRC about security and usability with OpenACS.

It turns out that OpenACS 5.1 was shipping with a default setting in the parameters section that allow any sort of HTML to be let through the security filters (to remove this, take out the *). This makes it trivial to completely take over an OpenACS server. I believe I could root a box in about 10 minutes with this security hole.

My basic argument is that the core of OpenACS should be secure by default, and that any packages that are insecure by design should be clearly marked as insecure.

Lars agreed that security was important, but thought that we too often sacrifice usability to obscure security issues. And he pointed out what a large usability problem our security filters have been in the past. Hopefully I've summed up your argument well, Lars?

In any case, I think our discussion (see brings up a larger issue of a security policy for OpenACS.

Perhaps we need both a security policy and some usability guidelines. To some extent, the conflict between usability and security can be alleviated by careful design, but it typically takes a lot more work to do so.

I personally would like to see our code held up to very high standards for both usability and security. But if we don't clarify what those standards are, and if they're worthwhile or not, we'll all have our own individual standards and no coherent strategy for OpenACS.

Any thoughts on this?

My proposal would be:

1) OpenACS *core* will be as secure as possible by default. Any changes that introduce a potential security hole have to be TIPped.

2) When security issues cause usability problems, we will make it clear to administrators how to lower the security, and what the risks are in doing so. (This should be in our usability guidelines).

3) Individual package writers should inform the public (via the forums) if they are aware of security holes in their software, so that concerned individuals can either fix it themselves, or not use that package.

Of course, if we had the manpower and the inclination (we don't), we could put stuff in the APM that reflected whether packages had been audited for security and against our usability standards. I don't see any volunteers for this, I believe?

You can 'root' a server running a default OpenACS install in ten minutes using a permissive HTML setting? That is clearly news. If this is true, wow! Can we get more information on this?

You're right, I overstated my case. I could only get the permissions of the Aolserver, which wouldn't be root.

Still that's the first step in getting root access. Once you have access to a user account, you have a lot of access. And there are a lot of privilege escalation bugs you could exploit to get root access from there.

I couldn't agree more with you Jade. I'd prefer the toolkit to be as safe as possible by default. Since when is "high usability" equal to "low security"? We need "high" of both.

Also there was a discussion on a similar topic (allowing IMG tags and SRC attributes in postings) over here:

I think you can execute arbitrary unix commands as the servers uid on 5.1 with the on a server with the permissive settings in about 10 minutes (well, really you need to wait until someone who has admin viewing a page with an IMG tag or a style attribute in it). You would have to find a local root exploit to escalate though (and such things are not as uncommon as remote root exploits).

What would lead to such access? How is the ability to execute arbitrary unix commands related to an admin account on OpenACS and the img tag? Is this a unique to OpenACS5.1 issue? If not I guess I'm going to toggle 'registration requires admin approval'.

Tom, Jeff explained it here:

Once someone has an admin account on OpenACS he could install acs-developer-support and and execute code via the its tcl shell feature.

This is not restricted to just 5.1, I believe...

Tom, the vector of attack is:

Post a comment in a visible place. Use HTML, and include an IMG tag that links to the admin page that grants site-wide-admin privileges.

Wait for the admin to visit that page. Now you have site-wide-admin privileges.

Now install developer-support, use the shell page, and you can execute arbitrary Unix commands, delete the database files and backups, etc.. You can also subtly change information on the webserver (since you have database access), and edit your posting, removing all trace of what happened. Then you can take away you privileges, and nobody would know.

Most OpenACS sites should not be vulnerable to this.

Toggling 'registration requires admin approval' won't help. You have to not allow all HTML tags. That's why that was put there in the first place. By default, it was very secure, but in OpenACS 5.1, it was made more lax.

So all OpenACS 5.1 installations (that weren't upgrades from something else) need to go into the parameters and remove the * from the HTML permitted section.

I've changed this in the 5.1 branch, so new installations from this date forward will be secure.

But beyond this, what are we going to do going forward?

On IRC, people are talking about making the admin  pages be in forms, because then you can sign the variables. This is a very good and important idea, but it needs volunteers, and as much as I'm bitching right now, I'm not volunteering :)

Would they have to be in forms?  Or couldn't you just send an expiring cryptographic hash of some kind in the URL variables?
The reason to make admin actions (and actions generally) forms is because you want to make them "POSTs" and have the page only accept posts. That will then prevent the involunatry "GETs" issued to retrieve images (via IMG OBJECT ... tags, and via style="background: url( ... )"). You can sign things in a get as easily as a post.

I do think we should add a -post flag to ad_page_contract which would throw an error if the page was not posted to.

I have been looking at what other people do and most of the php things use a sanitize script to remove dangerous things. We in fact already have something that does this but it's not really used (util_close_html_tags can be told to remove tags, remove tag content, etc). But it's slow and I think if we really did it we should return the sanatized html + a list of removed tags and attributes.

Okay, it is all coming back to me now. This is a problem with OpenACS, not the src attribute. If I was smart, I would just send a tempting email to all the admins:

Hey X,

Check out this link on XSS:

Then construct my unchecked src attriute on XSS.html

It seems the agreed upon solution is to require a tie between the form and the form processing script, and to require POST. I would add an 'I'm a human' test to certain actions as well.

Jeff, you once provided great links to open source tools for generating images to read, but I wasn't sure if the intent was to point out they are easily broken?? The nonce idea (or any tying method) would mostly eliminate simple attacks since it would require more than one request, but I'm starting to think that you could do something like


That is, two images loaded in sequence. At the very least I have noticed that Mozilla serializes frame source loading.

The reading images thing is CAPTCHA, and it's not trivial to break (although the simple ones you see on yahoo can be broken something like 80 or 90% of the time by a program and the hard ones people will get wrong some substantial fraction of the time).

The nonce would not be a cookie, it would be encoded in the url or in the form data so that two gets in a row would not matter.

It's also not accurate to say it's a "problem with OpenACS", at least not in the sense that it is somehow unique to openacs. You see similiar problems in IIS, analog, bug trackers, mailing list software, basically anything that takes user input and puts it into html at any point.

As for nonces, I do this on Carnage Blender--

confirmation page:
set security_key [ns_rand 1000000000]
nsv_set user_transfer_approval $user_id $security_key
# send $security_key with form

action page:
# read $security_key from form
catch {
    set t [nsv_get user_transfer_approval $user_id]
if {![info exists t] || $t != $security_key} {
    ad_return_error "Transfer not approved" "
    Go back and approve the transfer first.

I figure there's better odds than 1 in a billion that an attacker will find some security hole I haven't patched, so this is "secure enough" for me.

Anyway, if you limit this to POST (which I didn't think of), it seems to me you don't even need to bother with this, as long as you're having util_close_html_tags strip out FORM tags.  I'm reasonably sure you can't use javascript to POST w/o a FORM.  Certainly adding util_close_html_tags calls will be easier than adding code like mine here.  (Besides, it should be done anyway since users are stupid and don't, well, close their tags which is what that proc was originally for.)

This is a great solution, one I proposed for forms in general: pass some information along via nsv, which ties the form to the form processing script. I also recommend tying to a user session, which expires on a regular basis, but limiting to a certain user is a good first step.

What about adding some kind of "sudo" mechanism as well?

Currently a site-wide admin is always a site-wide admin. This is equivalent to using root on your Linux/Unix box all the time and do things like surfing the web.

Here is a quick idea: We could set a second cookie for all admin pages and that cookie expires after 15/30/60 minutes (apm parameter) and it determines whether you need to enter a password before being able to perform admin activities.

A real sudo and a root-like account would be cooler, but it may require a lot more changes.


Wow, that's a really good idea.

One challenge there will be to figure out when "sitewide admin" things are being done.  They are different than "package admin" things...

For "sudo" We could add something like "auth::force_authenticate" which would grant a short dated nonce to validate an action; it would not really even be that hard to implement but of course you would have to go through the code and apply it.

Jonathan's trick is essentially a nonce that expires when the server restarts. The downside is that relying on a shared variable like that means that if you have multiple frontend servers you need to sync these (whereas something signed with a random session cookie + a server secret + user_id would not need to be synced between the front ends, would survive server restart, could be made to expire by adding an expiry to the signed key, and would be equally secure).

Oh, one issue with making all the pages that do actions only accept POST is that if your login expires the whole redirect for registration relies on being able to do redirects to propigate state which of course result in GETs. That means if you fill out a form, send a post, it does a redirect to register with a return_url; signing in then does a redirect to the return_url which (because the target URL only accepts posts) fails.

It also means single page add-edit screens would need to be reworked since the initial link to the page would be a GET (i.e. you could only check it was a POST when you really went to modify state).

Anyway, there are a bunch of cases like that where the assumption is that GET == POST and fixing it in a way that is both secure and not horribly unpleasant to use is not-trivial I think.

One important note: OpenACS 5.1 and all prior versions to my knowledge does ship with a setting that causes it to only allow HREF, TITLE, NAME and ID attributes on HTML tags, so you can't do the image thing.

In fact, you can't post any images at all. And you can't use the htmlArea, because that will generate SPAN tags with STYLE attributes.

We really need something that can sanitize/secure posted HTML, as currently the only solution that will allow people to post images and use htmlArea, two very reasonable use-cases in my opinion, is to change the default setting to something more permissive.

A solution is really required. Any volunteers?


Jeff, let me clarify: the problem is with handling form input, not the src attribute. You can't clean the entire internet of malicious src attributes, that is just a 'feature' of HTML/HTTP. But OpenACS doesn't yet have a general method of handling the issue. Blocking usage of the src attribute will not fix the issue, it only limits it usage on a particular server. If you set your browser up to not accept images except from the same server as the web page, this will help a lot. But you need admins to do this always and in every browser they use, but this only works for images, probably not for frame/iframe/object tags. Admins also shouldn't load images in email messages by default.

This doesn't mean that these attributes couldn't be used for other badness. If a site like a slashdot allowed something like this, it could be used to spam another site with lots of hits, shutting down the site with a DOS type attack. So limiting the use of these attributes is still important in general.

So OpenACS didn't cause the root of the problem, and it can't really be solved by disallowing usage of certain attributes. But OpenACS can be fixed to make this kind of attack very difficult if not impossible.

No one has commented on the approach I took with my patch ( ) so I take it it's not a candidate for adding to the toolkit ... At any rate, it allows me to post images in blogs for instance on my personal site in what I think is a reasonably secure manner which could easily be made securer by simply extending the checks in ad_html_security_check. For instance, it would be easy to disallow external image references if off-site redirects back to your own site are a concern.

This approach is of course based on all forms being migrated to ad_form and ad_html_security_check being called with all form builder datatypes that are in use.

Is the suggested approach totally inadequate or what?

I have a blog/cms package which allows images in a safe way, probably you could apply it to OpenACS. It just allows special tags which construct the url of the local image, avoiding the issue of user supplied urls. The tag is included in the main message content, and the content is run through ns_adp_parse. For instance an image tag looks like: <image name="myimage" id="1"> and returns a formatted img tag.

In this case myimage is an attribute of the content item, the first one uploaded is given an id (really the sort order) of 1. If the image uploaded was named myjpg.jpg the src returned would look something like '/somepkg/files/[1234%23]/1234/myimage/1/myjpg.jpg' where /somepkg/files/index.vuh exists to get the actual content.

Isn't this the problem signed variables are supposed to solve and if not why not?

Sudo would be a very cool feature. How about a package (sudo) that keeps a table of urls to protect. Hook into the request processor so that if you go to one without an valid sudo session you are redirected just like the current session system. You could control the time with a package parameter. You could also specify that a url requires login even with a valid sudo session. The urls could have wild cards and package-keys.

package-key         url           single_P
     *                 */admin/*         f            would protect all admin pages
     *                 */permission    t           would require login to every access
acs-subsite      /admin*          f            would protect the subsite admin pages
sorry about the formating

This would allow sites to customize security policies without changing code just like sudo.

Once someone has an admin account on OpenACS he could install acs-developer-support and and execute code via the its tcl shell feature.


If acs-developer-support is such a powerful tool why make it available for install from the repository? Atleast if it was a local install only you would need access to the local file system before being able to install it.

    - Steve

Unfortunately signed variables are relatively easily forged. Here is a short summary of the problem I wrote before:

...there is nothing in place to insure that the signed variable sent back is the signed variable sent to the browser.

In ad_form for example the key is signed but the signature says:

<input type="hidden" 
   value="851 0 2D43063DA4AFDC46574E81151ED48C64939CF3AC" />
the 851 is the token_id and 0 is the expire time (does not expire) and the hash is:
ns_sha1 "$value$token_id$expire_time$secret_token"
Now, all you need to do to forge this is to get some other form to sign an integer (any form which lets you enter an integer which is subsequently served back signed would work) since there is nothing there tied to the session or a shared secret or anything like that.

Some ways to fix this would include having a shared secret (a random string in a session cookie for example) or a server side secret which is session specific (pick a token_id for the session but don't send it in the request).

One other thing I realized in looking at this is that as far as I can tell, we don't ever expire tokens which is probably a mistake. We should probably have a token lifetime parameter and sweep expired tokens.

Signed variables should be fixed then. It occurred to me that if (and perhaps it's stated above) it's not safe to allow users to input src tags to this website it's not safe to have src tags anywhere. For example if I create a img tag on my website with


And post into this forum a url to my site that says here's how to make acs secure. Then someone will click the link and be screwed.

also if the sudo package checked the refer header and caused forced a login if it did not match the current site it would help prevent dumb stuff from happening.

Barry, you are right: we can't fix the way the internet works. I'm not sure why an attacker would go to the trouble of using an image tag to break into OpenACS, given the ease it can be accomplished by tricking someone into visiting an offsite page. The offsite page could use every possible device to break in, not just a src attribute.

One way to fix it, mentioned by me before in this thread and others, is to tie the form to the form processing page. Forms should be tied to a session. Variables used in the form should be tied to the form. In the cases where ad_form is used, this might be possible without visible code changes to each page (where the default formtemplate tag is used), but I'm not a expert on ad_form.

Fixing the exploit code (developer support) isn't a long term solution. Someone could upload their own exploit code once they have admin access.

Barry, Tom,

URLs and ordinary links (posted on your site or from emails or elsewhere) do pose a potential danger to our sites if a privileged and logged in user
*visits* the link, but (as others have pointed out) there is a distinction between this and the IMG in so far that the IMG tag (and maybe some other tags) will cause an involuntary page request. So IMG tags ought to be viewed as more dangerous than most other tags, if only by just a fraction ...

I realize now, though, that by implementing my suggestion (the patch) and try to stop the problems at element validation time (before the data has reached the action part of the script)
there is nothing that stops "attacks" which originate from the outside world - only if the garbage is posted through one of your site's forms.

Thus I am prepared to believe that it *should* be fixed by a change to ad_page_contract and the
switch of all important admin actions to use forms which are POSTed, and nowhere else ...

The problem with post is if you get redirected thru /register because you session expires the update will break. That might be a feature. It also means you can't have links that perform admin type actions. Again that may be a feature.

The more I think about it the more I think sudo is the answer because it solves the real problem which is you don't want to run as root all the time. If I'm paranoid I could authenticate on every admin action without changing code. If I want to run as root all the time on my dev box that's ok too.

I looked at the code a bit and I think all you would need to do call sudo::checkauth in the request processor just before the other permission checks. If it's been too long since you authenticated redirect to reauthenticate and set a cookie that's a database key to the time.

You would also need a tcl interface so on package installs you could set defaults

If each entry in the sudo table is an object you could have even more control. For example I could create a /manage directory under a package and control access just like /admin

Yeah, the real problem isn't 'run(ning) as root all the time', I'm not sure what that even means. But whatever someone can do because of a vulnerability isn't the problem, just the potential effect of not fixing the problem.

The problem is with the protocol which allows actions to be performed without the user's knowledge. But OpenACS can be fixed to make the vulnerability mostly go away. OpenACS cannot be 'fixed' to prevent an admin from installing a package which does whatever they want it to do. This is a feature, and it can't be prevented.

I created an sudo package and added this to request-processor-procs.tcl
    # Make sure the user is authorized to make this request.
    sudo::checkauth -url [ad_conn extra_url]

    if { ![empty_string_p [ad_conn object_id]] } {
      ad_try {
        switch -glob [ad_conn extra_url] {
            admin/* {
              permission::require_permission -object_id [ad_conn object_id] -pri
vilege admin
wrote this function
namespace eval sudo {
        ad_proc checkauth { -url } {} {

                ns_log Notice "sudo login $url"
                if { $url eq "admin/" } {
                set val ""
                if { [catch {set val [ad_get_signed_cookie sudo]} err] == 1 } {
                        set val ""
                if { $val eq "" } {
                        ns_log Notice "sudo login $err"
                        ad_returnredirect "/sudo/login"

        ad_proc addurl { -url } {} {


copy acs-subsite/lib/login and added this
} -after_submit {

    # We're logged in
    set age 300
    set key 300
   ad_set_signed_cookie -signature_max_age $age sudo $key
ds_comment "cookie"

    # Handle account_message

and now I have to re-authenticate every 5 minutes to get to /admin

Obviously not complete but it works

Perhaps running as root is a bad analogy but if someone sends me a link saying I found a bug on your site at

it would be nice if I had to at least login before it did it. Anyway the more barriers that keep dumb stuff from happing the better. I don't want to go back to lynx to surf the web.

Tom, running as root means that in OpenACS a "regular-looking " user is very often also an administrative account e.g. on there is a bunch of people that have access to acs-admin (how many?)

So if someone of these folks is logged in and surfing the forums, he's entitled to do administrative tasks as well. Even worse if that person is surfing other sites, "dodgy" http requests may lead to "dodgy" requests on

So my analogy is: think Linux and you fire up firefox from your root account to surf the web.

(Windows users know that situation quite well: they usually use an "Adminstrator" account all the time)

Instead of trying to sanitize and parse html code to death we should just take Barry's sudo package and also add a switch to the form_builder and ad_page_contract to require a "second" login for pages where it seems necessary e.g. an order confirmation page or user-dependant "nuke" pages.

Would this require a TIP? Or is it considered a security fix ?

To paraphrase the paraphrase of Lars from the original post, "security is important, but we too often sacrifice usability to obscure security issues."

Just thinking about my own site, I'd go stark raving mad if I had to login every time I wanted to do something admin-ish.  How many of you backing "sudo" don't just build high-traffic sites, but are responsible for the day-to-day running of them?  Even with having a browser memorize my password it would be a total PITA.

Fix the actual vulnerability (excessively trusting admin form actions), rather than forcing everyone to run through the gauntlet of a thousand logins.  Seems to me that this would be the lazy man's "fix."


That is just wrong. Security is not just one thing. I don't see any reason to require a password for certain actions. Have you ever ordered something from

Increasing the security of certain forms is a good idea. Requiring authentication for certain actions is a good idea. Improving the signing of variables is a good idea.

The way to secure against a unknown vunerability is to have security in layers. Site-wide admin actions such as granting site-wide admin privilege is the type of action that can require additional authentication.

I think your would want both. Sudo on Unix is pretty flexible and doesn't really get in the way. I don't even have a root account on my mac but sudo bash works just fine and I think you would want something similar for the web.
saying "one or two things like granting site-wide admin priviledges should require additional authentication" is a very different thing from Dirk suggesting that regular users should be distinct from admin users.
After thinking about it and writing some code I came up with these package parameters
Allow a user to sudo forever: f
Referer must match this regexp: ""
IP must match this regexp: ""
Number of seconds until login required: 300
Extend timeout on every match: t
Allow ip address to change during session: f
Logout on a non match: t
Don't require a login just warn: t
The defaults mean that the first time you visit a protected page you are warned. As long as you access protected pages (and only protected pages) at least once every 5 minutes sudo is transparent. If you visit a non protected page you are warned again. I've got the default path mostly working. I'll post some code tomorrow.
Jonathon suggested that POST-only checking is enough even with Javascript.

Of course if you allow javascript, then you can issue multiple document.write statements to build a form and then submit that form.

If you forceably eliminate javascript, then post only checking should be sufficient? It's certainly more easily do-able than the other suggestions.

I also like Jonathon's method of putting a big ns_rand into an nsv of allowed "keys" and popping it out in the relevant admin pages.

Let's keep it simple if we can!

<Mark madly checks all his oacs sites for insecure antispam settings...>

A couple people pointed out that making POST-only the default behavior for ad_form processing would break the redirect-for-login code, which resubmits the page with GET variables after logging in.
41: How Admin-do should work (response to 1)
Posted by Andrew Piskorski on
If someone is actually going to implement sudo-like functionality for OpenACS - which is an excellent idea - the proper solution must encompass giving Admins at least these two switches:
  • Drop my admin privilege for now, until I tell you otherwise.
  • Turn on my admin privileges today (for X hours). [Requires password.]
Plus add any other similar variants that Admin users want.

By default, when an Admin logs in, he should have only the privileges of a normal user, because most Admins are also normal users of the site, and are usually logging to use the site, not to immediately start doing protected Admin activity. However, you might as well give Admins a checkbox on the log-in screen, defaulted to off, which says, "Enable my Admin privilige immediately, for the next 10 minutes."

Probably, when an Admin tries to do an admin-thing the system should ask him for his password and implicitly push a "Turn on my admin privileges for the next 10 minutes" button for him. However, it wouldn't be so bad just to deny the action and give him link to the "turn on my Admin privileges" page instead.

But if an Admin needs to, he should be able to explicitly enable - and leave enabled - his Admin-ness for some longer period of time. Depending on the site, this time should probably 1 to 24 hours.

And of course, very preferably, the "10 minutes" expiration time, just like with sudo, should be counted from the last successful Admin operation, not from the time it was turned on. This means if an Admin is admining away feverishly for 3 hours, the "10 minute" time will never expire until he's done. It's not absolutely critical, because an Admin should always be able to just explicitly turn on his privileges for longer instead, but actual real-world security will probably be better with the smart "timeout clock starts from last admin operation" feature.

If we are talking about a perfect solution, converting to POSTs won't cut it either. You can place a button on a site a which transfers data to site b. Sometimes it's a feature (vote here for our site), sometimes it's an exploit.

(Javascript may be another issue here? Can I issue post requests from Javascript?)

I suggest we add Barry's code to the stock release, issue a patch (request-processor-procs.tcl is stable over many versions of OpenACS), and then try to wiggle out a perfect solution.

I have a version that works but has very little testing. This version only supports Oracle. There are a couple of issues to work out. First the defaults need to be correct so the package provides some protection without getting in the way. The main one I'm still thinking about is referer. I built an option that will check that the referer hostname is the same as the login host name. Seemed like a good idea but if you just type you get a security violation because the referer is empty. That means every admin page must be accessible via a link. The second issue is how to match the urls. Currently I'm using like with a url pattern like %/admin%. The other option is of course a tcl regexp. See the docs for how to patch the system to enable it. I also added

&lt;if @logout_url@ not nil>
&lt;li>&lt;a href="@logout_url@" title="<span>#</span>acs-subsite.Logout_from_system#"><span>#</span>acs-subsite.Logout#&lt;/a>&lt;/li>
&lt;if @sudourl@ not nil>
&lt;li>&lt;a id="sudoend" href="@sudourl@" >&lt;font color="red">Sudo End&lt;/font>&lt;/a>&lt;/li>

to the site-master.adp file


if { [site_node::exists_p -url /sudo] } {
set sudourl [sudo::endsessonurl]
} else {
set sudourl ""

to site_master.tcl

Ye Gads. Shouldn't this issue deserve a top-level news item on the OpenACS site? Something like "Monstrous Security Hole: disable all HTML immediately!"

At least until a workaround is available!

Though we need a general solution, the major hole is the possibility to grant swa permissions to a certain user. If this page requires an additional password or uses a mechanism making sure it can be only called from a certain other page, this would help.
Kjell, unfortunately that would mean that everybody needs to disable HTML. Not very probable.

The issue here is that even with the most dilligent HTML parsing on your own site, somebody else can admit any kind of malicious HTML which would harm your page.

Jonathan, as to userfriendliness vs. security, I think the  sudo solution provides for a good trade-off:

* You need to relogin only once in a while
* No expired pages
* And let's not forget: the changes are minimally invasive.

Another solution, namely adding session ids to all GET and POST URLs would mean gigantic code changes, expired pages, and clunky URLs.

Kjell is right: we need to have a process for communicating important security announcements like this.

I propose a separate security forum, that is only for security updates and warnings. I know nobody likes having separate forums because it can fragment conversations, but the advantage is that nobody should have to follow any of the other forums if they only want security updates. And security updates are pretty manditory.

You don't need to disable all HTML, Kjell, just not allow * for HTML.

Is this something that the OCT is willing to discuss and make some decisions about?

Another point:

The developer support shell page needs to use signed variables too. Otherwise, you could pass the variables directly to developer-support using an IMG tag, and not even bother to make yourself site-wide admin.

Barry, thank you for actually doing something about all this discussion! Is anybody willing to port his code to Postgres once he's done testing it?

I personally think this is a security fix, so it wouldn't require TIPing, but perhaps it could be TIPed just to be extra sure...?

Jade, unfortunately it is not entirely true that disallowing * for HTML will be enough.

If a remote page (called B) contains an img tag that GETs a URL on your site (called A), you GET this page on B, then you'll issue a GET request to your resource on A as well - with your credentials on A.

So HTML parsing WON'T solve this problem.

(However disallowing * will significantly reduce risk)

Attacks from remote sites can be stopped reliably with the referer heading. I put a feature into sudo that requires refer host to a page must match the host you logged in on. Unfortunatly this causes a usablity problem, you cannot just type http:://myhost/acs-admin because the referer heading is null and you get a policy voilation. This could be solved with a page a links to various admin pages. This could be generated as part of the sudo package.
Yes, taking out "*" is utterly mandatory in the general case. I'm still quite disturbed that an advisory has not yet been issued warning users about the dangers of not only the default configuration, but "src" attribute attacks against OpenACS in general.

This is, in effect, a remote root compromise of any machine running OpenACS.

A security forum is a nice idea, but at a bare minimum, advisories should go to the usual places (bugtraq, etc.)

I think from a security standpoint it's probably a mistake to think limiting user input tags is a fix to this problem. The reason to limit tags is to keep people from putting javascript and blink tags into your site, plus sites like would not be that interesting without img tags.

I think the real solution is to make signed variables the default and make the signatures 1 time use only, but that seems like a lot of work. I suspect it's possible to do this at the package level and make it the default for new packages. I also suspect adform could also be changed in transparent way. The whole thing is probably more work than noquote.

It's probably worth pointing out that this is by no means limited to OpenACS; someone brought up a similar point with PhpNuke in a posting to bugtraq about three months ago, and made the point that most web applications are affected by similar attacks. Unfortunately, there weren't any responses to it, let alone anything smart :). IMO, the sudo-style approach is the way to go (not necessarily with referer checks), as you can still send out emails to administrators with links to perform actions, e.g. approving requests etc. The super-paranoid could always set the time between reauthentication to 0.

In looking around for XSS handling under various platforms, I was surprised how little the visiting-evil-website version of the hack was mentioned. Most people are implementing input validation. As of the 1.1 version of ASP.NET, Microsoft turned on validation by default. ASP.NET Request Validation and Cross-Site Scripting There were lots of complaints from their user-base, but it makes sense to be more secure out of the box, then allow savvy administrators to relax security. I'm still trying to get my head wrapped around the whole thing, but from my simplistic viewpoint, would the following work for a given website: (1) input validation everywhere, (2) administrator education that you don't wander with your browser, i.e. open/close whenever in admin role. #2 wouldn't protect against a teacher getting her class area hacked, but social engineering would help prevent OpenACS admin accounts from being misused.
Barry, your check on the referrer sounds like a good way to stop remote-site attacks. It enforces proper behavior for people who access privileged areas. Would the check be applicable to any page requiring privileges? Such a security feature could be enabled out-of-the-box, but admins like Jonathan could turn the feature off to prevent his descent into psychosis.
The referer check is nearly transparent. If you go to a page with the wrong referer host you are redirected to page with a link. Clicking the link completes the action. This is so you can get to acs-admin by just typing mysite/acs-admin. There could be times when clicking on the link would be a bad idea and I may disable the link if referer host is not null or go thru the sudo login page if the referer is null.

Unless you can javascript the referer on a url request (and I'm sure someone has figured out how) this should make remote attacks difficult. I'm also thinking that if you create a DNS entry like and force all admin thru that url then links from become remote attacks. That would also require a seperate login on Between the two I think that would make openacs safer than most sites.

I don't think relying on the referer header is a good idea, since it might not always be sent by the browser. Looking in my access logs I see from time to time users whose referer is always blank, even when they are apparently navigating within the site. It's propably caused by some kind of (paranoid) web filter, firewall or proxy software. I see that too often to feel comfortable locking these users out of adminstrative functions on my sites.
I've seen black referer in my logs also. I think some anti spyware software turns them off.

Checking referer can be turned off but I think it's the simplest (and perhaps only) way to block remote attacks. It just depends on what you are most afraid of.

You could add session_ids to block remote attacks: they are server-issued, rely on a server-side secret, bloat the URL, has you think about URL expiry etc. It is totally unrealistic that we would implement this for OpenACS though, too much work I fear. (One nitpicking side note is that the referer header is user-supplied and thus may be forged too.)

Relying on the referer header and being able to turn that off is imho a very sane solution. It provides close-to-optimal security with a minimum of fuss, new code, and UI hassles.

The stuff you've implemented (plan to implement?) should become part of the stock distro of OpenACS with a default setting of enabled.

I don't think the referer is easily forged in this case since it comes from the browser of the admin. Hopefully this cannot be javascripted or changed in some other way.
No I was looking for any javascript exploits of the referer header and to-date there aren't any. It's a very, very nitpicking remark. :)

Most sites describe referer checking as the 90% solution mostly due to the cases where the requests are legimate, but turned down because of the reasons you gave above (link in an email, bogus proxies, etc)

Barry, any developments on the sudo functionality? Could you commit it to HEAD?
I've been running it on my dev box since I wrote it and I haven't had any problems. I don't have a postgresql version yet but I've been thinking about simplifying a few things and I think I could make it run on both with no changes.

I don't have commit privileges to CVS but I can send you the current version. There is a required change to the request processor and I also made a change to the template so you can tell when you are in sudo mode. If the package is not mounted then the system works as before.

Also I'll be out of town this weekend and without email

Barry, I'd be happy to commit the package. If you think you'll get a chance to work on a version that would work with Postgres, we can hold off until then, though.
I would like to see the change to the request processor
since that's not something I want to see change much without
some thought.
Here is the change the request processor.
    # call sudo if it's mounted
    if { [site_node::exists_p -url /sudo] } {
         sudo::checkauth -url [ad_conn extra_url]
It should probably check for the package key instead of the url

I'll see if I can get a postgresql version working. I need to install it on may machine anyway

Barry, it might also be a good idea to cache the result of the exists_p operation, especially if that is being called often.

Let us know if you have an Oracle and Postgres version available. :)

I've got the postgresql version working. I'm going to do a 5.1.1 clean install on both databases and see what happens. I'll make the change you suggested also.

Currently I'm using % in my match string and using like to find them

select sudo_url_id from sudo_urls where :url like url
This works fine but costs a db hit on ever page. I'm thinking about changing this to use tcl regexp and cache the list of protected urls. I think this will be better if you don't have very many and I don't really know why you would. You can protect all the admin sections with .*admin.*

Anyone have an opinion?

Finally I was reading some W3 documents the other day and apparently there is a requirement that the GET method makes no data changes. The example they give is you should not send a link that subscribes to a mailing list. You should send a link to a form that when POSTed does the subscribe. I was surprised this was in the spec.


1) String match should be faster than a regexp for that (on Tcl 8.4 anyway.)

2) OpenACS is not the first or only platform to break that rule. I believe we discussed changing the most serious actions to forms when someone had time to work on it.

Barry, any update on this?
Ping to Barry :)

It would be really nice to get this plugged! Any updates?