Sunday, November 24, 2013

Preventing cross-site request forgery (CSRF)

What is CSRF?

Cross-site request forgery (CSRF) is a type of exploit of your web site whereby a malicious party may be able to perform operations posing as a given user of your site, without that user having authorized those operations, or even being aware they are performed.

In a nutshell, here is how a CSRF attack works:

  1. The setup – You are a client of Acme Bank. Through Acme Bank's web site, you can transfer funds from your account to someone else's account. When doing so, say transfering $1,000 to account #123, your browser issues a request that looks like http://acmebank.com/transfer?to=123&amount=1000. (We're using an GET in this example for simplicity, but the attack also works if requests are issues through a POST.)
  2. Authentication and HTTPS – To be able to issue that command you need to be logged in. When you log in, the bank site give your browser a cookie, say JSESSIONID=abc, which your browser then sends with every request. Since the value of this cookie was chosen by the bank site just for this session, and given just to you over a secure connection, the site can be certain that every request you issue comes from you. If a malicious party where to issue the request http://acmebank.com/transfer?to=123&amount=1000, the bank site would just reject it because the malicious party didn't provide the proper cookie. And again, thanks to HTTPS, there is no way for them to know what the value of the cookie is, even if they had the ability to eavesdrop on your connection. So you're safe, right? Not quite.
  3. The attack – A malicious party can't issue that HTTP transfer directly. But what if they put the link somewhere and get you to click on it. For instance, the link could be in an email, a message you get on a social network, or a web site you usually go to and that they managed to hack. Then the request will be issued by your browser, which will send the cookie, assuming you're still logged into the bank site. And bingo, the transfer will happen.

What can web apps do to prevent CSFR?

A web app can protect its users against CSRF attacks by generating for each user connecting to the site a random token, and requiring that token to be sent along every request. (And of course a mechanism other than cookies must be used to send this token with every request.) Software packages, such as Spring Security or OWASP CSRFGuard, can make it easier for you to do this in your own web application.

Orbeon Forms does this out-of-the-box

Orbeon Forms does everything required to prevent CSRF attacks, out-of-the-box, so this is one less thing you'll need to worry about. Specifically, every time users load a page containing a form, Orbeon Forms generates a document id, which acts as the token described earlier. That document id is then sent with every request, Ajax or form POST, and Orbeon Forms won't execute the request if the document id isn't valid, and doesn't match the session cookie.

This post was updated on 2014-05-23 to include more information on how CSRF attacks work, and to describe how Orbeon Forms completely prevents CSRF attacks on forms it generates.

Monday, November 18, 2013

Inserting and reordering grid rows

Grids are a key ingredient in Form Builder and Form Runner, as they are the main tool you use to layout controls on a page. And since Orbeon Forms 4 they are also repeatable, which means that you can repeat one or multiple row of the grid.

We have just added some important missing features to repeated grids:
  • The ability to reorder grid rows.
  • The ability to specify exactly where to insert a new row.
We thought quite a bit about how to best create the user interface. Drag and drop to reorder would be nice, but it is also hard to do right (we'll still consider adding this in the future). Cramming each row with 4 more icons didn't seem like a good idea. Icons showing upon hover would have been possible, but in the end we opted for a very simple solution: a menu, available on each row, which you explicitly open. Another benefit of the menu is that you also have more space to tell the user what the operations really do.

The new grid menu
We now have 5 operations:
  • Insert Above
  • Insert Below
  • Move Up
  • Move Down
  • Remove
The "Remove" operation was already available in previous versions via an icon.

Not all operations are available at all times. For example, you can't:
  • move rows beyond the top or bottom
  • insert or remove rows beyond the configured maximum or minimum if any
  • move and reorder "frozen" rows that can appear at the beginning of a grid
  • remove a row which has "remove constraint"
We have left the "+" icon at the top of the grid, which inserts a new row below the currently selected row. We have improved on this a bit by very discretely highlighting the current row, and improved keyboard focus handling when inserting/moving rows.

This feature will be available in Orbeon Forms 4.5.

Wednesday, November 6, 2013

Saving memory by removing unneeded whitespace

Photo by Brad Montgomery
Orbeon Forms stores form definitions and form data in XML format. When using XML, it is customary to use new lines and indentation to make it easier for humans to read and write. Notice for example the following bit of empty form data:
<book>
    <details>
        <title/>
        <author/>
        <language/>
        <link/>
        <rating/>
        <publication-year/>
        <review/>
        <image
            filename=""
            mediatype=""
            size=""/>
    </details>
    <notes>
        <note/>
    </notes>
</book>
New lines and indentation fall in the category of so-called whitespace. Whitespace consists in anything that looks “blank”, including actual space characters and line breaks.

What we have noticed is that, especially for large forms, whitespace can take a significant amount of memory in compiled form definitions. So for Orbeon Forms 4.4, we looked into how we could improve that situation.

The trick is to remove whitespace where it is not needed, because in some cases you do want to keep at least some of it. For example you can remove most indentation and new lines, but consider this HTML fragment:
<p>This is a      <b>great</b>       moment.</p>
Some space after “a” and before “moment” needs to be there, but in most cases that space could be collapsed or normalized to a single space (there are exceptions). But it would certainly be wrong to remove all the spaces.

A different example is the HTML <pre> tag, within which all the whitespace should be preserved, including indentation and new lines.

To address this, we implemented a configurable whitespace stripper [1] for form definitions, with the intent of removing as much whitespace as we can while keeping it where it is needed.

The result is that for very large forms, it is possible to save over 20% memory for the compiled form definition compared with Orbeon Forms 4.3.1. One very large form definition had over 15 MB of waste due to whitespace!

These savings are especially important if you have a large number of distinct form definitions.
We still hope to improve on this in the future. For example it is probably possible to save memory within form data as well.

  1. If you are curious as to how it’s done, see Whitespace.scala, CharacterAccumulator.scala, and WhitespaceXMLReceiver.scala. The default configuration is done with CSS 3 selectors in properties-internal.xml.  ↩

Monday, November 4, 2013

Orbeon Forms 4.4

Today we released Orbeon Forms 4.4!

This release includes the following new features and major enhancements:
  • Oracle support for autosave and owner/group-based permission. These two features were timidly introduced in 4.3 for MySQL and DB2 only, and required two properties to be set to be enabled. With 4.4, they are available on all the relational databases supported by Orbeon Forms (Oracle, MySQL, and DB2), and are enabled by default (doc for autosave, doc for owner/group-based permissions).
  • Publish to production. This feature allows you to configure access to a remote server and to publish, unpublish, and transfer forms between the local and remote server (doc).
  • Form Runner can use the current Liferay language. When enabled, the user's language set in Liferay is propagated to Form Runner and influences the Form Runner language. This is available for the Form Runner proxy portlet only (doc).
  • Form Runner can use Liferay user, group and roles. When enabled, the Form Runner proxy portlet propagates headers with user, group and roles information to Form Runner, which can then use that information for access control (doc).
  • Form Runner is more extensible. Form Runner now supports adding custom XForms model logic (doc), simple processes (doc) support sending custom events, and you can specify your own JavaScript code (doc). The combination of these features enables custom behaviors without changing Form Runner itself.
  • Memory improvements for compiled forms. We have worked on reducing the memory footprint of compiled form definitions. This is important for users with possibly hundreds of forms deployed. In a large form scenario, we have measured 20% less memory consumed (blog post).
But that's not all: there are over 80 closed issues since Orbeon Forms 4.3. For convenience, here is the list of the most relevant changes:
  • Form Builder
    • "Set Response Selection Control Items" only updates current language (#691)
    • Removing FB JAR causes FR Summary page not found (#1265)
    • Dynamic bindings by type (#1264)
    • Enter key in Control Settings must save and close (#1189)
    • Rename Grid Details to Grid Settings (#1163)
    • FB: Test mode crashes with wizard and inner buttons (#1313)
    • Tooltip for HTML checkbox is incorrectly placed (#1362)
    • XML Schema types is not set (#1372)
    • Grid in section template empty in review and at design time (#1370)
    • FB Summary page: title and description remain in English (#1376)
  • Form Runner
    • Way to run custom XForms when Form Runner button is pressed (#1040)
    • FR: Publish to production (#1055)
    • Red star isn't shown next to labels for required fields in repeat headers (#1176)
    • Image annotation image doesn't show in PDF (#1076)
    • Allow send properties to be AVTs (#1213)
    • Property to include custom content added to the fr-form-model (#1141) (doc)
    • fr-mode doesn't update form upon save (#1232)
    • Noscript doesn't honor oxf.xforms.format.input.date (#1154)
    • Conditionals in the simple process syntax (#1144)
    • Property to add custom JavaScript to Form Runner pages (#1262)
    • Paging not working on DB2 (#1263)
    • New page missing summary, PDF, and review buttons (#1269)
    • fr:section with open="false" is closed in PDF (#1287)
    • Ability to specify fr-language on PDF URL (#1290)
    • Readonly fr:grid first column is too narrow (#1296)
    • XML Schema service: NPE if section id ends with "-section" (#1297)
    • Edit page incorrectly showing 403 when users don't have update permissions and drafts exist (#1278)
    • Page allowing selection a drafts shows drafts we can't edit (#1279)
    • Autosave: prevent users editing a draft they only have read access to (#1277)
    • Oracle support for owner/group permissions and auto-save (#1237)
    • Regression: $fr-roles returns a single string (#1243)
    • Use Liferay language as requested Form Runner language (#1122)
    • Proxy portlet doesn't forward user/role information (#946)
    • Regression: Rich Text control output in PDF is incorrect (#1288)
    • Error Summary doesn't update when FR language changes (#1291)
    • Schema generator does not support attachment controls (#1336)
    • Document REST API changes if any (#1338)
    • Autosave is enabled by default but does not work with eXist when user logged in (#1333)
    • Add a property listing the active persistence API implementations (#1186)
    • fr:currency control doesn't update in repeat iteration (#1348)
    • Summary and Home page use shorter date formats (9fbe1deddb)
    • Summary page shows "23:44 pm" (#1355)
    • IE7: Created/Modified columns too narrow (#1356)
    • More links to FR Home page (#1196)
    • Schema generator to produce XForms namespace on root element (#1366)
    • Can't authorize XML Schema Generator Service (#1367)
    • eXist: Summary page lists form data for user with only "new" permission (#1381)
    • FR: Summary: Handle persistence without permissions support (#1384)
    • Home: "Select All" also selects rows w/o checkboxes (#1380)
    • FR Home: don't link if form is not available (#1389)
    • FR Home: no link if user can only create (#1388)
    • Wizard: click on error in error summary does not switch section (#1394)
    • Regression: Summary page shows "Search returned123of200documents." (#1401)
    • Regression: Import says validation import were canceled (#1398)
  • XForms engine
    • Add CSS class for LHHA appearances when present (#1203)
    • fr:tinymce not showing if in a Bootstrap tab (#1205)
    • Implement support for data: in xf:output mediatype="image/*" (#1065)
    • XForms LocationData takes a lot of memory (#1247)
    • XPath function to access basic image metadata (#1253)
    • JSESSIONID cookie not forwarded to service when xxf:username="" is specified (#1267)
    • A submission with echo: expires the session (#1268)
    • Upgrade to Bootstrap 2.3 (#1202)
    • fr:tinymce doesn't show in table if missing label/help (#1283)
    • Box select too wide on Firefox (#1293)
    • xforms-value-changed: context info for current value (#1295)
    • Incorrect repeat layout in Liferay (#984)
    • Don't proxy Set-Cookie header in response (#1188)
    • Readonly image attachment has upload file chooser (#1240)
    • Strip unneeded whitespace in XForms processing (#1252)
    • Non-relevant textarea can get the focus (#1387)
  • Other
    • Add a feature to delete documents stored by the scope serializer (#1251)
    • In logs, display whole exception message instead of truncating it (#626)
    • Upgrade to Scala 2.10.3 (#1064)
Compatibility notes:
  • If you're using Form Builder / Form Runner with Oracle or MySQL, you need to update your database tables; see the DDL for Oracle and the DDL for MySQL. These changes to the database were necessary to support autosave and owner/group-based permissions on Oracle and MySQL.
  • Form Builder is not supported on Safari 7 at this time (see #1399).
    • UPDATE: Form Builder works with Safari 7.0.1.
  • If you're implementing the REST API, you should now also support the following:
    • in CRUD requests, draft in path in addition to data, used by the autosave mechanism;
    • in search requests,
      • support the new drafts element in the search request;
      • for every document you return, specify the draft="true|false" attribute, used by the autosave mechanism.
    • both CRUD and search requests 
You can download the latest version of Orbeon Forms from the downloads page.
    Don't forget to grab a trial license for the PE version.

    Please send feedback:
    We hope you enjoy this release!

    Sunday, November 3, 2013

    Adjusting a dropdown width on IE7 and IE8

    At times, the width of a dropdown (aka combo box) is constrained, in the sense that it can't be made wide enough to completely show the text for the selected option. However, with all modern browsers, when you open the dropdown, the full values show, as illustrated on the following screenshot:

    A dropdown with long labels on a modern browser
    Unfortunately, IE up to version 8 (included) is cropping values even when the dropdown is opened, as shown below. Depending on the values you're showing in the dropdown and the width of your dropdown, this behavior may be just a minor inconvenience or could be fatal, in particular when two values in the dropdown differ only in the end of their text, which happens to be cropped.

    On IE7/IE8, labels are cropped, which can be fatal to your web app 
    A solution to this problem is to use JavaScript to increase the size of the dropdown when it is opened, setting its CSS width to auto; so it shows the full values:

    JavaScript fix applied to IE7/IE8

    Then, you set the width back to its original value when the dropdown is closed. This method works, until you hit a dropdown with a width: 100% for which labels are short, i.e. changing the width from 100% to auto makes the dropdown narrower instead of wider. If you're making the dropdown narrower and users click on a portion of the dropdown that isn't visible anymore after its width is adjusted, IE understandably "cancels" the user's click, and the dropdown doesn't open, which means the user will have to click a second time on the narrower dropdown to open it.

    Your first instinct might be to fix this with a min-width: 100%. Unfortunately, bummer, min-width doesn't work on dropdowns with IE7/IE8. This means that we have to resort to another technique: use JavaScript to check if setting the width to auto would reduce the actual width of the dropdown, and in this case just leave things as they are. If this sounds somewhat convoluted, it is because it is! But I imagine that, at times, this is price we have to pay to properly support older browsers.

    You can find the JavaScript for this, along with a test case, in this jsFiddle. At the moment, our thinking is that this is too much of a corner case for us to include this code by default in Orbeon Forms, but if this solves a problem for you, feel free to use it, with Orbeon Forms or elsewhere.