Can you keep a secret? @aaronbassett
A presentation at PyCon Australia in September 2020 in by Aaron Bassett
Can you keep a secret? @aaronbassett
Aaron Bassett SENIOR Developer Advocate
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)
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)
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)
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)
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)
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)
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)
git add . git commit -m “wip” git push
“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
Not safe, BUT VERY easy
Functionality Security Usability
Low Friction
Easy to implement
12 Factor Apps
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
III. Config
“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
environment variables
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)
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)
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)
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)
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)
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)
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)
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)
Creating environment variables
“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
$ echo export DB_PASSWORD=hunter2 > .envrc
$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed
$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow .
$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +DB_PASSWORD
$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +DB_PASSWORD $ cd &..
$ echo export DB_PASSWORD=hunter2 > .envrc .envrc is not allowed $ direnv allow . direnv: reloading direnv: loading .envrc direnv export: +DB_PASSWORD $ cd &.. direnv: unloading
Ignore .envrc echo .envrc > ~/.gitignore git config &—global core.excludesfile ~/.gitignore
Example .envrc grep -ohr “^export .*=” .envrc > .envrc.example
Sharing Secret Files
Encryption at Rest
Client-Side Field Level Encryption
USE A KMS
openssl rand -base64 32 > mongodb-keyfile chmod 600 mongodb-keyfile mongod &—enableEncryption &—encryptionKeyFile mongodb-keyfile
GIT-SECRET.IO
“git-secret encrypts files and stores them inside the git repository, so you will have all the changes for every commit.” –git-secret.io
Safe & Easy
Safe & Easy-ish
PGP
$ git secret init git-secret: init created: ‘/myproject/.gitsecret/’
$ 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.
$ 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.
$ cat .gitignore .gitsecret/keys/random_seed !*.secret mongodb-keyfile
$ ls .git .gitignore .gitsecret mongodb-keyfile
$ git secret hide git-secret: done. 1 of 1 files are hidden.
$ ls .git .gitignore .gitsecret mongodb-keyfile mongodb-keyfile.secret
$ git secret reveal File ‘/myproject/mongodb-keyfile’ exists. Overwrite? (y/N) y git-secret: done. 1 of 1 files are revealed.
$ git secret whoknows me@aaronbassett.com $ git secret list mongodb-keyfile
$ 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.
Git Secrets
“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
$ 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
$ 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
$ 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
providers
^[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——-)
$ git secrets &—add-provider &— cat /secret/file/patterns
(——-(BEGIN|END) RSA PRIVATE KEY——-)
ya29.[0-9A-Za-z-_]+
EAACEdEose0cBA[0-9A-Za-z]+
$ cd /secret/file/patterns $ ls crypto keys vendors
git secrets &—add-provider &— egrep -rhv “(^#|^$)” /secret/file/patterns
^[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——-)
$ 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
GITLEAKS
“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
I. A GIT REPO II. GITHUB USER III. GITHUB ORGANIZATION IV. GITHUB PR V. GITLAB USER VI. GITLAB GROUP
{ “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”: “” }
@aaronbassett noti.st/aaronbassett