Vulnerabilities and Security Round Trip
Jérémy DERUSSÉ
Developer at Blackfire.io
@symfony core team & security team
@jderusse
security@symfony.com
- collect and acknowlege reports
- work on patches with the core team
- publish security release (CVE, blog post, ...)
Running dev tools in production
The WebDebug Toolbar
- Sensitive data exposure
- SQL Injection
- Path traversal
- DDOS
- ...
how to fix?
enable only what you need
composer install --no-dev
APP_ENV=prod
// bundles.php
return [
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
];
// conposer.json
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4",
"symfony/debug-bundle": "^5.4",
"symfony/maker-bundle": "^1.34",
"symfony/web-profiler-bundle": "^5.4"
}
Host Header Attacks
<p>
You asked to reset your password. <br />
This can be done by clicking the link below.
</p>
<p>
<a href="{{ url('set_password', {token: token}) }}">
Reset my password
</a>
</p>
email poisonning
$ curl http://acme.com/forgot-password
-X POST
-d "user=victim"
-H "host: attacker.com"
* Trying 157.131.111.93:80...
* Connected to acme.com port 80
> POST /forgot-password HTTP/1.1
> Host: attacker.com
>
> user=victim
http://attacker.com/set-password?token=...
You asked to reset your password.
This can be done by clicking the link below.
Reset my password
From: admin@acme.com
I forgot my password
attacker
never trust user's payload
framework:
trusted_hosts:
- '^acme\.com$'
- '^(www|blog)\.acme\.org$'
framework:
trusted_proxies: '192.0.0.1,10.0.0.0/8'
trusted_headers:
# - 'x-forwarded-host' Make sure the proxy really sends it
- 'x-forwarded-proto'
- 'x-forwarded-port'
how to fix?
Vulnerable and Outdated Components
FriendsOfPHP/security-advisories
title: "CVE-2020-5274: Fix Exception message escaping rendered by ErrorHandler"
link: https://symfony.com/cve-2020-5274
cve: CVE-2020-5274
branches:
4.4.x:
time: 2020-03-30 14:00:00
versions: ['>=4.4.0', '<4.4.4']
5.0.x:
time: 2020-03-30 14:00:00
versions: ['>=5.0.0', '<5.0.4']
reference: composer://symfony/error-handler
How to use it
$ local-php-security-checker
Symfony Security Check Report
=============================
1 package has known vulnerabilities.
symfony/error-handler (v5.0.3)
------------------------------
* [CVE-2020-5274][]: Fix Exception message escaping rendered by ErrorHandler
[CVE-2020-5274]: https://symfony.com/cve-2020-5274
Note that this checker can only detect vulnerabilities that are referenced in the security
advisories database.
Execute this command regularly to check the newly discovered vulnerabilities.
$ symfony security:check
...
Schedule + notification
- scheduled CI (github actions, gitlab)
- cron on prod machine
- in your CI before your test suite
- dependabot
- must be automated
- team must be notified
roave/security-advisories
- works only when composer up
- no notification / warning
- ...
Broken Access Control
public function adminDashboard(): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
}
security:
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
use symfony / security
how to fix?
public function BlogPostEdit(Post $post): Response
{
$this->denyAccessUnlessGranted('post_edit', $post);
}
CSRF token
is dead?
SameSite Session Cookie
- strict => prevent top-level navigation
- lax => safe methods only
- not supported by all browsers => not trustable
Origin / Referer headers
- removed by anti-virus
- empty in <img /> tags
- empty for GET request (chrome/safari)
- sometimes empty for POST request
- an attacker can force an empty header
- not supported by all browsers
framework:
csrf_protection: ~
public function delete(Request $request): Response
{
$submittedToken = $request->request->get('token');
if (!$this->isCsrfTokenValid('delete-user', $submittedToken)) {
throw new BadRequestHttpException();
}
}
<a href="{{ path('user_delete', {
id: user.id,
csrf_token: csrf_token('delete-user')
}) }}"
>
DELETE
</a>
how to fix?
use good old CSRF token
Security Hardening
Insecure deserialization
How it works?
class TempFile
{
private string $filename;
public function __construct()
{
$this->filename = tempnam(sys_get_temp_dir(), 'test');
}
public function __destruct()
{
unlink($this->filename);
}
public function write(string $content): void {}
}
$_COOKIE['token'] = 'O:8:"TempFile":1:{s:8:"filename";s:17:"../src/Kernel.php";}';
unserialize($_COOKIE['token']);
I'm safe, I don't use unserialize
- php session
- symfony messenger
- symfony cache
Combining multiple package
namespace MessagingComponent;
class MessageBuffer
{
private $messages = [];
private $processor;
public function __construct(ProcessorInterface $processor) {
$this->processor = $processor;
}
public function __destruct() {
$this->processor->flush($this->messages);
}
}
namespace TestingFramework;
class Runner
{
private $serviceLocator;
public function __construct(array $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
public function __call($method, $arguments) {
return call_user_func_array(
$this->serviceLocator[$method],
$arguments,
);
}
}
[
'flush' => 'system'
]
'whoami'
$_COOKIE['token'] = 'O:32:"MessagingComponent\MessageBuffer":2:{s:42:"MessagingComponent\MessageBuffermessages";s:6:"whoami";s:43:"MessagingComponent\MessageBufferprocessor";O:23:"TestingFramework\Runner":1:{s:39:"TestingFramework\RunnerserviceLocator";a:1:{s:5:"flush";s:6:"system";}}}';
unserialize($_COOKIE['token']);
// will call
system('whoami');
- disable unserialization (throw in __wakeup, unserialize, etc...)
- typehint properties
- check carefully __destruct and __wakeup
- use "allowed_classes" unserialize's option
how to mitigate?
be careful with magic methods
User Enumeration
Attack vectors
- error message: "invalid username" vs "invalid password"
- behavior: check username availability in register form
- time measurement
# config/packages/security.yaml
security:
enable_authenticator_manager: true
firewalls:
main:
login_throttling:
max_attempts: 3
interval: '15 minutes'
login thottling
how to mitigate?
DDOS
- multiple request on heavy pages
- password hashing (ie. Argon2)
Attack vectors
- fpm pool dedicated to heavy jobs
- rate limiter
symfony / rate-limiter
how to mitigate?
It is better to know you are in danger than to think you are safe
Alphonse de Lamartine
@jderusse
Thank you!
symfony-security
By Jérémy Derussé
symfony-security
- 1,684