@danaluther
Keep it Secret, Keep it Safe
Docker Secrets and DI (Containers)
https://joind.in/talk/78c5b
https://github.com/DanaLuther/kiskis
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
• File Based Credentials
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
• File Based Credentials
• SSH key
fi
les
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
• File Based Credentials
• SSH key
fi
les
• Json auth
fi
les
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
• File Based Credentials
• SSH key
fi
les
• Json auth
fi
les
• Variable Credentials
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
• File Based Credentials
• SSH key
fi
les
• Json auth
fi
les
• Variable Credentials
• API keys and tokens
@danaluther
What are we keeping secret?
All the things? (no… but de
fi
nitely the important things)
• File Based Credentials
• SSH key
fi
les
• Json auth
fi
les
• Variable Credentials
• API keys and tokens
• Any value that could compromise security / grant access to your data or
accounts.
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
• Storing credentials directly into a repo
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
• Storing credentials directly into a repo
• Readable by anyone with access to that repo
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
• Storing credentials directly into a repo
• Readable by anyone with access to that repo
• Exposed in coverage reports or anything that includes the source code in
analysis or reporting displays
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
• Storing credentials directly into a repo
• Readable by anyone with access to that repo
• Exposed in coverage reports or anything that includes the source code in
analysis or reporting displays
• Storing credentials in ENV variables
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
• Storing credentials directly into a repo
• Readable by anyone with access to that repo
• Exposed in coverage reports or anything that includes the source code in
analysis or reporting displays
• Storing credentials in ENV variables
• Exposed via phpinfo, server dumps, or to anyone who has compromised
the server
@danaluther
Isn't it already safe?
…Ish. It depends - how are you storing it currently?
• Storing credentials directly into a repo
• Readable by anyone with access to that repo
• Exposed in coverage reports or anything that includes the source code in
analysis or reporting displays
• Storing credentials in ENV variables
• Exposed via phpinfo, server dumps, or to anyone who has compromised
the server
• Not static and has the potential to be modi
fi
ed nefariously
@danaluther
Does it really matter?
I don’t know … does it?
@danaluther
Does it really matter?
I don’t know … does it?
• How important is the service or information being used?
@danaluther
Does it really matter?
I don’t know … does it?
• How important is the service or information being used?
• How big is the impact if that service is accessed by another?
@danaluther
Does it really matter?
I don’t know … does it?
• How important is the service or information being used?
• How big is the impact if that service is accessed by another?
• Is the credential the sole key to access, or is does the service have additional
factors in play?
@danaluther
Does it really matter?
I don’t know … does it?
• How important is the service or information being used?
• How big is the impact if that service is accessed by another?
• Is the credential the sole key to access, or is does the service have additional
factors in play?
• What is the risk level compared to the technical debt?
@danaluther
Does it really matter?
I don’t know … does it?
• How important is the service or information being used?
• How big is the impact if that service is accessed by another?
• Is the credential the sole key to access, or is does the service have additional
factors in play?
• What is the risk level compared to the technical debt?
• Does the information get published into your application as part of the public
UI?
@danaluther
Sample API Object
Class with public API credential property
class SampleObject


{


	
public string $myApiKey = 'ABC1234';


	
public function connect()


	
{


	
	
/
/
Do something with $myApiKey here


	
}


}
@danaluther
Sample API Object
Class with public API credential property
class SampleObject


{


	
public string $myApiKey = 'ABC1234';


	
public function connect()


	
{


	
	
/
/
Do something with $myApiKey here


	
}


}


new ExampleSampleObject()
@danaluther
Sample API Object v2
Class with API credential property
class SampleObjectMoreSecure


{


	
private string $myApiKey = 'ABC1234';


	
public function connect()


	
{


	
	
/
/
Do something with $myApiKey here


	
}


}
@danaluther
Sample API Object v2
Class with API credential property
class SampleObjectMoreSecure


{


	
private string $myApiKey = 'ABC1234';


	
public function connect()


	
{


	
	
/
/
Do something with $myApiKey here


	
}


}


new ExampleSampleObjectMoreSecure()
@danaluther
Sample API Object v3
Class with constructed private API credential property
class SampleObjectConstructed


{


	
private string $_myApiKey;


	
public function __construct(string $apiKey)


	
{


	
	
$this
-
>
_myApiKey = $apiKey;


	
}


	
public function connect()


	
{


	
	
/
/
Do something


	
}


}
@danaluther
Sample API Object v3
Class with constructed private API credential property
class SampleObjectConstructed


{


	
private string $_myApiKey;


	
public function __construct(string $apiKey)


	
{


	
	
$this
-
>
_myApiKey = $apiKey;


	
}


	
public function connect()


	
{


	
	
/
/
Do something


	
}


}
new ExampleSampleObjectConstructed('Someothervalue')
@danaluther
Sample API Object v3 with $_ENV
Class with constructed private API credential property
class SampleObjectConstructed


{


	
private string $_myApiKey;


	
public function __construct(string $apiKey)


	
{


	
	
$this
-
>
_myApiKey = $apiKey;


	
}


	
public function connect()


	
{


	
	
/
/
Do something


	
}


}
@danaluther
Sample API Object v3 with $_ENV
Class with constructed private API credential property
class SampleObjectConstructed


{


	
private string $_myApiKey;


	
public function __construct(string $apiKey)


	
{


	
	
$this
-
>
_myApiKey = $apiKey;


	
}


	
public function connect()


	
{


	
	
/
/
Do something


	
}


}
new ExampleSampleObjectConstructed($_ENV['MY_SECRET_KEY'])
@danaluther
Sample API Object v3 with $_ENV
Class with constructed private API credential property
class SampleObjectConstructed


{


	
private string $_myApiKey;


	
public function __construct(string $apiKey)


	
{


	
	
$this
-
>
_myApiKey = $apiKey;


	
}


	
public function connect()


	
{


	
	
/
/
Do something


	
}


}
new ExampleSampleObjectConstructed($_ENV['MY_SECRET_KEY'])
@danaluther
Dependency Injection Containers
So, what is it and how does it help us??
@danaluther
Dependency Injection Containers
So, what is it and how does it help us??
• Does NOT help to solve the issue of
fi
le based credentials.
@danaluther
Dependency Injection Containers
So, what is it and how does it help us??
• Does NOT help to solve the issue of
fi
le based credentials.
• Does help to solve the issue of class based or variable credential strings.
@danaluther
Dependency Injection Containers
So, what is it and how does it help us??
• Does NOT help to solve the issue of
fi
le based credentials.
• Does help to solve the issue of class based or variable credential strings.
• Creates a map that de
fi
nes the default properties to be assigned to an object
when that object is created via the container.
@danaluther
Don’t reinvent the wheel.
https://packagist.org
@danaluther
Add your DI package to composer
composer.json
{


"require": {


"yiisoft/di": "^3.0.x
-
dev",


"yiisoft/injector": "^1.0",


"yiisoft/definitions": "^3.0.x
-
dev"


}


}
@danaluther
Install the composer packages
docker run --rm -v $PWD:/app composer update
https://hub.docker.com/_/composer
@danaluther
Sample API Object v3 with $_ENV via DI
Constructor moved to DI Container
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[$_ENV['MY_SECRET_KEY']]


],


]);
@danaluther
Sample API Object v3 with $_ENV via DI
Constructor moved to DI Container
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[$_ENV['MY_SECRET_KEY']]


],


]);
@danaluther
Sample API Object v3 with $_ENV via DI
Constructor moved to DI Container
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[$_ENV['MY_SECRET_KEY']]


],


]); $container
-
>
get(ExampleSampleObjectConstructed
:
:
class)
@danaluther
But is it secret? Is it safe?
Lets check those $_ENV vars …
@danaluther
But is it secret? Is it safe?
Lets check those $_ENV vars …
@danaluther
Docker Secrets
What are they and how do they help us?
@danaluther
Docker Secrets
What are they and how do they help us?
• Does help to solve the issue of
fi
le based credentials.
@danaluther
Docker Secrets
What are they and how do they help us?
• Does help to solve the issue of
fi
le based credentials.
• Does help to solve the issue of class based or variable credential strings.
@danaluther
Docker Secrets
What are they and how do they help us?
• Does help to solve the issue of
fi
le based credentials.
• Does help to solve the issue of class based or variable credential strings.
• Created as elements of a Docker stack and written to read-only
fi
les in the
container they are attached to.
@danaluther
Docker Secrets
What are they and how do they help us?
• Does help to solve the issue of
fi
le based credentials.
• Does help to solve the issue of class based or variable credential strings.
• Created as elements of a Docker stack and written to read-only
fi
les in the
container they are attached to.
• Can only be be modi
fi
ed by the stack when a new container is created.
@danaluther
Docker Secrets
What are they and how do they help us?
• Does help to solve the issue of
fi
le based credentials.
• Does help to solve the issue of class based or variable credential strings.
• Created as elements of a Docker stack and written to read-only
fi
les in the
container they are attached to.
• Can only be be modi
fi
ed by the stack when a new container is created.
• Cannot be exposed through inspection via the stack or parent machine. Only
visible in the container where it has been mounted.
@danaluther
What is Docker?
(Jumped ahead there didn’t I…)
https://www.docker.com
@danaluther
What is Docker?
Docker Hierarchy
https://hub.docker.com
@danaluther
What is Docker?
Docker Hierarchy
Image Container Service Stack
https://hub.docker.com
@danaluther
Basic Stack for PHP Apache on :8080
docker-compose.yml
https://docs.docker.com/compose/compose-
fi
le/
version: “3.7"


services:


php:


image: php:7.4-apache


ports:


- 8080
:
80


volumes:


- ./src:/var/
w
w
w
/html
@danaluther
version: “3.7"


services:


php:


image: php:7.4-apache


ports:


- 8080
:
80


volumes:


- ./src:/var/
w
w
w
/html


command:[


‘bash’, ‘
-
c’,


‘docker
-
php
-
ext
-
install
-
j$$(nproc) mysqli pdo_mysql
&
&
apache2-foreground’


]


db:


image: mysql


environment:


- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd


secrets:


- db_pwd


secrets:


db_pwd:


file: ./root_db_password.txt
@danaluther
version: “3.7"


services:


php:


image: php:8.0-apache


ports:


- 8080
:
80


volumes:


- ./src:/var/
w
w
w
/html


command:[


‘bash’, ‘
-
c’,


‘docker
-
php
-
ext
-
install
-
j$$(nproc) mysqli pdo_mysql
&
&
apache2-foreground’


]


db:


image: mysql


environment:


- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd


secrets:


- db_pwd


secrets:


db_pwd:


file: ./root_db_password.txt
@danaluther
version: “3.7"


services:


php:


image: php:8.1-rc—apache


ports:


- 8080
:
80


volumes:


- ./src:/var/
w
w
w
/html


command:[


‘bash’, ‘
-
c’,


‘docker
-
php
-
ext
-
install
-
j$$(nproc) mysqli pdo_mysql
&
&
apache2-foreground’


]


db:


image: mysql


environment:


- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd


secrets:


- db_pwd


secrets:


db_pwd:


file: ./root_db_password.txt
@danaluther
Deploy the Stack on a Swarm
(Group of machines … or just one machine)
> docker stack deploy
-
c docker
-
compose-8.yml test
> docker swarm init
@danaluther
Voila!
http://localhost:8080
@danaluther
Voila!
http://localhost:8080
@danaluther
No swarm? No problem…
> docker
-
compose up
(Well, mostly no problem. No services list, secrets list, etc)
@danaluther
No swarm? No problem…
> docker
-
compose up
(Well, mostly no problem. No services list, secrets list, etc)
@danaluther
@danaluther
Creating a Docker Secret
The secret is in the stack…
…


db:


image: mysql


environment:


- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd


secrets:


- db_pwd


secrets:


db_pwd:


file: ./root_db_password.txt
@danaluther
Creating a Docker Secret
The secret is in the stack…
…


db:


image: mysql


environment:


- MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd


secrets:


- db_pwd


secrets:


db_pwd:


file: ./root_db_password.txt
@danaluther
Check the containers…
docker container ls
@danaluther
Check the containers…
docker container ls
@danaluther
Inspect the container for the secret
docker container inspect
@danaluther
Inspect the container for the secret
docker container inspect
> docker container inspect $(docker ps
-
lq
-
f name=test_db)
> docker container inspect ff8dc39f919f
> docker container inspect test_db.1.l8enz0qx7x8zci4156gh7i0qy
@danaluther
Inspect the container for the secret
docker container inspect
> docker container inspect kiskis_db_1
> docker container inspect 378020331e87
> docker container inspect $(docker ps
-
lq
-
f name=test_db)
> docker container inspect ff8dc39f919f
> docker container inspect test_db.1.l8enz0qx7x8zci4156gh7i0qy
@danaluther
Inspect the container for the secret
docker container inspect
@danaluther
Inspect the container for the secret
docker container inspect
@danaluther
Inspect the container for the secret
docker container inspect
@danaluther
What's actually in the Secret?
@danaluther
What's actually in the Secret?
@danaluther
Can the container change the secret?
Nope!!
@danaluther
Can *I* change the secret?
Yes, yes I can.
@danaluther
Can *I* change the secret?
Yes, yes I can.
If I used docker stack deploy.
@danaluther
Updating a Docker Secret
(Ok, you can’t actually update it, you have to replace it.)
> docker secret create db_pwd.2 updatedcontent.txt
@danaluther
Updating a Docker Secret
(Ok, you can’t actually update it, you have to replace it.)
> docker service update
-
-
secret
-
rm test_db_pwd 


-
-
secret
-
add src=db_pwd.2,target=db_pwd test_db
@danaluther
Confirm the update
Updating the service launched a new container with a new secret.
New:
Original:
@danaluther
Great, but I don’t care about the database…
Right - lets put the secret to use in our DI container
@danaluther
Great, but I don’t care about the database…
Right - lets put the secret to use in our DI container
@danaluther
Sample API Object v3 with $_ENV via DI
Constructor moved to DI Container
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[$_ENV['MY_SECRET_KEY']]


],


]); $container
-
>
get(ExampleSampleObjectConstructed
:
:
class)
@danaluther
Sample API Object v3 with $_ENV via DI
Constructor moved to DI Container
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[$_ENV['MY_SECRET_KEY']]


],


]); $container
-
>
get(ExampleSampleObjectConstructed
:
:
class)
@danaluther
PHP stack with secrets
version: "3.7"


services:


php:


image: php:8.1-rc
-
apache


ports:


- 8081
:
80


volumes:


- ./src:/var/
w
w
w
/html


environment:


- MY_SECRET_KEY=SomeKeyValue


secrets:


- hiddenkey


- hiddenkey2


secrets:


hiddenkey:


file: ./secrets/secret
-
key
-
val.txt


hiddenkey2
:


file: ./secrets/secret
-
key
-
val2.txt
@danaluther
Deploy the Stack on a Swarm
(Don’t need to init, just re-deploy for updates)
> docker stack deploy
-
c docker
-
compose-8-1-nodb.yml test
@danaluther
Sample API Object v3 with secret via DI
Container populated from secret
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[file_get_contents('/run/secrets/hiddenkey')]


],


]);
@danaluther
Sample API Object v3 with secret via DI
Container populated from secret
require 'vendor/autoload.php';


use YiisoftDiContainer;


$container = new Container([


ExampleSampleObjectConstructed
:
:
class
=
>
[


	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
'__construct()'
=
>
[file_get_contents('/run/secrets/hiddenkey')]


],


]); $container
-
>
get(ExampleSampleObjectConstructed
:
:
class)
@danaluther
Define multiple objects in the container
Multiple classes, multiple con
fi
gurations of one class
$container = new Container([


	
ExampleSampleObjectConstructed
:
:
class
=
>
[


	
	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
	
'__construct()'
=
>
[file_get_contents('/run/secrets/hiddenkey')]


	
],


	
ExamplePartnerConfigIdentifier
:
:
OPT_CONFIG_1
=
>
[


	
	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
	
'__construct()'
=
>
[file_get_contents('/run/secrets/hiddenkey2')]


	
],


	
ExamplePartnerConfigIdentifier
:
:
OPT_CONFIG_2
=
>
[


	
	
'class'
=
>
ExampleSampleObjectConstructed
:
:
class,


	
	
'__construct()'
=
>
['You can read me in the source repo.']


	
],


]);
@danaluther
Using constants to keep things tidy
(Yes, it wants to be an enum, but we’re 7.4 compatible currently)
class PartnerConfigIdentifier


{


	
public const DEFAULT_CONFIG = self
:
:
class.':default';


	
public const OPT_CONFIG_1 = self
:
:
class.':opt1';


	
public const OPT_CONFIG_2 = self
:
:
class.':opt2';


	
public const OPT_CONFIG_3 = self
:
:
class.':opt3';


}
@danaluther
Using constants to keep things tidy
(Yes, it wants to be an enum, but we’re 7.4 compatible currently)
class PartnerConfigIdentifier


{


	
public const DEFAULT_CONFIG = self
:
:
class.':default';


	
public const OPT_CONFIG_1 = self
:
:
class.':opt1';


	
public const OPT_CONFIG_2 = self
:
:
class.':opt2';


	
public const OPT_CONFIG_3 = self
:
:
class.':opt3';


}
@danaluther
Create multiple objects via the container
$container
-
>
get(ExampleSampleObjectConstructed
:
:
class);
$container
-
>
get(ExamplePartnerConfigIdentifier
:
:
OPT_CONFIG_1);
$container
-
>
get(ExamplePartnerConfigIdentifier
:
:
OPT_CONFIG_2);
@danaluther
Questions??
Ask now or tweet at me if you think of it later!
https://www.linkedin.com/in/danaluther
dluther@envisageinternational.com
https://joind.in/talk/78c5b
🤔
?
? ?
?
https://github.com/DanaLuther/kiskis

More Related Content

PPTX
Getting the Most Out of jQuery Widgets
PDF
WCLV13 JavaScript
PPTX
jQuery from the very beginning
KEY
Motion Django Meetup
PDF
PDF
深入淺出 MVC
PPTX
Maintainable JavaScript 2012
ODP
Creating fast, dynamic ACLs in Zend Framework
Getting the Most Out of jQuery Widgets
WCLV13 JavaScript
jQuery from the very beginning
Motion Django Meetup
深入淺出 MVC
Maintainable JavaScript 2012
Creating fast, dynamic ACLs in Zend Framework

What's hot (19)

ODP
Creating fast, dynamic ACLs in Zend Framework (Zend Webinar)
PDF
Writing a REST Interconnection Library in Swift
PDF
Zf2 how arrays will save your project
PPT
PDF
Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
PPTX
jQuery PPT
PDF
Expression Language in JSP
PDF
Real World Dependency Injection - oscon13
PDF
Metrics-Driven Engineering at Etsy
PDF
Your Business. Your Language. Your Code - dpc13
PDF
Puppet Camp Portland 2015: Introduction to Hiera (Beginner)
PDF
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
PPTX
API Design Antipatterns - APICon SF
PDF
Refactor Dance - Puppet Labs 'Best Practices'
PDF
jQuery Features to Avoid
PDF
Yii, frameworks and where PHP is heading to
PPTX
Unobtrusive javascript with jQuery
PDF
George Thiruvathukal, User Experiences with Plone Content Management
PDF
Puppet Camp Sydney 2015: The (Im)perfect Puppet Module
Creating fast, dynamic ACLs in Zend Framework (Zend Webinar)
Writing a REST Interconnection Library in Swift
Zf2 how arrays will save your project
Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
jQuery PPT
Expression Language in JSP
Real World Dependency Injection - oscon13
Metrics-Driven Engineering at Etsy
Your Business. Your Language. Your Code - dpc13
Puppet Camp Portland 2015: Introduction to Hiera (Beginner)
Doing the Refactor Dance - Making Your Puppet Modules More Modular - PuppetCo...
API Design Antipatterns - APICon SF
Refactor Dance - Puppet Labs 'Best Practices'
jQuery Features to Avoid
Yii, frameworks and where PHP is heading to
Unobtrusive javascript with jQuery
George Thiruvathukal, User Experiences with Plone Content Management
Puppet Camp Sydney 2015: The (Im)perfect Puppet Module
Ad

Similar to Keep it Secret, Keep it Safe - Docker Secrets and DI (20)

PDF
apidays LIVE Australia 2021 - Levelling up database security by thinking in A...
PPTX
Microservices security - jpmc tech fest 2018
PDF
CIS14: Best Practices You Must Apply to Secure Your APIs
PPTX
Best Practices You Must Apply to Secure Your APIs - Scott Morrison, SVP & Dis...
PDF
Introduction to DI(C)
PDF
Identiverse 2018 nathanael coffing
PPTX
Cybercrime and the developer 2021 style
PDF
Credential store using HashiCorp Vault
PDF
APIdays Paris 2019 - API Gateway & Identity Providers, a Match Made in Micros...
PDF
Secure Secret Management on a Budget: Reasoning about Scalable SM with Vault ...
PDF
Cryptography with Zend Framework
PDF
Strong cryptography in PHP
KEY
IoC with PHP
PDF
Practical API Security - Midwest PHP 2018
PDF
Protecting Your APIs Against Attack & Hijack
PPTX
2022 APIsecure_Securing APIs with Open Standards
PPTX
Enterprise Access Control Patterns for REST and Web APIs Gluecon 2011, Franco...
PDF
WebApp_to_Container_Security.pdf
PDF
GHC18 Abstract - API Security, a Grail Quest
PDF
Securing Your Database Dynamic DB Credentials
apidays LIVE Australia 2021 - Levelling up database security by thinking in A...
Microservices security - jpmc tech fest 2018
CIS14: Best Practices You Must Apply to Secure Your APIs
Best Practices You Must Apply to Secure Your APIs - Scott Morrison, SVP & Dis...
Introduction to DI(C)
Identiverse 2018 nathanael coffing
Cybercrime and the developer 2021 style
Credential store using HashiCorp Vault
APIdays Paris 2019 - API Gateway & Identity Providers, a Match Made in Micros...
Secure Secret Management on a Budget: Reasoning about Scalable SM with Vault ...
Cryptography with Zend Framework
Strong cryptography in PHP
IoC with PHP
Practical API Security - Midwest PHP 2018
Protecting Your APIs Against Attack & Hijack
2022 APIsecure_Securing APIs with Open Standards
Enterprise Access Control Patterns for REST and Web APIs Gluecon 2011, Franco...
WebApp_to_Container_Security.pdf
GHC18 Abstract - API Security, a Grail Quest
Securing Your Database Dynamic DB Credentials
Ad

More from Dana Luther (14)

PDF
Convert Your Dev Environment to a Docker Stack - PHP Tek 2025.pdf
PDF
Enums In the Wild at PHP[tek] Conference 2025
PDF
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
PDF
How to analyze your codebase with Exakat using Docker - Longhorn PHP
PDF
Integrated Feature Management - Using Feature Flags - PHPSerbia
PDF
Integrated Feature Management - Using Feature Flags - MidwestPHP
PDF
Integrated Feature Management - Using Feature Flags - SunshinePHP
PDF
Hands on Docker - Launch your own LEMP or LAMP stack - SunshinePHP
PDF
Hands on Docker - Launch your own LEMP or LAMP stack
PDF
Converting Your Dev Environment to a Docker Stack - php[world]
PDF
Converting Your Dev Environment to a Docker Stack - Cascadia
PDF
Converting your DEV Environment to a Docker Stack - ZCOE18
PDF
Converting Your DEV Environment to a Docker Stack
PDF
Code Coverage for Total Security in Application Migrations
Convert Your Dev Environment to a Docker Stack - PHP Tek 2025.pdf
Enums In the Wild at PHP[tek] Conference 2025
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
How to analyze your codebase with Exakat using Docker - Longhorn PHP
Integrated Feature Management - Using Feature Flags - PHPSerbia
Integrated Feature Management - Using Feature Flags - MidwestPHP
Integrated Feature Management - Using Feature Flags - SunshinePHP
Hands on Docker - Launch your own LEMP or LAMP stack - SunshinePHP
Hands on Docker - Launch your own LEMP or LAMP stack
Converting Your Dev Environment to a Docker Stack - php[world]
Converting Your Dev Environment to a Docker Stack - Cascadia
Converting your DEV Environment to a Docker Stack - ZCOE18
Converting Your DEV Environment to a Docker Stack
Code Coverage for Total Security in Application Migrations

Recently uploaded (20)

PDF
📍 LABUAN4D EXCLUSIVE SERVER STAR GAMING ASIA NO.1 TERPOPULER DI INDONESIA ! 🌟
PDF
si manuel quezon at mga nagawa sa bansang pilipinas
PDF
Exploring VPS Hosting Trends for SMBs in 2025
PDF
Containerization lab dddddddddddddddmanual.pdf
PPTX
Introduction to cybersecurity and digital nettiquette
PDF
Alethe Consulting Corporate Profile and Solution Aproach
DOCX
Powerful Ways AIRCONNECT INFOSYSTEMS Pvt Ltd Enhances IT Infrastructure in In...
PDF
Understand the Gitlab_presentation_task.pdf
PDF
Alethe Consulting Corporate Profile and Solution Aproach
PDF
The Evolution of Traditional to New Media .pdf
PPTX
Reading as a good Form of Recreation
PDF
Top 8 Trusted Sources to Buy Verified Cash App Accounts.pdf
PPTX
Cyber Hygine IN organizations in MSME or
PPTX
AI_Cyberattack_Solutions AI AI AI AI .pptx
PPTX
IPCNA VIRTUAL CLASSES INTERMEDIATE 6 PROJECT.pptx
PDF
Course Overview and Agenda cloud security
PDF
Session 1 (Week 1)fghjmgfdsfgthyjkhfdsadfghjkhgfdsa
PDF
The Ikigai Template _ Recalibrate How You Spend Your Time.pdf
PPT
12 Things That Make People Trust a Website Instantly
PPTX
Database Information System - Management Information System
📍 LABUAN4D EXCLUSIVE SERVER STAR GAMING ASIA NO.1 TERPOPULER DI INDONESIA ! 🌟
si manuel quezon at mga nagawa sa bansang pilipinas
Exploring VPS Hosting Trends for SMBs in 2025
Containerization lab dddddddddddddddmanual.pdf
Introduction to cybersecurity and digital nettiquette
Alethe Consulting Corporate Profile and Solution Aproach
Powerful Ways AIRCONNECT INFOSYSTEMS Pvt Ltd Enhances IT Infrastructure in In...
Understand the Gitlab_presentation_task.pdf
Alethe Consulting Corporate Profile and Solution Aproach
The Evolution of Traditional to New Media .pdf
Reading as a good Form of Recreation
Top 8 Trusted Sources to Buy Verified Cash App Accounts.pdf
Cyber Hygine IN organizations in MSME or
AI_Cyberattack_Solutions AI AI AI AI .pptx
IPCNA VIRTUAL CLASSES INTERMEDIATE 6 PROJECT.pptx
Course Overview and Agenda cloud security
Session 1 (Week 1)fghjmgfdsfgthyjkhfdsadfghjkhgfdsa
The Ikigai Template _ Recalibrate How You Spend Your Time.pdf
12 Things That Make People Trust a Website Instantly
Database Information System - Management Information System

Keep it Secret, Keep it Safe - Docker Secrets and DI

  • 1. @danaluther Keep it Secret, Keep it Safe Docker Secrets and DI (Containers) https://joind.in/talk/78c5b https://github.com/DanaLuther/kiskis
  • 2. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things)
  • 3. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things) • File Based Credentials
  • 4. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things) • File Based Credentials • SSH key fi les
  • 5. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things) • File Based Credentials • SSH key fi les • Json auth fi les
  • 6. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things) • File Based Credentials • SSH key fi les • Json auth fi les • Variable Credentials
  • 7. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things) • File Based Credentials • SSH key fi les • Json auth fi les • Variable Credentials • API keys and tokens
  • 8. @danaluther What are we keeping secret? All the things? (no… but de fi nitely the important things) • File Based Credentials • SSH key fi les • Json auth fi les • Variable Credentials • API keys and tokens • Any value that could compromise security / grant access to your data or accounts.
  • 9. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently?
  • 10. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently? • Storing credentials directly into a repo
  • 11. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently? • Storing credentials directly into a repo • Readable by anyone with access to that repo
  • 12. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently? • Storing credentials directly into a repo • Readable by anyone with access to that repo • Exposed in coverage reports or anything that includes the source code in analysis or reporting displays
  • 13. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently? • Storing credentials directly into a repo • Readable by anyone with access to that repo • Exposed in coverage reports or anything that includes the source code in analysis or reporting displays • Storing credentials in ENV variables
  • 14. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently? • Storing credentials directly into a repo • Readable by anyone with access to that repo • Exposed in coverage reports or anything that includes the source code in analysis or reporting displays • Storing credentials in ENV variables • Exposed via phpinfo, server dumps, or to anyone who has compromised the server
  • 15. @danaluther Isn't it already safe? …Ish. It depends - how are you storing it currently? • Storing credentials directly into a repo • Readable by anyone with access to that repo • Exposed in coverage reports or anything that includes the source code in analysis or reporting displays • Storing credentials in ENV variables • Exposed via phpinfo, server dumps, or to anyone who has compromised the server • Not static and has the potential to be modi fi ed nefariously
  • 16. @danaluther Does it really matter? I don’t know … does it?
  • 17. @danaluther Does it really matter? I don’t know … does it? • How important is the service or information being used?
  • 18. @danaluther Does it really matter? I don’t know … does it? • How important is the service or information being used? • How big is the impact if that service is accessed by another?
  • 19. @danaluther Does it really matter? I don’t know … does it? • How important is the service or information being used? • How big is the impact if that service is accessed by another? • Is the credential the sole key to access, or is does the service have additional factors in play?
  • 20. @danaluther Does it really matter? I don’t know … does it? • How important is the service or information being used? • How big is the impact if that service is accessed by another? • Is the credential the sole key to access, or is does the service have additional factors in play? • What is the risk level compared to the technical debt?
  • 21. @danaluther Does it really matter? I don’t know … does it? • How important is the service or information being used? • How big is the impact if that service is accessed by another? • Is the credential the sole key to access, or is does the service have additional factors in play? • What is the risk level compared to the technical debt? • Does the information get published into your application as part of the public UI?
  • 22. @danaluther Sample API Object Class with public API credential property class SampleObject { public string $myApiKey = 'ABC1234'; public function connect() { / / Do something with $myApiKey here } }
  • 23. @danaluther Sample API Object Class with public API credential property class SampleObject { public string $myApiKey = 'ABC1234'; public function connect() { / / Do something with $myApiKey here } } new ExampleSampleObject()
  • 24. @danaluther Sample API Object v2 Class with API credential property class SampleObjectMoreSecure { private string $myApiKey = 'ABC1234'; public function connect() { / / Do something with $myApiKey here } }
  • 25. @danaluther Sample API Object v2 Class with API credential property class SampleObjectMoreSecure { private string $myApiKey = 'ABC1234'; public function connect() { / / Do something with $myApiKey here } } new ExampleSampleObjectMoreSecure()
  • 26. @danaluther Sample API Object v3 Class with constructed private API credential property class SampleObjectConstructed { private string $_myApiKey; public function __construct(string $apiKey) { $this - > _myApiKey = $apiKey; } public function connect() { / / Do something } }
  • 27. @danaluther Sample API Object v3 Class with constructed private API credential property class SampleObjectConstructed { private string $_myApiKey; public function __construct(string $apiKey) { $this - > _myApiKey = $apiKey; } public function connect() { / / Do something } } new ExampleSampleObjectConstructed('Someothervalue')
  • 28. @danaluther Sample API Object v3 with $_ENV Class with constructed private API credential property class SampleObjectConstructed { private string $_myApiKey; public function __construct(string $apiKey) { $this - > _myApiKey = $apiKey; } public function connect() { / / Do something } }
  • 29. @danaluther Sample API Object v3 with $_ENV Class with constructed private API credential property class SampleObjectConstructed { private string $_myApiKey; public function __construct(string $apiKey) { $this - > _myApiKey = $apiKey; } public function connect() { / / Do something } } new ExampleSampleObjectConstructed($_ENV['MY_SECRET_KEY'])
  • 30. @danaluther Sample API Object v3 with $_ENV Class with constructed private API credential property class SampleObjectConstructed { private string $_myApiKey; public function __construct(string $apiKey) { $this - > _myApiKey = $apiKey; } public function connect() { / / Do something } } new ExampleSampleObjectConstructed($_ENV['MY_SECRET_KEY'])
  • 31. @danaluther Dependency Injection Containers So, what is it and how does it help us??
  • 32. @danaluther Dependency Injection Containers So, what is it and how does it help us?? • Does NOT help to solve the issue of fi le based credentials.
  • 33. @danaluther Dependency Injection Containers So, what is it and how does it help us?? • Does NOT help to solve the issue of fi le based credentials. • Does help to solve the issue of class based or variable credential strings.
  • 34. @danaluther Dependency Injection Containers So, what is it and how does it help us?? • Does NOT help to solve the issue of fi le based credentials. • Does help to solve the issue of class based or variable credential strings. • Creates a map that de fi nes the default properties to be assigned to an object when that object is created via the container.
  • 35. @danaluther Don’t reinvent the wheel. https://packagist.org
  • 36. @danaluther Add your DI package to composer composer.json { "require": { "yiisoft/di": "^3.0.x - dev", "yiisoft/injector": "^1.0", "yiisoft/definitions": "^3.0.x - dev" } }
  • 37. @danaluther Install the composer packages docker run --rm -v $PWD:/app composer update https://hub.docker.com/_/composer
  • 38. @danaluther Sample API Object v3 with $_ENV via DI Constructor moved to DI Container require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [$_ENV['MY_SECRET_KEY']] ], ]);
  • 39. @danaluther Sample API Object v3 with $_ENV via DI Constructor moved to DI Container require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [$_ENV['MY_SECRET_KEY']] ], ]);
  • 40. @danaluther Sample API Object v3 with $_ENV via DI Constructor moved to DI Container require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [$_ENV['MY_SECRET_KEY']] ], ]); $container - > get(ExampleSampleObjectConstructed : : class)
  • 41. @danaluther But is it secret? Is it safe? Lets check those $_ENV vars …
  • 42. @danaluther But is it secret? Is it safe? Lets check those $_ENV vars …
  • 43. @danaluther Docker Secrets What are they and how do they help us?
  • 44. @danaluther Docker Secrets What are they and how do they help us? • Does help to solve the issue of fi le based credentials.
  • 45. @danaluther Docker Secrets What are they and how do they help us? • Does help to solve the issue of fi le based credentials. • Does help to solve the issue of class based or variable credential strings.
  • 46. @danaluther Docker Secrets What are they and how do they help us? • Does help to solve the issue of fi le based credentials. • Does help to solve the issue of class based or variable credential strings. • Created as elements of a Docker stack and written to read-only fi les in the container they are attached to.
  • 47. @danaluther Docker Secrets What are they and how do they help us? • Does help to solve the issue of fi le based credentials. • Does help to solve the issue of class based or variable credential strings. • Created as elements of a Docker stack and written to read-only fi les in the container they are attached to. • Can only be be modi fi ed by the stack when a new container is created.
  • 48. @danaluther Docker Secrets What are they and how do they help us? • Does help to solve the issue of fi le based credentials. • Does help to solve the issue of class based or variable credential strings. • Created as elements of a Docker stack and written to read-only fi les in the container they are attached to. • Can only be be modi fi ed by the stack when a new container is created. • Cannot be exposed through inspection via the stack or parent machine. Only visible in the container where it has been mounted.
  • 49. @danaluther What is Docker? (Jumped ahead there didn’t I…) https://www.docker.com
  • 50. @danaluther What is Docker? Docker Hierarchy https://hub.docker.com
  • 51. @danaluther What is Docker? Docker Hierarchy Image Container Service Stack https://hub.docker.com
  • 52. @danaluther Basic Stack for PHP Apache on :8080 docker-compose.yml https://docs.docker.com/compose/compose- fi le/ version: “3.7" services: php: image: php:7.4-apache ports: - 8080 : 80 volumes: - ./src:/var/ w w w /html
  • 53. @danaluther version: “3.7" services: php: image: php:7.4-apache ports: - 8080 : 80 volumes: - ./src:/var/ w w w /html command:[ ‘bash’, ‘ - c’, ‘docker - php - ext - install - j$$(nproc) mysqli pdo_mysql & & apache2-foreground’ ] db: image: mysql environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd secrets: - db_pwd secrets: db_pwd: file: ./root_db_password.txt
  • 54. @danaluther version: “3.7" services: php: image: php:8.0-apache ports: - 8080 : 80 volumes: - ./src:/var/ w w w /html command:[ ‘bash’, ‘ - c’, ‘docker - php - ext - install - j$$(nproc) mysqli pdo_mysql & & apache2-foreground’ ] db: image: mysql environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd secrets: - db_pwd secrets: db_pwd: file: ./root_db_password.txt
  • 55. @danaluther version: “3.7" services: php: image: php:8.1-rc—apache ports: - 8080 : 80 volumes: - ./src:/var/ w w w /html command:[ ‘bash’, ‘ - c’, ‘docker - php - ext - install - j$$(nproc) mysqli pdo_mysql & & apache2-foreground’ ] db: image: mysql environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd secrets: - db_pwd secrets: db_pwd: file: ./root_db_password.txt
  • 56. @danaluther Deploy the Stack on a Swarm (Group of machines … or just one machine) > docker stack deploy - c docker - compose-8.yml test > docker swarm init
  • 59. @danaluther No swarm? No problem… > docker - compose up (Well, mostly no problem. No services list, secrets list, etc)
  • 60. @danaluther No swarm? No problem… > docker - compose up (Well, mostly no problem. No services list, secrets list, etc)
  • 62. @danaluther Creating a Docker Secret The secret is in the stack… … db: image: mysql environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd secrets: - db_pwd secrets: db_pwd: file: ./root_db_password.txt
  • 63. @danaluther Creating a Docker Secret The secret is in the stack… … db: image: mysql environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_pwd secrets: - db_pwd secrets: db_pwd: file: ./root_db_password.txt
  • 66. @danaluther Inspect the container for the secret docker container inspect
  • 67. @danaluther Inspect the container for the secret docker container inspect > docker container inspect $(docker ps - lq - f name=test_db) > docker container inspect ff8dc39f919f > docker container inspect test_db.1.l8enz0qx7x8zci4156gh7i0qy
  • 68. @danaluther Inspect the container for the secret docker container inspect > docker container inspect kiskis_db_1 > docker container inspect 378020331e87 > docker container inspect $(docker ps - lq - f name=test_db) > docker container inspect ff8dc39f919f > docker container inspect test_db.1.l8enz0qx7x8zci4156gh7i0qy
  • 69. @danaluther Inspect the container for the secret docker container inspect
  • 70. @danaluther Inspect the container for the secret docker container inspect
  • 71. @danaluther Inspect the container for the secret docker container inspect
  • 74. @danaluther Can the container change the secret? Nope!!
  • 75. @danaluther Can *I* change the secret? Yes, yes I can.
  • 76. @danaluther Can *I* change the secret? Yes, yes I can. If I used docker stack deploy.
  • 77. @danaluther Updating a Docker Secret (Ok, you can’t actually update it, you have to replace it.) > docker secret create db_pwd.2 updatedcontent.txt
  • 78. @danaluther Updating a Docker Secret (Ok, you can’t actually update it, you have to replace it.) > docker service update - - secret - rm test_db_pwd - - secret - add src=db_pwd.2,target=db_pwd test_db
  • 79. @danaluther Confirm the update Updating the service launched a new container with a new secret. New: Original:
  • 80. @danaluther Great, but I don’t care about the database… Right - lets put the secret to use in our DI container
  • 81. @danaluther Great, but I don’t care about the database… Right - lets put the secret to use in our DI container
  • 82. @danaluther Sample API Object v3 with $_ENV via DI Constructor moved to DI Container require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [$_ENV['MY_SECRET_KEY']] ], ]); $container - > get(ExampleSampleObjectConstructed : : class)
  • 83. @danaluther Sample API Object v3 with $_ENV via DI Constructor moved to DI Container require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [$_ENV['MY_SECRET_KEY']] ], ]); $container - > get(ExampleSampleObjectConstructed : : class)
  • 84. @danaluther PHP stack with secrets version: "3.7" services: php: image: php:8.1-rc - apache ports: - 8081 : 80 volumes: - ./src:/var/ w w w /html environment: - MY_SECRET_KEY=SomeKeyValue secrets: - hiddenkey - hiddenkey2 secrets: hiddenkey: file: ./secrets/secret - key - val.txt hiddenkey2 : file: ./secrets/secret - key - val2.txt
  • 85. @danaluther Deploy the Stack on a Swarm (Don’t need to init, just re-deploy for updates) > docker stack deploy - c docker - compose-8-1-nodb.yml test
  • 86. @danaluther Sample API Object v3 with secret via DI Container populated from secret require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [file_get_contents('/run/secrets/hiddenkey')] ], ]);
  • 87. @danaluther Sample API Object v3 with secret via DI Container populated from secret require 'vendor/autoload.php'; use YiisoftDiContainer; $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [file_get_contents('/run/secrets/hiddenkey')] ], ]); $container - > get(ExampleSampleObjectConstructed : : class)
  • 88. @danaluther Define multiple objects in the container Multiple classes, multiple con fi gurations of one class $container = new Container([ ExampleSampleObjectConstructed : : class = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [file_get_contents('/run/secrets/hiddenkey')] ], ExamplePartnerConfigIdentifier : : OPT_CONFIG_1 = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > [file_get_contents('/run/secrets/hiddenkey2')] ], ExamplePartnerConfigIdentifier : : OPT_CONFIG_2 = > [ 'class' = > ExampleSampleObjectConstructed : : class, '__construct()' = > ['You can read me in the source repo.'] ], ]);
  • 89. @danaluther Using constants to keep things tidy (Yes, it wants to be an enum, but we’re 7.4 compatible currently) class PartnerConfigIdentifier { public const DEFAULT_CONFIG = self : : class.':default'; public const OPT_CONFIG_1 = self : : class.':opt1'; public const OPT_CONFIG_2 = self : : class.':opt2'; public const OPT_CONFIG_3 = self : : class.':opt3'; }
  • 90. @danaluther Using constants to keep things tidy (Yes, it wants to be an enum, but we’re 7.4 compatible currently) class PartnerConfigIdentifier { public const DEFAULT_CONFIG = self : : class.':default'; public const OPT_CONFIG_1 = self : : class.':opt1'; public const OPT_CONFIG_2 = self : : class.':opt2'; public const OPT_CONFIG_3 = self : : class.':opt3'; }
  • 91. @danaluther Create multiple objects via the container $container - > get(ExampleSampleObjectConstructed : : class); $container - > get(ExamplePartnerConfigIdentifier : : OPT_CONFIG_1); $container - > get(ExamplePartnerConfigIdentifier : : OPT_CONFIG_2);
  • 92. @danaluther Questions?? Ask now or tweet at me if you think of it later! https://www.linkedin.com/in/danaluther dluther@envisageinternational.com https://joind.in/talk/78c5b 🤔 ? ? ? ? https://github.com/DanaLuther/kiskis