Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-32776

Jenkins shouldn't store API tokens in a recoverable format

    • Icon: Improvement Improvement
    • Resolution: Fixed
    • Icon: Major Major
    • core
    • Platforms: Jenkins
      Versions: 1.620, 1.622

      Submitting as delegate for a Cisco pen-testing team
      This is a modification on the product to adopt secure best practices to enhance the security posture and resiliency of the product.

      Headline: Jenkins stores API tokens in a recoverable format
      Platforms: Jenkins
      Versions: 1.620, 1.622
      CWE Tags: CWE-257

      Jenkins allows users to authenticate via multiple credentials including a
      Jenkins-generated API token. This API token provides the user with a fully
      authenticated session (the same as if the user had logged in using a password).

      Although the user's password and API token provide equivalent access to Jenkins,
      these credentials are not stored with equivalent security protections. User
      passwords are stored as salted hashes (SHA-256 or bcrypt); whereas API tokens
      are encrypted using an AES-128 ECB-mode block cipher, using a static key shared
      among all users. As a result, an attacker with sufficient access to internal
      Jenkins datastructures can decrypt API tokens and impersonate any user.

      User credentials should never be stored in a recoverable format (e.g. encrypted)
      unless the application requires them to authenticate on behalf of the user (e.g.
      to authenticate to an external system). Jenkins does not appear to have any
      such reason for storing API tokens in a recoverable format, as the API token
      appears to only be used for inbound authentication.

      It is assumed that Jenkins developers may have chosen to store API tokens in a
      recoverable form in order to implement the "Show API Token..." functionality
      contained on the user configuration page. This is a dangerous feature, as an
      application should never display stored user credentials via the UI. If an
      attacker were able to gain unauthorized access to an authenticated session, he
      could utilize this feature to trivially recover the user's credential.

      Just as a user never needs an application to display his stored password, a user
      should never need an application to display a previously stored API token. When
      first generated, the application can display the API token to the user. If he
      loses/forgets his API token, a user can request a new token be generated (and
      displayed). If it's deemed useful to allow the user to confirm the value of his
      API token, Jenkins could store a few characters of the API token that could be
      displayed to the user (e.g. similar to displaying the last 4 digits of a credit
      card).

      It is recommended that Jenkins securely store user API tokens in a
      non-recoverable form (e.g. using a strong, salted, one-way hash). Making this
      change will necessitate removal of the "Show API Token..." feature, which can be
      simply replaced with the existing "Change API Token" feature.

      References:
      [1] http://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
      [2] http://www.owasp.org/index.php/Storing_passwords_in_a_recoverable_format
      [3] http://www.owasp.org/index.php/Top_10_2013-A2-Broken_Authentication_and_Session_Management
      [4] http://cwe.mitre.org/data/definitions/257.html

          [JENKINS-32776] Jenkins shouldn't store API tokens in a recoverable format

          Daniel Beck added a comment -

          crlorent Thanks for the report! I have a few questions regarding this:

          Just as a user never needs an application to display his stored password

          A password is typically chosen by the user, and sometimes re-used between services, while they have no control over the API token. Does that not make a significant difference?

          As a result, an attacker with sufficient access to internal Jenkins datastructures can decrypt API tokens and impersonate any user.

          The same user will likely always be able to execute arbitrary code, at least by installing a rogue plugin, so I'm not really convinced that this is a problem.


          Would you consider this a vulnerability? It doesn't look like it is to me, best practices or not. As mentioned last time, we're tracking actual vulnerabilities in this tracker (those that we want to keep private until release of a fix), and security best practices/hardening/… are tracked in JENKINS using the security label. So I'd move this over if not a vulnerability.

          Daniel Beck added a comment - crlorent Thanks for the report! I have a few questions regarding this: Just as a user never needs an application to display his stored password A password is typically chosen by the user, and sometimes re-used between services, while they have no control over the API token. Does that not make a significant difference? As a result, an attacker with sufficient access to internal Jenkins datastructures can decrypt API tokens and impersonate any user. The same user will likely always be able to execute arbitrary code, at least by installing a rogue plugin, so I'm not really convinced that this is a problem. Would you consider this a vulnerability ? It doesn't look like it is to me, best practices or not. As mentioned last time, we're tracking actual vulnerabilities in this tracker (those that we want to keep private until release of a fix), and security best practices/hardening/… are tracked in JENKINS using the security label. So I'd move this over if not a vulnerability.

          Craig Lorentzen added a comment - - edited

          I would be uncomfortable publicly disclosing how a credential obfuscation technique works without having new code that makes that credential unrecoverable. We know that a malicious actor could determine this from the source code but, we should make them do that leg work.

          If I remember correctly, AWS doesn't provide a recovery technique for tokens, they follow the best practice that is laid out in this defect, only providing the token on first creation and storing it in a non-recoverable format. If a user loses their token, then they should replace it with a new token. In the end, Jenkin's will have to decide what they want to do with regards to this.

          Craig Lorentzen added a comment - - edited I would be uncomfortable publicly disclosing how a credential obfuscation technique works without having new code that makes that credential unrecoverable. We know that a malicious actor could determine this from the source code but, we should make them do that leg work. If I remember correctly, AWS doesn't provide a recovery technique for tokens, they follow the best practice that is laid out in this defect, only providing the token on first creation and storing it in a non-recoverable format. If a user loses their token, then they should replace it with a new token. In the end, Jenkin's will have to decide what they want to do with regards to this.

          Craig Lorentzen added a comment - - edited

          A password is typically chosen by the user, and sometimes re-used between services, while they have no control over the API token. Does that not make a significant difference?

          Response from the pen tester

          It makes a difference only in the sense that the disclosure
          of an API token definitely only affects Jenkins, whereas the
          disclosure of a password potentially affects other services.
          Both the API token and the password give you the same access to
          the Jenkins service, so from Jenkins's perspective both need
          equal protection.

          I doubt this really makes much difference in terms of the
          severity though. In many (maybe most?) cases, severity is
          assessed only in terms of the impact on the application itself
          (and not on other applications). For example, CVSS score
          wouldn't change if the exposure impacted only Jenkins (API token)
          or if it impacted Jenkins and potentially other services (e.g.
          password).

          =====

          The same user will likely always be able to execute arbitrary code, at least by installing a rogue plugin, so I'm not really convinced that this is a problem.

          Response from the pen tester

          This is definitely not true. A great example is that an
          attacker may gain access to a Jenkins backup (e.g. not stored on
          the Jenkins server). If an administrator wants to be able to
          fully restore from this backup, then it must necessarily contain
          the encryption key and encrypted API tokens for all users. By
          gaining access to this backup, the attacker can now trivially
          decrypt the API tokens and impersonate any user (including
          administrators). However, readonly access to this backup doesn't
          otherwise give the attacker any privilege to Jenkins.

          Craig Lorentzen added a comment - - edited A password is typically chosen by the user, and sometimes re-used between services, while they have no control over the API token. Does that not make a significant difference? Response from the pen tester It makes a difference only in the sense that the disclosure of an API token definitely only affects Jenkins, whereas the disclosure of a password potentially affects other services. Both the API token and the password give you the same access to the Jenkins service, so from Jenkins's perspective both need equal protection. I doubt this really makes much difference in terms of the severity though. In many (maybe most?) cases, severity is assessed only in terms of the impact on the application itself (and not on other applications). For example, CVSS score wouldn't change if the exposure impacted only Jenkins (API token) or if it impacted Jenkins and potentially other services (e.g. password). ===== The same user will likely always be able to execute arbitrary code, at least by installing a rogue plugin, so I'm not really convinced that this is a problem. Response from the pen tester This is definitely not true. A great example is that an attacker may gain access to a Jenkins backup (e.g. not stored on the Jenkins server). If an administrator wants to be able to fully restore from this backup, then it must necessarily contain the encryption key and encrypted API tokens for all users. By gaining access to this backup, the attacker can now trivially decrypt the API tokens and impersonate any user (including administrators). However, readonly access to this backup doesn't otherwise give the attacker any privilege to Jenkins.

          Jesse Glick added a comment -

          readonly access to this backup doesn't otherwise give the attacker any privilege to Jenkins

          Sure it does. If you have access to a $JENKINS_HOME backup you can easily take over the system in various ways.

          FWIW the CloudBees Jenkins Enterprise backup plugin offers an option to exclude the (write-once) secrets/master.key from the backup for this reason. You can put it on a secured USB key and drop it in a safe, then back up everything else to a less-secure storage system.

          I am inclined to reject this as not a defect.

          Jesse Glick added a comment - readonly access to this backup doesn't otherwise give the attacker any privilege to Jenkins Sure it does. If you have access to a $JENKINS_HOME backup you can easily take over the system in various ways. FWIW the CloudBees Jenkins Enterprise backup plugin offers an option to exclude the (write-once) secrets/master.key from the backup for this reason. You can put it on a secured USB key and drop it in a safe, then back up everything else to a less-secure storage system. I am inclined to reject this as not a defect.

          Daniel Beck added a comment -

          crlorent Thanks for the update!

          I am inclined to reject this as not a defect.

          While I don't expect there's a significant risk of this being exploited (requiring keeping this information private for the protection of Jenkins users until there's a coordinated release of all Jenkins release lines), this looks like a legitimate hardening/best practice recommendation. Therefore I suggest this be moved into JENKINS (public tracker) as an improvement of API tokens. WDYT?

          Daniel Beck added a comment - crlorent Thanks for the update! I am inclined to reject this as not a defect. While I don't expect there's a significant risk of this being exploited (requiring keeping this information private for the protection of Jenkins users until there's a coordinated release of all Jenkins release lines), this looks like a legitimate hardening/best practice recommendation. Therefore I suggest this be moved into JENKINS (public tracker) as an improvement of API tokens. WDYT?

          danielbeck, I appreciate the lively discussion on this issue and will defer to the Jenkin's team on where the JIRA issue ends up.

          Craig Lorentzen added a comment - danielbeck , I appreciate the lively discussion on this issue and will defer to the Jenkin's team on where the JIRA issue ends up.

          New API Token system proposed. User will have the capability to have multiple API Token (with a name) that are working like in GitHub, meaning they can see the token only after the creation. Only the SHA-256 hashes are stored in the server for the verification but the token cannot be displayed anymore.

          In order to keep the current (legacy) behavior, the users with a legacy token can still display its value. If the corresponding API Token Property Configuration option is enabled, they can create new legacy token even if they do not have one anymore (to ease migration) but once disabled and the users have no legacy token, there is no option to create new ones.

          See the attached PR to have more details.

          Wadeck Follonier added a comment - New API Token system proposed. User will have the capability to have multiple API Token (with a name) that are working like in GitHub, meaning they can see the token only after the creation. Only the SHA-256 hashes are stored in the server for the verification but the token cannot be displayed anymore. In order to keep the current (legacy) behavior, the users with a legacy token can still display its value. If the corresponding API Token Property Configuration option is enabled, they can create new legacy token even if they do not have one anymore (to ease migration) but once disabled and the users have no legacy token, there is no option to create new ones. See the attached PR to have more details.

          Correction released in 2.129.

          Wadeck Follonier added a comment - Correction released in 2.129.

            wfollonier Wadeck Follonier
            crlorent Craig Lorentzen
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: