Can you keep a secret?

A presentation at PyCon Australia in September 2020 in by Aaron Bassett

Slide 1

Slide 1

Can you keep a secret? @aaronbassett

Slide 2

Slide 2

Aaron Bassett SENIOR Developer Advocate

Slide 3

Slide 3

Slide 4

Slide 4

import pprint from pymongo import MongoClient client = MongoClient( “C01.5tsil.mongodb.net”, username=”admin”, password=”hunter2” ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 5

Slide 5

import pprint from pymongo import MongoClient client = MongoClient( “C01.5tsil.mongodb.net”, username=”admin”, password=”hunter2” ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 6

Slide 6

import pprint from pymongo import MongoClient client = MongoClient( “C01.5tsil.mongodb.net”, username=”admin”, password=”hunter2” ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 7

Slide 7

import pprint from pymongo import MongoClient client = MongoClient( “C01.5tsil.mongodb.net”, username=”admin”, password=”hunter2” ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 8

Slide 8

import pprint from pymongo import MongoClient client = MongoClient( “C01.5tsil.mongodb.net”, username=”admin”, password=”hunter2” ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 9

Slide 9

import pprint from pymongo import MongoClient DB_HOST = “C01.5tsil.mongodb.net” DB_USERNAME = “admin” DB_PASSWORD = “hunter2” client = MongoClient(DB_HOST, username=DB_USERNAME, password=DB_PASSWORD) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 10

Slide 10

import pprint from pymongo import MongoClient DB_HOST = “C01.5tsil.mongodb.net” DB_USERNAME = “admin” DB_PASSWORD = “hunter2” client = MongoClient(DB_HOST, username=DB_USERNAME, password=DB_PASSWORD) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 11

Slide 11

git add . git commit -m “wip” git push

Slide 12

Slide 12

“the median time to discovery was 20 seconds, with times ranging from half a second to over 4 minutes” – How Bad Can It Git? Characterizing Secret Leakage in Public GitHub Repositories

Slide 13

Slide 13

Not safe, BUT VERY easy

Slide 14

Slide 14

Functionality Security Usability

Slide 15

Slide 15

Low Friction

Slide 16

Slide 16

Easy to implement

Slide 17

Slide 17

12 Factor Apps

Slide 18

Slide 18

I. CODEBASE II. DEPENDENCIES III. CONFIG IV. BACKING SERVICES V. BUILD, RELEASE, RUN VI. PROCESSES VII. PORT BINDING VIII. CONCURRENCY IX. DISPOSABILITY X. DEV/PROD PARITY XI. LOGS XII. ADMIN PROCESSES

Slide 19

Slide 19

III. Config

Slide 20

Slide 20

“A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials.” –The twelve-factor app

Slide 21

Slide 21

environment variables

Slide 22

Slide 22

import os import pprint from pymongo import MongoClient client = MongoClient( os.environ[“DB_HOST”], username=os.environ[“DB_USERNAME”], password=os.environ[“DB_PASSWORD”], ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 23

Slide 23

import os import pprint from pymongo import MongoClient client = MongoClient( os.environ[“DB_HOST”], username=os.environ[“DB_USERNAME”], password=os.environ[“DB_PASSWORD”], ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 24

Slide 24

import os import pprint from pymongo import MongoClient client = MongoClient( os.environ.get(“DB_HOST”), username=os.environ.get(“DB_USERNAME”), password=os.environ.get(“DB_PASSWORD”), ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 25

Slide 25

import os import pprint from pymongo import MongoClient client = MongoClient( os.environ.get(“DB_HOST”), username=os.environ.get(“DB_USERNAME”), password=os.environ.get(“DB_PASSWORD”), ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 26

Slide 26

import os import pprint from pymongo import MongoClient client = MongoClient( os.getenv(“DB_HOST”), username=os.getenv(“DB_USERNAME”), password=os.getenv(“DB_PASSWORD”), ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 27

Slide 27

import os import pprint from pymongo import MongoClient client = MongoClient( os.getenv(“DB_HOST”), username=os.getenv(“DB_USERNAME”), password=os.getenv(“DB_PASSWORD”), ) db = client.geo_example query = {“loc”: {“$within”: {“$center”: [[0, 0], 6]}}} for doc in db.places.find(query).sort(“_id”): pprint.pprint(doc)

Slide 28

Slide 28

def getenv(key, default=None): “”“Get an environment variable, return None if it doesn’t exist. The optional second argument can specify an alternate default. key, default and the result are str.”“” return environ.get(key, default)

Slide 29

Slide 29

def getenv(key, default=None): “”“Get an environment variable, return None if it doesn’t exist. The optional second argument can specify an alternate default. key, default and the result are str.”“” return environ.get(key, default)

Slide 30

Slide 30

Creating environment variables

Slide 31

Slide 31

This file must be used with “source bin/activate” from bash # you cannot run it directly deactivate () { &&… # Unset variables unset NEXMO_KEY unset NEXMO_SECRET unset MY_NUMBER } &&… export NEXMO_KEY=”a925db1ar392” export NEXMO_SECRET=”01nd637fn29oe31mc721” export MY_NUMBER=”447700900981”

Slide 32

Slide 32

This file must be used with “source bin/activate” from bash # you cannot run it directly deactivate () { &&… # Unset variables unset NEXMO_KEY unset NEXMO_SECRET unset MY_NUMBER } &&… export NEXMO_KEY=”a925db1ar392” export NEXMO_SECRET=”01nd637fn29oe31mc721” export MY_NUMBER=”447700900981”

Slide 33

Slide 33

This file must be used with “source bin/activate” from bash # you cannot run it directly deactivate () { &&… # Unset variables unset NEXMO_KEY unset NEXMO_SECRET unset MY_NUMBER } &&… export NEXMO_KEY=”a925db1ar392” export NEXMO_SECRET=”01nd637fn29oe31mc721” export MY_NUMBER=”447700900981”

Slide 34

Slide 34

“direnv is an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.” – direnv.net

Slide 35

Slide 35

$ echo export DB_PASSWORD=hunter2 > .envrc

Slide 36

Slide 36

$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed

Slide 37

Slide 37

$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow .

Slide 38

Slide 38

$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +DB_PASSWORD

Slide 39

Slide 39

$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +DB_PASSWORD $ cd &..

Slide 40

Slide 40

$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +DB_PASSWORD $ cd &.. direnv: unloading

Slide 41

Slide 41

Ignore .envrc echo .envrc > ~/.gitignore git config &—global core.excludesfile ~/.gitignore

Slide 42

Slide 42

Example .envrc grep -ohr “^export .*=” .envrc > .envrc.example

Slide 43

Slide 43

Sharing Secret Files

Slide 44

Slide 44

Encryption at Rest

Slide 45

Slide 45

Client-Side Field Level Encryption

Slide 46

Slide 46

USE A KMS

Slide 47

Slide 47

openssl rand -base64 32 > mongodb-keyfile chmod 600 mongodb-keyfile mongod &—enableEncryption &—encryptionKeyFile mongodb-keyfile

Slide 48

Slide 48

GIT-SECRET.IO

Slide 49

Slide 49

“git-secret encrypts files and stores them inside the git repository, so you will have all the changes for every commit.” –git-secret.io

Slide 50

Slide 50

Safe & Easy

Slide 51

Slide 51

Safe & Easy-ish

Slide 52

Slide 52

PGP

Slide 53

Slide 53

$ git secret init git-secret: init created: ‘/myproject/.gitsecret/’

Slide 54

Slide 54

$ git secret tell me@aaronbassett.com gpg: marginals needed: 3 completes needed: 1 trust model: pgp gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u gpg: next trustdb check due at 2021-09-30 gpg: keybox ‘/myproject/.gitsecret/keys/pubring.kbx’ created gpg: /myproject/.gitsecret/keys/trustdb.gpg: trustdb created git-secret: done. me@aaronbassett.com added as user(s) who know the secret.

Slide 55

Slide 55

$ git secret add mongodb-keyfile git-secret: these files are not in .gitignore: mongodb-keyfile git-secret: auto adding them to .gitignore git-secret: 1 item(s) added.

Slide 56

Slide 56

$ cat .gitignore .gitsecret/keys/random_seed !*.secret mongodb-keyfile

Slide 57

Slide 57

$ ls .git .gitignore .gitsecret mongodb-keyfile

Slide 58

Slide 58

$ git secret hide git-secret: done. 1 of 1 files are hidden.

Slide 59

Slide 59

$ ls .git .gitignore .gitsecret mongodb-keyfile mongodb-keyfile.secret

Slide 60

Slide 60

$ git secret reveal File ‘/myproject/mongodb-keyfile’ exists. Overwrite? (y/N) y git-secret: done. 1 of 1 files are revealed.

Slide 61

Slide 61

$ git secret whoknows me@aaronbassett.com $ git secret list mongodb-keyfile

Slide 62

Slide 62

$ git secret killperson me@aaronbassett.com git-secret: removed keys. git-secret: now [me@aaronbassett.com] do not have an access to the repository. git-secret: make sure to hide the existing secrets again. $ git secret reveal git-secret: abort: no public keys for users found. run ‘git secret tell email@address.

Slide 63

Slide 63

Git Secrets

Slide 64

Slide 64

“git-secrets scans commits, commit messages, and —no-ff merges to prevent adding secrets into your git repositories. If a commit, commit message, or any commit in a —no-ff merge history matches one of your configured prohibited regular expression patterns, then the commit is rejected.” – awslabs/git-secrets

Slide 65

Slide 65

$ git secrets &—register-aws &—global OK $ git secrets &—install ~/.git-templates/git-secrets ✓ Installed commit-msg hook to /Users/aaronbassett/.git-templates/gitsecrets/hooks/commit-msg ✓ Installed pre-commit hook to /Users/aaronbassett/.git-templates/gitsecrets/hooks/pre-commit ✓ Installed prepare-commit-msg hook to /Users/aaronbassett/.gittemplates/git-secrets/hooks/prepare-commit-msg $ git config &—global init.templateDir ~/.git-templates/git-secrets

Slide 66

Slide 66

$ git secrets &—register-aws &—global OK $ git secrets &—install ~/.git-templates/git-secrets ✓ Installed commit-msg hook to /Users/aaronbassett/.git-templates/gitsecrets/hooks/commit-msg ✓ Installed pre-commit hook to /Users/aaronbassett/.git-templates/gitsecrets/hooks/pre-commit ✓ Installed prepare-commit-msg hook to /Users/aaronbassett/.gittemplates/git-secrets/hooks/prepare-commit-msg $ git config &—global init.templateDir ~/.git-templates/git-secrets

Slide 67

Slide 67

$ git secrets &—register-aws &—global OK $ git secrets &—install ~/.git-templates/git-secrets ✓ Installed commit-msg hook to /Users/aaronbassett/.git-templates/gitsecrets/hooks/commit-msg ✓ Installed pre-commit hook to /Users/aaronbassett/.git-templates/gitsecrets/hooks/pre-commit ✓ Installed prepare-commit-msg hook to /Users/aaronbassett/.gittemplates/git-secrets/hooks/prepare-commit-msg $ git config &—global init.templateDir ~/.git-templates/git-secrets

Slide 68

Slide 68

providers

Slide 69

Slide 69

^[5KL][1-9A-HJ-NP-Za-km-z]{50,51}$ (xox[p|b|o|a]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32}) https:&//hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24} EAACEdEose0cBA[0-9A-Za-z]+ [t|T][w|W][i|I][t|T][t|T][e|E][r|R].[1-9][0-9]+-[0-9a-zA-Z]{40} [t|T][w|W][i|I][t|T][t|T][e|E][r|R].[‘|”][0-9a-zA-Z]{35,44}[‘|”] AIza[0-9A-Za-z-]{35} [0-9]+-[0-9A-Za-z]{32}.apps.googleusercontent.com ya29.[0-9A-Za-z-_]+ [h|H][e|E][r|R][o|O][k|K][u|U].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12} (——-(BEGIN|END) PRIVATE KEY——-) (——-(BEGIN|END) RSA PRIVATE KEY——-)

Slide 70

Slide 70

$ git secrets &—add-provider &— cat /secret/file/patterns

Slide 71

Slide 71

(——-(BEGIN|END) RSA PRIVATE KEY——-)

Slide 72

Slide 72

ya29.[0-9A-Za-z-_]+

Slide 73

Slide 73

EAACEdEose0cBA[0-9A-Za-z]+

Slide 74

Slide 74

$ cd /secret/file/patterns $ ls crypto keys vendors

Slide 75

Slide 75

Slack (xox[p|b|o|a]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32}) https:&//hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24} # Facebook EAACEdEose0cBA[0-9A-Za-z]+ # Twitter [t|T][w|W][i|I][t|T][t|T][e|E][r|R].[1-9][0-9]+-[0-9a-zA-Z]{40} [t|T][w|W][i|I][t|T][t|T][e|E][r|R].[‘|”][0-9a-zA-Z]{35,44}[‘|”] # Google AIza[0-9A-Za-z-]{35} [0-9]+-[0-9A-Za-z]{32}.apps.googleusercontent.com ya29.[0-9A-Za-z-_]+ # Heroku [h|H][e|E][r|R][o|O][k|K][u|U].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}

Slide 76

Slide 76

git secrets &—add-provider &— egrep -rhv “(^#|^$)” /secret/file/patterns

Slide 77

Slide 77

^[5KL][1-9A-HJ-NP-Za-km-z]{50,51}$ (xox[p|b|o|a]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32}) https:&//hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24} EAACEdEose0cBA[0-9A-Za-z]+ [t|T][w|W][i|I][t|T][t|T][e|E][r|R].[1-9][0-9]+-[0-9a-zA-Z]{40} [t|T][w|W][i|I][t|T][t|T][e|E][r|R].[‘|”][0-9a-zA-Z]{35,44}[‘|”] AIza[0-9A-Za-z-]{35} [0-9]+-[0-9A-Za-z]{32}.apps.googleusercontent.com ya29.[0-9A-Za-z-_]+ [h|H][e|E][r|R][o|O][k|K][u|U].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12} (——-(BEGIN|END) PRIVATE KEY——-) (——-(BEGIN|END) RSA PRIVATE KEY——-)

Slide 78

Slide 78

$ git add ‘private.key’ $ git commit -m “Adding some files I shouldn’t” private.key:1:——-BEGIN PRIVATE KEY——[ERROR] Matched one or more prohibited patterns Possible mitigations: - Mark false positives as allowed using: git config &—add secrets.allowed &&… - Mark false positives as allowed by adding regular expressions to .gitallowed at repository’s root directory - List your configured patterns: git config &—get-all secrets.patterns - List your configured allowed patterns: git config &—get-all secrets.allowed - List your configured allowed patterns in .gitallowed at repository’s root directory - Use &—no-verify if this is a one-time false positive

Slide 79

Slide 79

GITLEAKS

Slide 80

Slide 80

“Audit git repos for secrets. Gitleaks provides a way for you to find unencrypted secrets and other unwanted data types in git source code repositories” – zricethezav/gitleaks

Slide 81

Slide 81

I. A GIT REPO II. GITHUB USER III. GITHUB ORGANIZATION IV. GITHUB PR V. GITLAB USER VI. GITLAB GROUP

Slide 82

Slide 82

{ “line”: “——-BEGIN PRIVATE KEY——-“, “commit”: “37f19780583f12fd2fb687f2d7d3840880e79c76”, “offender”: “——-BEGIN PRIVATE KEY——-“, “rule”: “PKCS8”, “info”: “——-BEGIN PRIVATE KEY——- regex match”, “commitMsg”: “wip backup “, “author”: “Joe Bloggs”, “email”: “jbloggs@example.com”, “file”: “app/private.key”, “repo”: “my-awesome-app”, “date”: “2019-09-14T12:26:10+08:00”, “tags”: “key, PKCS8”, “severity”: “” }

Slide 83

Slide 83

  1. Keep secrets and code separate

Slide 84

Slide 84

  1. If you need to share secrets encrypt them first # PGP is a PITA, use tools to make it easier #

Slide 85

Slide 85

  1. Automate, Automate, Automate

Slide 86

Slide 86

  1. Late is better than never

Slide 87

Slide 87

@aaronbassett noti.st/aaronbassett