Allow for the encryption/decryption of secret data using a session-based key #686

Closed
opened 2025-12-29 16:24:46 +01:00 by adam · 2 comments
Owner

Originally created by @jeremystretch on GitHub (Feb 3, 2017).

Background

The "secrets" app in NetBox allows for the secure storage of sensitive data. This is accomplished by symmetrically encrypting the payload of each Secret object with a symmetric key known only to NetBox. Each user can provide a personal RSA key pair to store his or her own encrypted copy of the master key, thus allowing users to encrypt and decrypt secret data without exposing the master key.

Currently, NetBox requires a user to POST his or her private RSA key for each request that requires encryption or decryption of a secret. To avoid requiring manual re-entry of the key each time, the key is cached locally using HTML5's local storage. This has two disadvantages:

  • The local storage scope is limited to the active browser tab. If the user opens a second tab to access NetBox secrets, he or she must re-enter the private key so that it can be stored locally for that tab. (This is a limitation of the HTML5 implementation and not something that can be controlled by NetBox.)

  • The private key must be re-transmitted as part of each request. This introduces significant overhead as the encoded form of a private key is typically several kilobytes in size. Additionally, it necessitates the use of POST requests, because data appended to the body of GET requests may or may not be honored by a web server. (Notably, jQuery also does not support sending AJAX GET requests with data in the body.)

This second point has become particularly problematic while developing API 2.0. In adherence with REST methodology, the retrieval of objects should be done using only GET requests. This hampers our ability to provide a decryption key along with the request, as there's no good place to put it:

  • We can't put it in the request body, since this is a GET request and it might be ignored by the server.
  • We can't put it in the query string, because it may be logged or otherwise recorded.
  • We can't put it in a cookie, because it may be stored locally on the user's machine (and it's relatively large).
  • We probably don't want to pass it as a custom header due to its size (and the need to re-encode the key to avoid line breaks).

The Proposed Solution

I'd like to implement a new model, SessionKey, to store a copy of the master key which has been encrypted using a temporary symmetric key tied to a specific user. The proposed workflow is:

  • The user provides his or her private key to the client (e.g. the web browser).
  • The client POSTs the private key to a designated API endpoint.
  • NetBox retrieves the master encryption key using the POSTed private key.
  • NetBox generates a random symmetric encryption key, encrypts the master key, and stores the encrypted copy in the database as a SessionKey object.
  • The randomly-generated symmetric encryption key is returned to the user in the API response.

This approach essentially replaces the user's large and very sensitive private RSA key with a small throwaway key which is much more practical to transmit with each request. If the key is lost, it is easily replaced by POSTing the user's private key via the API again to generate a new one.

(Why not replace the RSA scheme altogether? The public/private key pair approach is needed to allow for a single party to activate new UserKeys and to re-key the master key should the need arise. Storing only a symmetrically-encrypted key would require all users to provide their personal keys in either instance.)

All requests to retrieve secret data from NetBox are checked for the presence of the session key. If the key is absent, secret objects are returned without being decrypted. If the key is present, NetBox will attempt to decrypt the requested secret data. (Note that the session key is used only for encryption/decryption of secret data and plays no part in authentication. API 2.0 will utilize tokens for authentication.)

Upon creation, each SessionKey will be assigned an administratively-configurable expiration time. An expired key cannot be used to retrieve secret data and will be subject to deletion from the database. This has the added benefit of enabling a user or administrator to deactivate a key without the user needing to generate a new RSA key pair.

It's possible that I've overlooked something, but I think it's a decent plan to improve the current scheme. Interested to hear what others think.

Originally created by @jeremystretch on GitHub (Feb 3, 2017). ## Background The "secrets" app in NetBox allows for the secure storage of sensitive data. This is accomplished by symmetrically encrypting the payload of each Secret object with a symmetric key known only to NetBox. Each user can provide a personal RSA key pair to store his or her own encrypted copy of the master key, thus allowing users to encrypt and decrypt secret data without exposing the master key. Currently, NetBox requires a user to POST his or her private RSA key for each request that requires encryption or decryption of a secret. To avoid requiring manual re-entry of the key each time, the key is cached locally using [HTML5's local storage](http://www.w3schools.com/html/html5_webstorage.asp). This has two disadvantages: * The local storage scope is limited to the active browser tab. If the user opens a second tab to access NetBox secrets, he or she must re-enter the private key so that it can be stored locally for that tab. (This is a limitation of the HTML5 implementation and not something that can be controlled by NetBox.) * The private key must be re-transmitted as part of each request. This introduces significant overhead as the encoded form of a private key is typically several kilobytes in size. Additionally, it necessitates the use of POST requests, because data appended to the body of GET requests [may or may not be honored by a web server](http://stackoverflow.com/questions/978061/http-get-with-request-body). (Notably, jQuery also does not support sending AJAX GET requests with data in the body.) This second point has become particularly problematic while developing API 2.0. In adherence with REST methodology, the retrieval of objects should be done using only GET requests. This hampers our ability to provide a decryption key along with the request, as there's no good place to put it: * We can't put it in the request body, since this is a GET request and it might be ignored by the server. * We can't put it in the query string, because it may be logged or otherwise recorded. * We can't put it in a cookie, because it may be stored locally on the user's machine (and it's relatively large). * We probably don't want to pass it as a custom header due to its size (and the need to re-encode the key to avoid line breaks). ## The Proposed Solution I'd like to implement a new model, SessionKey, to store a copy of the master key which has been encrypted using a temporary symmetric key tied to a specific user. The proposed workflow is: * The user provides his or her private key to the client (e.g. the web browser). * The client POSTs the private key to a designated API endpoint. * NetBox retrieves the master encryption key using the POSTed private key. * NetBox generates a random symmetric encryption key, encrypts the master key, and stores the encrypted copy in the database as a SessionKey object. * The randomly-generated symmetric encryption key is returned to the user in the API response. This approach essentially replaces the user's large and very sensitive private RSA key with a small throwaway key which is much more practical to transmit with each request. If the key is lost, it is easily replaced by POSTing the user's private key via the API again to generate a new one. (Why not replace the RSA scheme altogether? The public/private key pair approach is needed to allow for a single party to activate new UserKeys and to re-key the master key should the need arise. Storing only a symmetrically-encrypted key would require all users to provide their personal keys in either instance.) All requests to retrieve secret data from NetBox are checked for the presence of the session key. If the key is absent, secret objects are returned without being decrypted. If the key is present, NetBox will attempt to decrypt the requested secret data. (Note that the session key is used only for encryption/decryption of secret data and plays no part in authentication. API 2.0 will utilize tokens for authentication.) Upon creation, each SessionKey will be assigned an administratively-configurable expiration time. An expired key cannot be used to retrieve secret data and will be subject to deletion from the database. This has the added benefit of enabling a user or administrator to deactivate a key without the user needing to generate a new RSA key pair. It's possible that I've overlooked something, but I think it's a decent plan to improve the current scheme. Interested to hear what others think.
adam closed this issue 2025-12-29 16:24:46 +01:00
Author
Owner

@jeremystretch commented on GitHub (Feb 3, 2017):

I've implemented this proposal under the api2 branch in a42eeb12d2.

@jeremystretch commented on GitHub (Feb 3, 2017): I've implemented this proposal under the api2 branch in a42eeb12d2b6cbecbda158a8a0e1dd64b60655ec.
Author
Owner

@quantumzedno commented on GitHub (Sep 18, 2018):

Hi there,

regarding this feature, where exactly can the session key expiration time be configured?

Thank you,

Ben

@quantumzedno commented on GitHub (Sep 18, 2018): Hi there, regarding this feature, where exactly can the session key expiration time be configured? Thank you, Ben
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#686