Skip to content

Commit 6e4bf91

Browse files
committed
Merge pull request #4 from rezzza/obfuscation
obfuscation
2 parents 8881e70 + da1f37b commit 6e4bf91

File tree

17 files changed

+1806
-3
lines changed

17 files changed

+1806
-3
lines changed

.atoum.bootstrap.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
require_once __DIR__.'/vendor/autoload.php';

.atoum.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
use \mageekguy\atoum;
4+
5+
$script->bootstrapFile(__DIR__ . DIRECTORY_SEPARATOR . '.atoum.bootstrap.php');
6+
7+
$cliReport = $script->addDefaultReport();
8+
$cliReport->addField(new atoum\report\fields\runner\result\logo());
9+
10+
$runner->addReport($cliReport);
11+
$runner->addTestsFromDirectory(__DIR__.'/Tests/Units');

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
bin
12
composer.phar
23
vendor

.travis.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
language: php
2+
3+
php:
4+
- 5.3
5+
6+
before_script:
7+
- wget http://getcomposer.org/composer.phar
8+
- php composer.phar install --dev --prefer-source
9+
10+
script:
11+
- bin/atoum
12+
13+
notifications:
14+
email:
15+
recipients:
16+
17+
on_success: change
18+
on_failure: change
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Rezzza\SecurityBundle\Controller\Annotations;
4+
5+
/**
6+
* @Annotation()
7+
*
8+
* ObfuscateRequest
9+
*
10+
* @author Stephane PY <[email protected]>
11+
*/
12+
class ObfuscateRequest
13+
{
14+
/**
15+
* @var array<string>
16+
*/
17+
private $obfuscatedPatterns;
18+
19+
/**
20+
* @param array $data data
21+
*/
22+
public function __construct(array $obfuscatedPatterns)
23+
{
24+
$this->obfuscatedPatterns = $obfuscatedPatterns;
25+
}
26+
27+
/**
28+
* @return array<string>
29+
*/
30+
public function getObfuscatedPatterns()
31+
{
32+
return $this->obfuscatedPatterns;
33+
}
34+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Rezzza\SecurityBundle\DataCollector;
4+
5+
use Doctrine\Common\Annotations\Reader as AnnotationReader;
6+
use Doctrine\Common\Util\ClassUtils;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\HttpFoundation\Response;
9+
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector as BaseRequestDataCollector;
10+
use Rezzza\SecurityBundle\Controller\Annotations\ObfuscateRequest;
11+
use Rezzza\SecurityBundle\Request\Obfuscator\ObfuscatorInterface;
12+
13+
class RequestDataCollector extends BaseRequestDataCollector
14+
{
15+
/**
16+
* @var AnnotationReader
17+
*/
18+
private $annotationReader;
19+
20+
/**
21+
* @var Obfuscator
22+
*/
23+
private $obfuscator;
24+
25+
/**
26+
* @param AnnotationReader $annotationReader annotationReader
27+
* @param ObfuscatorInterface $obfuscator obfuscator
28+
*/
29+
public function __construct(AnnotationReader $annotationReader, ObfuscatorInterface $obfuscator)
30+
{
31+
$this->annotationReader = $annotationReader;
32+
$this->obfuscator = $obfuscator;
33+
34+
parent::__construct();
35+
}
36+
37+
public function collect(Request $request, Response $response, \Exception $exception = null)
38+
{
39+
parent::collect($request, $response, $exception);
40+
41+
$controller = explode('::', $request->get('_controller'));
42+
43+
if (count($controller) !== 2) {
44+
return;
45+
}
46+
47+
$class = new \ReflectionClass($controller[0]);
48+
$reflectionMethod = $class->getMethod($controller[1]);
49+
$annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, '\Rezzza\SecurityBundle\Controller\Annotations\ObfuscateRequest');
50+
51+
if ($annotation) {
52+
$this->data = $this->obfuscator->obfuscate($this->data, $annotation->getObfuscatedPatterns());
53+
}
54+
}
55+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Rezzza\SecurityBundle\DependencyInjection\Compiler;
4+
5+
use Symfony\Component\DependencyInjection\Reference;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
8+
9+
class ObfuscatorCompilerPass implements CompilerPassInterface
10+
{
11+
/**
12+
* {@inheritdoc}
13+
*/
14+
public function process(ContainerBuilder $container)
15+
{
16+
// request obfuscator is not enabled.
17+
if (!$container->getParameter('rezzza.security.request_obfuscator.enabled')) {
18+
return;
19+
}
20+
21+
$container->setParameter('data_collector.request.class', 'Rezzza\SecurityBundle\DataCollector\RequestDataCollector');
22+
23+
$container->getDefinition('data_collector.request')
24+
->addArgument(new Reference('annotation_reader'))
25+
->addArgument(new Reference('rezzza.security.request_obfuscator.obfuscator'));
26+
}
27+
}

DependencyInjection/Configuration.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public function getConfigTreeBuilder()
2525

2626
$rootNode
2727
->children()
28+
->arrayNode('request_obfuscator')
29+
->addDefaultsIfNotSet()
30+
->children()
31+
->booleanNode('enabled')->defaultFalse()->end()
32+
->end()
33+
->end()
2834
->arrayNode('firewalls')
2935
->useAttributeAsKey('name')
3036
->prototype('array')

DependencyInjection/RezzzaSecurityExtension.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,15 @@ public function load(array $configs, ContainerBuilder $container)
2929
$config = $processor->processConfiguration($configuration, $configs);
3030

3131
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config/services'));
32+
3233
$loader->load('security.xml');
3334

35+
$container->setParameter('rezzza.security.request_obfuscator.enabled', $config['request_obfuscator']['enabled']);
36+
37+
if ($container->getParameter('rezzza.security.request_obfuscator.enabled')) {
38+
$loader->load('request_obfuscator.xml');
39+
}
40+
3441
$firewalls = $config['firewalls'];
3542

3643
foreach ($firewalls as $name => $data) {

README.md

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
SecurityBundle
22
==============
33

4+
[![Build Status](https://travis-ci.com/rezzza/SecurityBundle.png)](https://travis-ci.com/#!/rezzza/SecurityBundle)
5+
46
# Installation
57

68
## With Composer
@@ -71,8 +73,6 @@ It'll hash all theses criterias with a secret defined on `security.yml`, example
7173
Build the signature:
7274

7375
```php
74-
use \Rezzza\SecurityBundle\Security\Authentication\RequestDataCollector;
75-
7676
$context = new \Rezzza\SecurityBundle\Security\Firewall\Context();
7777
$context->set('request.method', 'GET')
7878
->set('request.host', 'subdomain.domain.tld')
@@ -112,6 +112,59 @@ $builder = $this->get('rezzza.security.request_signature.builder');
112112
$signature = $builder->build($context);
113113
```
114114

115+
# Obfuscate request
116+
117+
If you have critical data coming on your application, you may not want to expose them into symfony profiler. You can easily define which data will not appear on this one on each routes.
118+
119+
```
120+
rezzza_security:
121+
request_obfuscator:
122+
enabled: 1
123+
```
124+
125+
In your route:
126+
127+
```
128+
129+
use \Rezzza\SecurityBundle\Controller\Annotations\ObfuscateRequest;
130+
131+
/**
132+
* @ObfuscateRequest()
133+
*/
134+
public function indexAction(Request $request)
135+
{
136+
}
137+
```
138+
139+
Will obfuscate all datas on symfony profiler.
140+
141+
```
142+
@obfuscate("content=*") // obfuscate $request->getContent()
143+
@obfuscate("headers={'foobar'}") // obfuscate $request->headers->get('foobar')
144+
@obfuscate("request_request={"customer[password]"}") // obfuscate $request->request->get('customer')['password']
145+
```
146+
147+
Keys to obfuscate are:
148+
149+
- format
150+
- content
151+
- content_type
152+
- status_text
153+
- status_code
154+
- request_query ($_GET)
155+
- request_request ($_POST)
156+
- request_headers ($_HEADER)
157+
- request_server ($_SERVER)
158+
- request_cookies ($_COOKIES)
159+
- request_attributes ($request->attributes)
160+
- response_headers
161+
- session_metadata
162+
- session_attributes
163+
- flashes
164+
- path_info
165+
- controller
166+
- locale
167+
115168
# WishList
116169
117170
- QueryString or HTTP Headers
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Rezzza\SecurityBundle\Request\Obfuscator;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
7+
/**
8+
* ObfuscatorInterface
9+
*
10+
* @author Stephane PY <[email protected]>
11+
*/
12+
interface ObfuscatorInterface
13+
{
14+
/**
15+
* @param array $data data
16+
* @param array $obfuscatedPatterns obfuscatedPatterns
17+
*
18+
* @return array
19+
*/
20+
public function obfuscate(array $data, array $obfuscatedPatterns);
21+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Rezzza\SecurityBundle\Request\Obfuscator;
4+
5+
use Rezzza\SecurityBundle\Exception\ObfuscateBadPatternException;
6+
7+
/**
8+
* RequestObfuscator
9+
*
10+
* @uses ObfuscatorInterface
11+
* @author Stephane PY <[email protected]>
12+
*/
13+
class RequestObfuscator implements ObfuscatorInterface
14+
{
15+
CONST TOKEN_REPLACE = 'X';
16+
CONST TOKEN_ALL = '*';
17+
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public function obfuscate(array $data, array $obfuscatedPatterns)
22+
{
23+
foreach ($obfuscatedPatterns as $key => $pattern) {
24+
if (isset($data[$key])) {
25+
$data[$key] = $this->obfuscateContentWithPattern($data[$key], $pattern);
26+
}
27+
}
28+
29+
return $data;
30+
}
31+
32+
private function obfuscateContentWithPattern($content, $pattern)
33+
{
34+
if (!is_array($content)) {
35+
return is_scalar($content) ? $this->obfuscateContent($content) : null;
36+
}
37+
38+
if ($pattern === self::TOKEN_ALL) {
39+
return self::TOKEN_REPLACE;
40+
}
41+
42+
$patterns = (array) $pattern;
43+
foreach ($patterns as $pattern) {
44+
$keys = array_map(function($v) {
45+
return str_replace(']', '', $v);
46+
}, explode('[', $pattern));
47+
48+
$pattern = array_shift($keys);
49+
50+
if (array_key_exists($pattern, $content)) {
51+
if (count($keys) === 0) {
52+
$content[$pattern] = $this->obfuscateContent($content[$pattern]);
53+
} else {
54+
$newPattern = array_shift($keys);
55+
foreach ($keys as $key) {
56+
$newPattern .= sprintf('[%s]', $key);
57+
}
58+
$content[$pattern] = $this->obfuscateContentWithPattern($content[$pattern], $newPattern);
59+
}
60+
}
61+
}
62+
63+
return $content;
64+
}
65+
66+
private function obfuscateContent($content)
67+
{
68+
return is_scalar($content) ? str_repeat(self::TOKEN_REPLACE, strlen($content)) : self::TOKEN_REPLACE;
69+
}
70+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<parameters>
8+
<parameter key="rezzza.security.request_obfuscator.obfuscator.class">Rezzza\SecurityBundle\Request\Obfuscator\RequestObfuscator</parameter>
9+
</parameters>
10+
11+
<services>
12+
<service id="rezzza.security.request_obfuscator.obfuscator" class="%rezzza.security.request_obfuscator.obfuscator.class%"/>
13+
</services>
14+
15+
</container>

RezzzaSecurityBundle.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Rezzza\SecurityBundle;
44

55
use Rezzza\SecurityBundle\DependencyInjection\Security\Factory\RequestSignatureFactory;
6+
use Rezzza\SecurityBundle\DependencyInjection\Compiler;
67
use Symfony\Component\HttpKernel\Bundle\Bundle;
78
use Symfony\Component\DependencyInjection\ContainerBuilder;
89

@@ -27,5 +28,7 @@ public function build(ContainerBuilder $container)
2728
if (method_exists('\Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension', 'addSecurityListenerFactory')) {
2829
$extension->addSecurityListenerFactory(new RequestSignatureFactory());
2930
}
31+
32+
$container->addCompilerPass(new Compiler\ObfuscatorCompilerPass());
3033
}
3134
}

0 commit comments

Comments
 (0)