jsr344-experts@javaserverfaces-spec-public.java.net

[jsr344-experts] Re: [869-CSRF] Proposal

From: Ed Burns <edward.burns_at_oracle.com>
Date: Thu, 25 Aug 2011 14:27:13 -0700

>>>>> On Wed, 17 Aug 2011 15:12:16 -0700, Ed Burns <edward.burns_at_oracle.com> said:

EB> Relative to proposal version 20110726 of
EB> <http://javaserverfaces-spec-public.java.net/nonav/proposals/JAVASERVERFACES_SPEC_PUBLIC-869-CSRF.txt>.

EB> This email contains my responses to Blake's helpful 27 July reply.

EB> I will produce a new version of the proposal as time allows this
EB> week.

And here it is. Sorry for the delay. ACTION: Please review by 09:00 -5
Tuesday 30 August 2011. That's 9am Eastern Daylight Time.

Thanks,

Ed

Version 20110825

http://jsf-spec.java.net/nonav/proposals/JAVASERVERFACES_SPEC_PUBLIC-869-CSRF.txt

SECTION: Discussion

>>>>> On Mon, 25 Jul 2011 18:43:53 -0700, Blake Sullivan <blake.sullivan_at_oracle.com> said:

B> On the GET side, we have two problems--one implementation and one
B> controlling which pages this applies to (or doesn't apply to). I laid
B> out the implementation possibilities. The problem of determining when
B> to apply is that any page that needs to be directly accessible from
B> outside the application can not have this check applied to it, but JSF
B> doesn't currently keep track of valid external entry points.

I assert that we only need to keep track of views that should be
protected. Any view that is in the app, but not in the set of protected
views, is a valid external entry point. When JSF renders a view that is
an external entry point, if it is rendering a link to a protected view,
it will include information in the link such that when the protected
view is requested by the user agent, CSRF protection is applied.

B> If we are specifying rules, should the rules be based purely
B> on the URL?

I think the rules should be based on viewId.

B> If we are modifying the generated URLs, I worry some about
B> the performance impact of stamped actions.

Noted, we must ensure the overhead of discovering "is this view
protected" is very small.

B> Even if we end up falling back to URL manipulation for GETs, I would
B> prefer that we use referer checking if the referer was present in the
B> HTTP Request that initially created the session, as this doesn't
B> require any URL manipulation.

Blake also requested a way to declare protection on a per-view level,
within the view. I assert this will be too tricky to implement because
we would have to crawl the views at startup time to discover which pages
were declared for protection in this way. I don't think it's worth the
trouble and complexity.

B> Shouldn't we be more specific about the exception thrown? I would think
B> that an IllegalStateException would do the trick, but we might want an
B> explicit exception so that the exception handling system knows what kind
B> of exception this is. I don't think that anyone would do this, but why
B> can't the token be in the exception?

Yes, I'll declare an explicit exception.

SECTION: Proposal

JSF offers three varieties of navigation from view to view, all of which
can be used at any point in an application.

SUBSECTION: Terms for Navigation in JSF

PostBack

  In this variety, the user agent issues an initial HTTP GET to enter
  into the navigation and subsequent navigations are done by the browser
  sending a POST request with a server side RequestDispatcher.forward()
  to another view.

PostRedirectGet

  This is the same as PostBack except that the server sends a
  redirect to the user agent, causing it to issue a new GET request.

GetGet

  This variety can be used when form submission is not required.

All three varieties must be protected from Cross Site Request Forgery
attacks.

PostBack navigation is protected implicitly by virtue of the
requirement, new in JSF 2.2, that the view state token must be
cryptographically strong and verified.

PostRedirectGet and GetGet navigation is protected by the developer
taking explicit action to mark any views which should be protected.

SUBSECTION: Developer Experience

Within the <faces-config-extension> element, we introduce a new element:
<protected-views/>. This element contains zero or more <url-pattern />
elements. Any view that matches any of the url-patterns in this element
may only reached from another JSF view in the same web
application. Because the runtime is aware of which views are protected,
any navigation from an unprotected view to a protected view is
automatically subject to protection.

To support dynamic views, there must be a way at runtime to modify the
set of protected views. API has been added to ViewHandler to support
this feature.

public List<String> getProtectedViewsUnmodifiable();
public void addProtectedView(String urlPattern);
public void removeProtectedView(String urlPattern);

This API is also called by the runtime when it needs to determine if a
view is protected when rendering navigation to that view.

SUBSECTION: Specification and Implementation Details

* In the spec PDF, in the section titled
  "faces-config-extension-handling", add a section after the section on
  the <facelets-processing /> element, specifying the handling of the
  <protected-views/> element. Follow the example of
  <facelets-processing />. Specify that the content of the
  <protected-views /> element must populate the protected view API on
  the ViewHandler.

* In the spec PDF, in the section titled "Default ViewHandler
  Implementation" declare the protected view API and refer to the
  javadoc for the methods for the specification.

* In the ViewHandler javadoc, specify the protected view API.

* In the spec PDF, in the section titled "ResponseStateManager" modify
  the spec for writeState(FacesContext,Object) to point to the javadoc.

* In an apparent spec bug, there is no javadoc on
  ResponseStateManager.writeState(FacesContext,Object). Move the text
  from the corresponding section in the PDF into the javadoc. Include
  text requiring the state to be encrypted and tamper evident.

* In the spec PDF, in the section titled "ResponseStateManager", modify
  the spec for getState(FacesContext, String) to point to the javadoc.

* In the javadoc for ResponseStateManager.getState(FacesContext,String),
  specify that the state written by the previous call to
  RSM.writeState() must by decrypted and verified before being
  restored. Specify that if the state fails to decrypt or verify, a
  ProtectedViewException must be thrown.

  class javax.faces.application.ProtectedViewException extends
  IllegalStateException

* In the spec PDF, in the section titled "Restore View", add a bullet
  immediately before the first bullet in the section that starts with
  "If the request is not a postback". This bullet will say something
  like:

    If the request is not a postback, use the protected view API to
    determine if the view for this viewId is protected. If not,
    continue. Otherwise, look for a "Referer" request header. If the
    header is present, use the protected view API to see if any of the
    protected views match the value of the Referer header. Note that
    this allows a web app to be constructed so that it accepts arbitrary
    urls, even from other web apps. If so, continue. If not, verify
    that the Referer header value corresponds to a view in the current
    web app. If not, throw a ProtectedViewException. If so, continue.

    If the Referer header is not present, verify the request parameter's
    value given by the value of the constant
    ResponseStateManager.NON_POSTBACK_VIEW_TOKEN_PARAM is the same as
    the token value generated from the "secret key" stored in the
    session. If the values do not match, throw a ProtectedViewException.

* In the spec PDF, in the section titled "Default ViewHandler
  Implementation", in the specification for getActionURL(), incorporate
  text similar to the following.

    If the current view is one of the views to which CSRF protection
    should be applied, the returned URL must contain the parameter with
    a name consisting of the form's fully namespaced client identifier,
    the NamingContainer.SEPARATOR_CHAR and the constant defined by
    ResponseStateManager.NON_POSTBACK_VIEW_TOKEN_PARAM. The value of
    this parameter, known as the "token value" must be a
    cryptographically produced random generated value (known as the
    "secret key") retrieved from the session. If the "secret key" does
    not already exist in the session, create the random value and store
    it in the session. Implementations may choose to produce a more
    complex token value by combining the random "secret key" with other
    values.



-- 
| edward.burns_at_oracle.com | office: +1 407 458 0017
| homepage:               | http://ridingthecrest.com/
| 21 business days until JSF 2.2 Early Draft Review
| 25 business days until JavaOne 2011