Cross-scripting attacks don't just affect public wiki sites. For example,
a footpad could mail one of your users with a crafted URL that, when
clicked on, compromises your entire corporate intranet. All wikis, public
or private, need protection against these attacks.

The plugin works by filtering the HTML output by Foswiki as late as
possible in the rendering process. It removes anything dodgy from the
HTML, such as inline script tags, Javascript event handlers
containing complex script, and URIs that refer to objects outside a
controlled range of sites.

Whenever anything is filtered, a report is written to the Foswiki warning
log.

The plugin filters all HTML it thinks is dodgy from the output. There is
a chance that one or more of the extensions you are using works by embedding
naughty HTML. If you find that
SafeWikiPlugin kills one or more of your other extensions, then you are
advised to seek fixes from the authors of those extensions.

SafeWikiPlugin also has a 'clean html' switch that can make it report
an error if malformed HTML is generated by Foswiki.

It is unavoidable that there will be a performance penalty when using the
plugin. The size of this penalty depends on your exact configuration, but
benchmarks suggest that on average it is less than 1% of the total
rendering time.

WARNING

This software is provided in the hope that it may be useful.
The authors make no warranty, implied or otherwise, about the
suitability of this software for safety or security purposes.

The authors shall not in any case (except as required by applicable laws) be
liable for special, incidental, consequential, indirect or other similar
damages arising from the use of this software.

In clearer words: while the authors are confident that the plugin does
exactly what is documented, we can't make an ironclad guarantee. Additionally,
the safety checks introduced by SafeWikiPlugin can be circumvented by
plugins, by misconfiguration, and by JavaScript code making use of macros
without sufficient validation. This plugin does not replace common sense!
It should prevent attacks out of the box, but additional plugins may weaken
the protection, so administrators are advised to be careful about installing
new plugins, signing new JavaScript code, or making changes to the filter
expressions in the configuration.

How to authorize/sign JavaScript code

The easiest way to get script code into the wiki without having to deal with
signing is to put it in a .js file that is included by URL. By default,
scripts attached in the System web are considered safe; you can define
additional safe locations in the configuration. Unsafe locations will, of
course, be filtered.

Authorizing static scripts in topics or templates

For good reasons that are a bit complicated to explain, SafeWikiPlugin can't
magically exempt certain topics from XSS filtering. Therefore, each individual
piece of script code has to be authorized explicitly. SafeWikiPlugin provides
three ways to do that:

an administrator can add a signature to the wiki configuration;

a plugin can include a signature;

script code can be cryptographically authenticated by people who know a secret key (chosen per wiki in /bin/configure).

The third option is meant to be used for providing wiki apps that users can
install without needing administrator rights; for instance, a wiki consultant
might use their customers' secret keys to distribute pure wiki apps without
causing any administrative overhead for the customers -- all the customers
need to do is copy and paste the wiki app's code, and it's already signed so
SafeWikiPlugin won't destroy it.

The first two ways use simple SHA-256 signatures of the script code; the third
way uses a HMAC/SHA-1 hash instead (HMAC means that you can only calculate the
"right" hash value if you know the secret key). We're going to call these
hashes *MAC*s from now on.

The two types of hashes are easy to distinguish: while both use a Base64
encoding and thus look like gibberish, the SHA-256 is 43 characters long
whereas the MAC is only 27 characters long.

While it's possible to calculate the SHA/MAC values yourself if you want to,
it's much easier to let SafeWikiPlugin do it instead. For security reasons,
console access to the server is required. After writing a piece of
JavaScript code and embedding it in a topic or template, just run
./safewiki-sign from within Foswiki's tools directory and paste the code.
As always, you can terminate your input with Ctrl-Z + Enter (Windows server)
or Ctrl-D (Linux etc.; you may need to press it twice).

The script will output the correct SHA/MAC hashes (using the currently
configured secret key in the case of MAC).

The section for SafeWikiPlugin in /bin/configure has an option
{SignaturesList} where you can add your own signatures, like this:

Plugins can add signatures by installing a file to a special location. For
example, EmptyPlugin might install lib/Foswiki/Plugins/SafeWikiPlugin/Signatures/EmptyPlugin.pm.
This file should contain Perl code and do nothing but return a reference to
an array of signatures, like this:

MACs are simply inserted as a specially crafted JavaScript comment at the
very beginning of the inline code, be it a script tag, an on* attribute,
or a javascript: URL (in that case the comment goes right after the
javascript: part. Here's an example:

<script>
/*safewiki:U2AGOBOv0pY4R4poBM/EXQNRFoE*/alert('This works (if the MAC is correct)');
</script>

Authorizing dynamically generated or altered code

If you are shipping dynamically generated code with a plugin (example:
JQueryPlugin's code to make Foswiki preference values available to scripts),
just embed the code using a call to Foswiki::addToZone. It will
automatically bypass filtering. In addition,
=Foswiki::Plugins::SafeWikiPlugin::Signatures::permitInlineCode temporarily
(for the current request) whitelists a piece of code to be used in a handler
attribute or an inline script tag (as with normal signatures, it only works if
that piece of code is the exact content of the handler attribute or script
tag). In either of these cases, it's your responsibility to validate any code
you add to a page this way.

Alternatively, if that's not an option for some reason or another, you can
add a signature even for code that contains macros and is embedded in a topic
or template file. There are a few requirements for making this work:

Macros used within the script code must be escaped (using $percnt syntax).

Special syntax must be wrapped around macros used in script code, in order to allow SafeWikiPlugin to verify that, for instance, the expanded macro doesn't break out of a JavaScript string constant.

The syntax for verifying macro expansions is very simple. You use it to tell
SafeWikiPlugin which pattern the expanded macro (the "result") is matched
against. Any macro that doesn't have such a pattern associated with it, or
that fails to match the pattern, will not be expanded nor unescaped.

At this point there are two patterns:

identifier permits an expansion that is made up of nothing but alphanumeric characters and underscores;

string permits any character but backslashes and the quotes surrounding the macro.

Matching strings more exactly is not currently implemented, so it's not
possible to use macros that expand to values containing quotes or backslashes.

If the macro expansions are accepted by the patterns (for example,
BROADCASTMESSAGE must not contain double quotes or backslashes, and
SPECIALWEB is restricted to alphanumeric characters and the underscore),
this will expand to something like the following, in the script zone:

Please note that these special comments are only recognized if there is no
space between them and the string constant containing the macro expression,
and neither may there be anything else in that string constant than the macro
expression.

Annotating macros is not necessary for a number of standard Foswiki macros
(e.g. BASEWEB, PUBURL, SYSTEMWEB) -- these have the appropriate types
pre-defined, so in many cases your script will actually work without adding
the annotations.

Now that these prerequisites have been fulfilled, all that's left to do is to
determine the MAC. You can't use exactly the same method as above because the
assertions are generated after dynamic scripts got their chance, i.e. macros
have already been expanded.

To tell SafeWikiPlugin that it should try to protect the zone snippet, add a
signature parameter to the ADDTOZONE macro, with any non-empty value. Now,
the above ./view command will output something like this somewhere near the
top:

We have to let the ADDTOZONE macro know about that MAC so that SafeWikiPlugin
won't attempt to filter the zone snippet. That's what the signature
parameter you just read about comes in: just put the MAC in there. Done!

(You could also add the SHA to the list of signatures instead if you prefer.)

Gory Details

This explains the additional filtering steps if none of the signatures apply.

JavaScript

The values of all Javascript on* handlers (such as onload, onmouseover,
onblur etc) are automatically compared against a list of filter-in regular
expressions, one of which must match, or the handler will be replaced by a
disarming string.

By default only simple function calls with atomic parameters are
permitted in on* handlers. For example:
javascript: fn(param1, "param2") is permitted,
but javascript: alert(window.open("http://evilsite.cn")) is not.

Inline scripts (SCRIPT tags without a src parameter) are always
filtered out (removed). URIs used in certain parameters are compared
against a whitelist of filter-in regular expressions, one of which must match
or the URI will be replaced with a disarming string.

In addition, script snippets interpreted by JQueryPlugin's METADATA plugin
(notably {key:value} constructs in class/data attributes) are filtered in
the same way.

URIs

The tags filtered, the "whitelist" regular expressions, and the placeholder
string used to mark disarmed code are all defined using the configure
interface. See the setup for SafeWikiPlugin for more help.

As mentioned above, there is a risk that use of SafeWikiPlugin might
prevent your extension from working. If that is the case, it will usually
be because you have tried to embed something in the HTML that the
SafeWikiPlugin regards as "naughty" - for example, inline script,
complex expressions in handlers etc.

The way to overcome this in your plugins is described in the section about
authorizing dynamically generated code, but for your convenience here's
something to copy & paste:

Foswikitask:Item8220: support filtering of eval() calls by supporting filter-out for handlers, and URIs too while I was in there Foswikitask:Item1963: hardened the regex that selects where to get JS from to restrict it to the Foswiki System web, which is not normally writable by ordinary users

14 Jun 2009

Foswikitask:Item8181: plugin made aware of use of foswikiStrikeOne which is needed to work with Foswiki 1.0.6 and later versions.