Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Warning
title

Duo 'Cancel this Request' issue affecting IdP v4.0.0 and v4.0.1

New installs of version 4.0.0 or 4.0.1 of the IdP (or upgrades from new installs of these versions) are missing the CSRF Token on the 'Cancel this Request' hyperlink in the duo.vm velocity template. This must be added manually, see duo-cancel-request. Note, this does not impact on the successful operation of the Duo 2FA process within the iframe.


Table of Contents

Cross-Site Request Forgery (CSRF)

...

CSRF exploits existing user session identifiers with ambient authority that are provided automatically by the browser during each request, e.g. cookies such as the JSESSIONID and the JSESSIONID and shib_idp_session, or the IP address of the victim. As the attacker has no way of determining/reading the results of that request, useful requests are limited to state-changing, non-idempotent (has side effects) actions that the user is allowed to perform.

Despite the fact the IdP does not itself represent the end resource the user will ultimately interact with, the IdP does present some user flows that could be manipulated by an attacker. For example, of particular importance is the prevention of login CSRF [2], where the attacker attempts to trick the victim into logging in as themselves (the attacker), with the aim then of:

  1. Tracking a victim's activity because it is happening in their own session.

  2. Tricking the victim into entering sensitive information into an

    attackers

    attacker's session/account e.g. bank account details etc.

Consequently, the IdP now employs a synchroniser token pattern [1] CSRF defense. Abstractly, a cryptographically strong string token is generated and attached to the user session by the IdP each time a view is rendered and embedded into HTML forms in the view. When the form is submitted by the user back to the IdP, the IdP verifies that the token in the request matches that stored against the user's session. If they do not match, it is assumed the request was forged - the attacker would not have access to this token, and should not be able to ‘guess’ it.

...

Performing a CSRF attack on the IdP is difficult even without specialised CSRF defence. Firstly, for the most part, the IdP does not have stable, static, endpoints listening for requests (other than for certain RESTFul admin functions). Instead, because of the underlying use of Spring Web Flow, the conversation the browser is having with the IdP must be at the correct flow state to accept a HTTP POST/GET request with a given set of manipulatable parameters. For example, using the password authentication flow, the attack opportunity only occurs when the user has progressed the flow to the the DisplayUsernamePasswordPage view view-state (login page) and no further. It is fairly unlikely, although not impossible, a user would stop at this point and navigate to a malicious website to be tricked into submitting an attackers attacker's username and password back to the IdP.

...

New installations of IdP v4 will have CSRF protection enabled by default. This is configured by a property in in %{idp.home}/conf/idp.properties which  which globally turns on CSRF protection:

...

When enabled, all view-states will automatically be protected protected unless explicitly  explicitly excluded; see excluding-views. This means any custom view-state added to new or existing flows of the IdP will also be protected, and any submissions back to the IdP will require an anti-CSRF token; see adding-csrf-token.


title
Warning

Duo 'Cancel this request' issue affecting IdP v4.0.0 and v4.0.1

New installs of, or upgrades from new installs of, version 4.0.0 or 4.0.1 of the IdP are missing the CSRF Token on the 'Cancel this request' hyperlink in the duo.vm velocity template. This must be added manually, see duo-cancel-request

...

Enabling After Upgrade

Info

The The /dist folder  folder in the installation directory of the IdP contains a fresh copy of the IdP properties and views with CSRF protection enabled. These could be referenced against your current views to highlight the changes required to enable CSRF.

To enable CSRF protection the following property should be added to to %{idp.home}/conf/idp.properties:

Code Block
languageyml
# Enable cross-site request forgery mitigation for views. 
idp.csrf.enabled = true
Note
title

Caution

Enabling CSRF protection and restarting the IdP without modifying required views will break authentication flows.

Once enabled, the following views need to be updated to include the anti-CSRF token inside HTML forms; see adding-csrf-token for more information on injecting the tokens.

View

Number of Forms
(in distribution)

Others

views/login.vm

1


views/duo.vm

1

Is also required in the 'Cancel this request' hyperlink, see DuoCancelRequest.

views/intercept/attribute-release.vm

1


views/intercept/impersonate.vm

1


views/intercept/terms-of-use.vm

2


views/admin/unlock-keys.vm

1


The IdP contains a number of other views from which CSRF has been excluded by configuration; see excluding-views for more information on excluding views.

...

Sometimes views do not require CSRF protection, for example when they do not submit sensitive information back to the IdP. Other times, views are involved in user flows that cannot meaningfully benefit from ‘IdP led’ CSRF protection, for example, an external authentication servlet that reaches outside of the IdP and can include arbitrary user interactions. In either case, views can be excluded by annotating the <view-state> element with the following CSRF annotation attribute in the corresponding flow XML configuration file.

CSRF excluded annotation attribute
Code Block
languagexml
titleCSRF excluded annotation attribute
<attribute <attribute name="csrf_excluded" value="true" type="boolean"/>

For example, to exclude an external authentication servlet:

...

Example: exclude external authentication view
Code Block
linenumberslanguagetruexml
<view-state id="ExternalTransfer" view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.External.externalAuthnPathStrategy').apply(opensamlProfileRequestContext), flowExecutionContext.getKey().toString())}">
        <attribute name="csrf_excluded" value="true" type="boolean"/> <!-- excludes attribute here -->
  ...
</view-state>

...

HTML forms within protected views must include a hidden input field with both the anti-CSRF token (value attribute) and the HTTP parameter name (name attribute). The IdP comes with a velocity template fragment which adds the anti-CSRF token into a form if present. To add it, simply place the following velocity script element element inside the  the form where it is required:

...

For example, inside the login form:

Code Block
languagexmllinenumberstrue
<form action="$flowExecutionUrl" method="post">
            #parse("csrf/csrf.vm")
            ....
  </form>

If protection is enabled, the token will then be rendered similar to the output below:Image Removed

...


Info

The csrf.vm template can be added (using the #parse directive) safely to forms even if protection is disabled. In these cases, Velocity will not add the token to the form.

Iframe source

Iframes that return control to the IdP in the source attribute also require CSRF tokens unless their parent view-state is excluded from protection. This is required even in a GET request as Spring Web Flow does not differentiate between GET and POST to initiate or resume flows. These can be added with conditional velocity logic e.g.

...

It is unlikely however that iframe usage benefits much from CSRF protection, and most often the view-state can be excluded. That said, the implementer of a custom flow and or view is responsible for determining that.

title
Note

Token Exposure

This will expose the CSRF token in browser network inspectors and possibly server logs. However, it will not display in the navigation bar, and should not appear in browser history/bookmarks. That said, a new token is generated for every GET request to a view, greatly limiting its reply value.

...

Code Block
<a href="$flowExecutionUrl&_eventId=cancel#parse("csrf/csrf-qparam.vm")">TEXT</a>

Anchor
DuoAuthHref
DuoAuthHref
Issue: Duo 2FA 'Cancel this Request'

...

To cleanly handle the InvalidCSRFTokenException the IdP throws on a CSRF token mismatch, a global on-exception transition must be registered in appropriate flows in addition to a corresponding action/end state. In subflows, this can be added to the list of error events that are reflected back to the parent flow. For example:

...

Catching a CSRF exception in subflows
linenumbers
Code Block
true
languagecollapsetruexml
<end-state id="InvalidCSRFToken" />
 
 <global-transitions>
   <transition on-exception="net.shibboleth.idp.ui.csrf.InvalidCSRFTokenException" to="InvalidCSRFToken" />
<transition on-exception="java.lang.RuntimeException" to="LogRuntimeException" /> <!-- must be declared before this transition -->
   <transition on="InvalidCSRFToken" to="InvalidCSRFToken" />
   ...

In abstract parent flows, this can be added to the global-transitions transitioning to a new action state:

...

Catching a CSRF exception in parent flows
linenumbers
Code Block
true
languagecollapsetruexml
<!-- action state to set the InvalidCSRFToken event and proceed to HandleError -->
    <action-state id="InvalidCSRFToken">
        <evaluate expression="'InvalidCSRFToken'" />
        <transition to="HandleError"/>
    </action-state>
 
<!-- Default is to turn non-proceed events into an error. -->
    <global-transitions>
        <transition  on-exception="net.shibboleth.idp.ui.csrf.InvalidCSRFTokenException" to="InvalidCSRFToken"/> <!--HERE-->
        <transition on-exception="java.lang.RuntimeException" to="LogRuntimeException" />
        <transition on="#{!'proceed'.equals(currentEvent.id)}" to="HandleError" />
    </global-transitions>
Note

You are still protected from CSRF attacks even if you do not catch the exception, however, a generic uncaught exception error will be shown to the user instead.


References

1 - https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.md#synchronizer-token-pattern
2 - https://seclab.stanford.edu/websec/csrf/csrf.pdf