Securing Sensitive Information in Polyglot Notebooks
Published: 2024-01-05 05:13:43

First, let's tackle the most important question – why did the cat refuse to store its password in Git? Because it heard it's a cat-astrophic idea and wanted to keep its purr-sonal information safe! Jokes aside, the leaking of sensitive information through GitHub and other source repositories is no laughing matter. 

[TLDR; take me to the code!](#what-you-will-need)

The scale of the problem related to the accidental exposure of passwords, API keys, credentials, and other sensitive information on public GitHub repositories is challenging to gauge but remains a significant concern. GitHub addressed this issue by introducing a [secret scanning service](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning) several years ago. Unfortunately, each year brings reports of significant information breaches originating from source repositories. As an illustration, in 2023, AI researchers from [Microsoft inadvertently disclosed](https://fortune.com/2023/09/19/microsoft-ai-researchers-accidental-data-leak/) over 32TB of data, including confidential information, by committing it to a publicly accessible source repository.

Despite the existence of long-standing best practices, interactive coding tools like notebooks raise some unique concerns. When working in notebooks, one might find themselves in the informal atmosphere reminiscent of a lone graduate student hastily crafting solutions during late-night sessions, rather than engaging with code that has undergone rigorous engineering, testing, and scrutiny through formal code review processes. While the user-friendly nature of notebooks makes them an enjoyable and practical tool, it is precisely this informality that introduces the risk of inadvertently exposing sensitive information.

Developers utilizing .NET have long benefitted from a handy tool called User Secrets. This tool serves the purpose of relocating sensitive information, such as API keys, entirely out of the code project. Instead, the information undergoes encryption and is securely stored in a designated user-level directory. .NET developers can programmatically access this information through the utilization of the special project (`.csproj`) file in conjunction with the Configuration extensions library. 

Harnessing User Secrets in Polyglot Notebooks is surprisingly straightforward, even if it may not be immediately evident. Typically, Polyglot Notebooks aren't paired with the formality of being an integral part of an actual .NET project—a prerequisite for utilizing User Secrets. Although it's feasible to integrate them into a formal project following established development practices, for simplicity and to align with a common user scenario, let's focus on the basics. We'll initiate a temporary project to set up user secrets and then guide you through the process of importing and utilizing them within Polyglot Notebooks.

---
   
###What You Will Need

- Proficiency with the command line
- .NET 8.0 or a later version installed
- Installation of the Polyglot Notebooks extension in Visual Studio Code

#### Create a project

Execute the following from the command line:

```bash
dotnet new console --name temporaryproject
```
####  Initialize User Secrets
Run the following command from the command line:

```bash
dotnet user-secrets init
```

#### Add Your Secrets
Now, include a client ID and an API key in the secrets file. Execute the following from the command line:

```bash
dotnet user-secrets set "ClientId" "1234567"
dotnet user-secrets set "ApiKey" "11992288337744665"
```

#### Add the Unique ID To The Notebook
User Secrets generates a unique ID for each project. Open the project file and copy the value of the secrets ID into your notebook. In your preferred editor, locate the tag named UserSecretsId and copy its value:

```xml
<PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <RootNamespace>temporaryproject</RootNamespace>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <UserSecretsId>7ffbd8ef-bb06-47e8-a5e1-9c3103773101</UserSecretsId>
  </PropertyGroup>
```
 
Copy the value of UserSecretsId into your notebook:

```csharp
string UserSecretsId = "7ffbd8ef-bb06-47e8-a5e1-9c3103773101";
```
At this point, you have the option to either delete the temporary .csproj file or retain it for sharing and updating values as needed.

#### Create a POCO For the Secrets:

```csharp
public class MyCredentials {

  public string ClientId {get; set;} = string.Empty;

  public string ApiKey {get; set;} = string.Empty;
}
```

#### Import the Secrets Into the Notebook
To access the secrets, use a ConfigurationBuilder with User Secrets enabled. Start by adding the necessary packages to your notebook and include the using statements to import into your code:

```csharp
#r "nuget: Microsoft.Extensions.Configuration"
#r "nuget: Microsoft.Extensions.Configuration.Binder"
#r "nuget: Microsoft.Extensions.Configuration.UserSecrets"

using Microsoft.Extensions.Configuration;
```

Create a `ConfigurationBuilder` and load your secrets into an instance of `MyCredentials`:

```csharp
var configuration = new ConfigurationBuilder().
  AddUserSecrets(UserSecretsId).
  Build();

var credentials = configuration.Get<MyCredentials>();
```

That's it! You can find a working example in a notebook [HERE](https://gist.github.com/mjamesharmon/dd2e6d87218633611cc506fec0d0fcdb). 

Thanks for reading! If you have any questions or need further assistance, feel free to reach out.