Preventing ‘token trading’¶
Some WOPI operations, such as GetEcosystem (files), EnumerateAncestors (files), and EnumerateChildren (containers), return URLs that must include WOPI access tokens. WOPI access tokens must always be treated as per-resource, per-user by a WOPI client, and they must always be expected to expire after a period of time. WOPI deliberately does not define a means for a client to refresh a WOPI access token given another WOPI access token.
However, WOPI is also deliberately designed to support navigation from a file or container to the 🔧 Ecosystem endpoint, then back to a container or file. This means that it is possible for a client to ‘refresh’ their WOPI tokens indefinitely unless the WOPI host is careful when issuing new access tokens to mitigate these threats. We refer to this threat as ‘token trading.’
To illustrate the token trading threat, consider the following scenario:
A WOPI client, on behalf of User A, is issued a WOPI access token,
TOKEN1, for the file
Document.docx. The token has a lifetime of 12 hours.
TOKEN1is still valid, the client calls the EnumerateAncestors (files) operation using
TOKEN1as the access token.
The server responds with a URL to the parent container along with a new WOPI access token for that container,
The client then calls EnumerateChildren (containers) using
TOKEN2as the access token.
The server responds with the URL for the children files, including
Document.docx, along with a new access token for
At this point, the client has effectively ‘traded’
TOKEN3. If each token issued has a lifetime of 12
hours, then this means that a client can effectively refresh their access token for a particular file by going through
the container hierarchy, without actually authenticating with the server using a non-WOPI authentication mechanism.
If unmitigated, this scenario greatly increases the potential damage caused by token leakage. If a malicious attacker
gains access to
TOKEN1, then they can potentially access
Document.docx indefinitely, as long as User A
still has access to it. In other words, the attacker can impersonate User A indefinitely.
In addition, an attacker could use EnumerateAncestors (files) and EnumerateChildren (containers) to trade
TOKEN1 for a
token that is valid for any other document in the container hierarchy that the user has access to, then impersonate
User A indefinitely to access those other documents and containers as well.
The preferred way to mitigate this threat is to create new WOPI access tokens with the same token lifetime as the WOPI
access token that was used when the operation was called. In other words, in the scenario described above, the lifetime
TOKEN3 should be the same as
TOKEN1. All three tokens should expire at the same time.
Note that this should only apply when a WOPI access token is used to create another WOPI access token. In other cases where WOPI access tokens are issued, such as from Bootstrap or other OAuth-authenticated Shortcut operations, or, in the case of web-based WOPI clients, by visiting a host page that issues the access token, the host-defined default access token lifetime can apply. This is safe in those cases because there is a separate primary authentication method that is also in use. In the case of Bootstrap, it is an OAuth token. In the case of web-based WOPI clients, it is the host’s standard web authentication system.