Announcement Announcement Module
Collapse
No announcement yet.
Protection Token implementation Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Protection Token implementation

    Unless I've missed something, neither SWF, Spring MVC, nor vanilla JSF implementations offer a built-in token mechanism, as in Struts. A token mechanism can be used to deal with multiple-submit issues on a page and to act as a defense to Cross Site Request Forgery (CSRF) attacks.

    SWF's execution keys almost work as CSRF protection tokens, but execution keys increment predictably and they may stick around for a while, depending on your maximum executions and snapshots settings.

    I haven't heard any followup on efforts to manually generate your own flow execution IDs, and even then that doesn't solve the multiple-submit issue.

    I've got a potential solution, but it's still semi-automatic and doesn't completely address multiple submits. I thought I'd post it here and get some feedback and maybe suggestions for a better approach.

    In transitions with the attribute "TOKEN_PROTECTED", it attempts to match the "GUARD_TOKEN" requestParameter with an attribute of the same name in viewScope. Through blatant hackery, the most I could get it to do was refresh the current page if the tokens don't match, and the act of refreshing the page brings the tokens back in sync, so it's a little weak for preventing multiple-submits, but then you'd probably use history="invalidate" for that anyway.

    Code:
    /**
     * Generates guard tokens on view render (set as "GUARD_TOKEN" in viewScope)
     * and checks for a valid token requestParameter in transitions with the attribute
     * "TOKEN_PROTECTED". If no valid token is found, the transition is cancelled.  
     */
    public class FlowTokenGuardian extends FlowExecutionListenerAdapter {
      private static final String GUARD_TOKEN = "GUARD_TOKEN";
    
      public void viewRendering(RequestContext requestContext, View view, 
          StateDefinition stateDefinition) {
          
        requestContext.getViewScope().put(GUARD_TOKEN, UUID.randomUUID());
      }
    
      public void transitionExecuting(RequestContext requestContext, 
          TransitionDefinition transitionDefinition) {
    
        if (transitionDefinition.getAttributes().contains("TOKEN_PROTECTED")) {
          String guardTokenString = requestContext.getRequestParameters().get(GUARD_TOKEN);
          UUID guardToken = guardTokenString != null ? UUID.fromString(guardTokenString) : null;
          UUID viewToken = (UUID) requestContext.getViewScope().get(GUARD_TOKEN);
        
          if (guardToken == null || viewToken == null || !guardToken.equals(viewToken)) {
            Transition transition = (Transition) transitionDefinition;
            transition.setExecutionCriteria(new VetoedExecutionCriteria(transition));
          }
        }
      }
    }
    Code:
    /**
     * Performs a one-time veto of the transition, then restores the previous
     * execution criteria.
     */
    public class VetoedExecutionCriteria implements TransitionCriteria {
      private Transition transition;
      private TransitionCriteria previousExecutionCriteria;
      
      public VetoedExecutionCriteria(Transition transition) {
        this.transition = transition;
        this.previousExecutionCriteria = transition.getExecutionCriteria();
      }
      
      /**
       * Restores the previously set execution criteria, but returns false this one
       * time.
       * @param requestContext
       * @return false
       */
      public boolean test(RequestContext requestContext) {
        transition.setExecutionCriteria(previousExecutionCriteria);
        return false;
      }
    }

  • #2
    Just realized my hack with VetoedExecutionCriteria isn't going to work. I think the Transition object is shared between all users, so my implementation would introduce a race condition which could cause some users to see their transition randomly fail and refresh the page with no feedback.

    Anyone have any thoughts on how to cancel a transition from inside a FlowExecutionListener?

    Comment

    Working...
    X