DocuSign API Integration Security: Input Validation
Earlier this month the OWASP (Open Web Application Security Project) Foundation released an update to their “Top Ten” project (https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project), describing the top ten most critical web application security risks. Although the update is only a release candidate (the final version is expected to land in July or August), I do not expect it to change much.
I’ve read a few articles questioning the relevance of the update (see “OWASP Top 10 Update: Long Overdue Or Same-Old, Same-Old?”) – indeed the updated list does look very similar to the 2013 release with the only differences being the inclusion of “insufficient attack protection” and “under protected APIs”, and the merging of “insecure direct object references” with “missing function level access control”:
Source: OWASP Top 10 - 2017 Release Candidate
In my opinion, the OWASP Top Ten is undoubtedly still useful to broadly describe areas of vulnerability. While it may not hold true that if you’ve covered the top ten, your application is secure, the converse certainly holds true: if you’ve paid no attention to these areas, you will have security flaws.
I’ll be giving a talk in the developer track at this year’s Momentum (see below for more info) on how to ensure your DocuSign integrations are secure and in homage to the OWASP Top Ten, I’ll be mapping the issues I discuss back to vulnerability categories.
One of the themes of my talk will be the necessity of strong, consistent input validation. Let’s take a look at an example:
The DocuSign REST API URL format is coupled to the resource to be acted upon and most resource identifiers in our system are globally unique identifiers (GUIDs), e.g.:
-
https://www.docusign.net/restapi/v2/accounts/ba6a6041-fdc8-435b-966b-448f34ac1ff8
-
https://www.docusign.net/v2/accounts/ba6a6041-fdc8-435b-966b-448f34ac1ff8 envelopes/ccab2c6e-f55a-4e66-be3f-52402d58b5e3/documents/8d4e9422-f626-42a3-ae89-bec8e0f6fa5d/fields
In a typical integration, some of the resource identifiers will be user-supplied, e.g.:
- The user edits an envelope resulting in your application making a PUT to: /v2/accounts/{accountId}/envelopes/{envelopeId} with a user-controlled envelopeId.
- The user selects an envelope to sign resulting in your application making a POST to /v2/accounts/{accountId}/envelopes/{envelopeId}/views/recipient with a user-controlled envelopeId.
- The admin user removes a Connect configuration from their account resulting in your application making a DELETE to /v2/accounts/{accountId}/connect/{connectId}.
So what’s the concern? Well if your application builds a URL without validating user-supplied input, a malicious user can inject into the URL path! Let’s look at a simple example:
Assume your account ID is “416e68ee-2728-4231-8ed1-01f0809dced2” and you are making a request to /v2/accounts/ 416e68ee-2728-4231-8ed1-01f0809dced2/envelopes/{envelopeId}/views/recipient. The {envelopeId} string is replaced with a user-supplied envelope ID, without sufficient input validation.
A malicious user supplies the following envelope Id:
../users?
Your application will end up sending the following request to the DocuSign REST API:
https://www.docusign.net/restapi/v2/accounts/416e68ee-2728-4231-8ed1-01f0809dced2/envelopes/../users?/views/recipientOur API servers will then canonicalize the URL. During this process parent path sequences (../) are collapsed and URL encoded characters are decoded. The REST API will therefore end up processing the following request:
https://www.docusign.net/restapi/v2/accounts/416e68ee-2728-4231-8ed1-01f0809dced2/users
The request has been sent to the “users” endpoint instead of the “envelopes” endpoint! If the request is made in the admin context, this could allow an attacker to add a new admin user to the account or modify another resource.
Fortunately, simple best practices around input validation can prevent this type of attack. When accepting user input, your application should ensure that the data matches the expected format. In this case, you should validate that the envelope ID – and any other user supplied resources – are indeed GUIDs. Here is sample validation code in C#, Java, Node.js and Apex:
C#
// DO THIS if (!Guid.TryParse(envelopeIdString, out envelopeId)) { // Not a valid envelope Id }
Java
// DO THIS try { UUID.FromString(envelopeId); } catch (IllegalArgumentException iaex) { // Not a valid envelope Id }
Node.js
// DO THIS // <https://www.npmjs.com/package/guid> if (!Guid.isGuid(envelopeIdString)) { // Not a valid envelope Id }
Apex
// DO THIS Pattern p = Pattern.compile('^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$'); Matcher pm = p.matcher(envelopeIdString); if (!pm.matches()) { // Not a valid envelope Id }
I mentioned that I’ll be mapping examples back to Top Ten categories, so where does this one fit? Take a look at the list at the beginning of this blog post and figure out where you’d put this example on the list. Then come along to the DocuSign Momentum conference from May 3-4 in San Francisco and catch my talk! Developers can attend the conference (and my session) for free by registering here.