Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f9d196f

Browse files
committed
[Security] Added CSRF sub-component
0 parents  commit f9d196f

13 files changed

Lines changed: 891 additions & 0 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml

CsrfTokenGenerator.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Csrf;
13+
14+
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
15+
use Symfony\Component\Security\Core\Util\SecureRandom;
16+
use Symfony\Component\Security\Core\Util\StringUtils;
17+
use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
18+
use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
19+
20+
/**
21+
* Generates and validates CSRF tokens.
22+
*
23+
* @since 2.4
24+
* @author Bernhard Schussek <[email protected]>
25+
*/
26+
class CsrfTokenGenerator implements CsrfTokenGeneratorInterface
27+
{
28+
/**
29+
* The entropy of the token in bits.
30+
* @var integer
31+
*/
32+
const TOKEN_ENTROPY = 256;
33+
34+
/**
35+
* @var TokenStorageInterface
36+
*/
37+
private $storage;
38+
39+
/**
40+
* The generator for random values.
41+
* @var SecureRandomInterface
42+
*/
43+
private $random;
44+
45+
/**
46+
* Creates a new CSRF provider using PHP's native session storage.
47+
*
48+
* @param TokenStorageInterface $storage The storage for storing generated
49+
* CSRF tokens
50+
* @param SecureRandomInterface $random The used random value generator
51+
* @param integer $entropy The amount of entropy collected for
52+
* newly generated tokens (in bits)
53+
*
54+
*/
55+
public function __construct(TokenStorageInterface $storage = null, SecureRandomInterface $random = null, $entropy = self::TOKEN_ENTROPY)
56+
{
57+
if (null === $storage) {
58+
$storage = new NativeSessionTokenStorage();
59+
}
60+
61+
if (null === $random) {
62+
$random = new SecureRandom();
63+
}
64+
65+
$this->storage = $storage;
66+
$this->random = $random;
67+
$this->entropy = $entropy;
68+
}
69+
70+
/**
71+
* {@inheritDoc}
72+
*/
73+
public function generateCsrfToken($tokenId)
74+
{
75+
$currentToken = $this->storage->getToken($tokenId, false);
76+
77+
// Token exists and is still valid
78+
if (false !== $currentToken) {
79+
return $currentToken;
80+
}
81+
82+
// Token needs to be (re)generated
83+
// Generate an URI safe base64 encoded string that does not contain "+",
84+
// "/" or "=" which need to be URL encoded and make URLs unnecessarily
85+
// longer.
86+
$bytes = $this->random->nextBytes($this->entropy / 8);
87+
$token = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
88+
89+
$this->storage->setToken($tokenId, $token);
90+
91+
return $token;
92+
}
93+
94+
/**
95+
* {@inheritDoc}
96+
*/
97+
public function isCsrfTokenValid($tokenId, $token)
98+
{
99+
if (!$this->storage->hasToken($tokenId)) {
100+
return false;
101+
}
102+
103+
return StringUtils::equals((string) $this->storage->getToken($tokenId), $token);
104+
}
105+
}

CsrfTokenGeneratorInterface.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Csrf;
13+
14+
/**
15+
* Generates and validates CSRF tokens.
16+
*
17+
* You can generate a CSRF token by using the method {@link generateCsrfToken()}.
18+
* This method expects a unique token ID as argument. The token ID can later be
19+
* used to validate a token provided by the user.
20+
*
21+
* Token IDs do not necessarily have to be secret, but they should NEVER be
22+
* created from data provided by the client. A good practice is to hard-code the
23+
* token IDs for the various CSRF tokens used by your application.
24+
*
25+
* You should use the method {@link isCsrfTokenValid()} to check a CSRF token
26+
* submitted by the client. This method will return true if the CSRF token is
27+
* valid.
28+
*
29+
* @since 2.4
30+
* @author Bernhard Schussek <[email protected]>
31+
*/
32+
interface CsrfTokenGeneratorInterface
33+
{
34+
/**
35+
* Generates a CSRF token with the given token ID.
36+
*
37+
* @param string $tokenId An ID that identifies the token
38+
*/
39+
public function generateCsrfToken($tokenId);
40+
41+
/**
42+
* Validates a CSRF token.
43+
*
44+
* @param string $tokenId The token ID used when generating the token
45+
* @param string $token The token supplied by the client
46+
*
47+
* @return Boolean Whether the token supplied by the client is correct
48+
*/
49+
public function isCsrfTokenValid($tokenId, $token);
50+
}

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2004-2013 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Security Component - CSRF
2+
=========================
3+
4+
The Security CSRF (cross-site request forgery) component provides a class
5+
`CsrfTokenGenerator` for generating and validating CSRF tokens.
6+
7+
Resources
8+
---------
9+
10+
Documentation:
11+
12+
http://symfony.com/doc/2.4/book/security.html
13+
14+
Tests
15+
-----
16+
17+
You can run the unit tests with the following command:
18+
19+
$ cd path/to/Symfony/Component/Security/Csrf/
20+
$ composer.phar install --dev
21+
$ phpunit

Tests/CsrfTokenGeneratorTest.php

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Extension\Csrf\CsrfProvider;
13+
14+
use Symfony\Component\Security\Csrf\CsrfTokenGenerator;
15+
16+
/**
17+
* @author Bernhard Schussek <[email protected]>
18+
*/
19+
class CsrfTokenGeneratorTest extends \PHPUnit_Framework_TestCase
20+
{
21+
/**
22+
* A non alpha-numeric byte string
23+
* @var string
24+
*/
25+
private static $bytes;
26+
27+
/**
28+
* @var \PHPUnit_Framework_MockObject_MockObject
29+
*/
30+
private $random;
31+
32+
/**
33+
* @var \PHPUnit_Framework_MockObject_MockObject
34+
*/
35+
private $storage;
36+
37+
/**
38+
* @var CsrfTokenGenerator
39+
*/
40+
private $generator;
41+
42+
public static function setUpBeforeClass()
43+
{
44+
self::$bytes = base64_decode('aMf+Tct/RLn2WQ==');
45+
}
46+
47+
protected function setUp()
48+
{
49+
$this->random = $this->getMock('Symfony\Component\Security\Core\Util\SecureRandomInterface');
50+
$this->storage = $this->getMock('Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface');
51+
$this->generator = new CsrfTokenGenerator($this->storage, $this->random);
52+
}
53+
54+
protected function tearDown()
55+
{
56+
$this->random = null;
57+
$this->storage = null;
58+
$this->generator = null;
59+
}
60+
61+
public function testGenerateNewToken()
62+
{
63+
$this->storage->expects($this->once())
64+
->method('getToken')
65+
->with('token_id', false)
66+
->will($this->returnValue(false));
67+
68+
$this->storage->expects($this->once())
69+
->method('setToken')
70+
->with('token_id', $this->anything())
71+
->will($this->returnCallback(function ($tokenId, $token) use (&$storedToken) {
72+
$storedToken = $token;
73+
}));
74+
75+
$this->random->expects($this->once())
76+
->method('nextBytes')
77+
->will($this->returnValue(self::$bytes));
78+
79+
$token = $this->generator->generateCsrfToken('token_id');
80+
81+
$this->assertSame($token, $storedToken);
82+
$this->assertTrue(ctype_print($token), 'is printable');
83+
$this->assertStringNotMatchesFormat('%S+%S', $token, 'is URI safe');
84+
$this->assertStringNotMatchesFormat('%S/%S', $token, 'is URI safe');
85+
$this->assertStringNotMatchesFormat('%S=%S', $token, 'is URI safe');
86+
}
87+
88+
public function testUseExistingTokenIfAvailable()
89+
{
90+
$this->storage->expects($this->once())
91+
->method('getToken')
92+
->with('token_id', false)
93+
->will($this->returnValue('TOKEN'));
94+
95+
$this->storage->expects($this->never())
96+
->method('setToken');
97+
98+
$this->random->expects($this->never())
99+
->method('nextBytes');
100+
101+
$token = $this->generator->generateCsrfToken('token_id');
102+
103+
$this->assertEquals('TOKEN', $token);
104+
}
105+
106+
public function testMatchingTokenIsValid()
107+
{
108+
$this->storage->expects($this->once())
109+
->method('hasToken')
110+
->with('token_id')
111+
->will($this->returnValue(true));
112+
113+
$this->storage->expects($this->once())
114+
->method('getToken')
115+
->with('token_id')
116+
->will($this->returnValue('TOKEN'));
117+
118+
$this->assertTrue($this->generator->isCsrfTokenValid('token_id', 'TOKEN'));
119+
}
120+
121+
public function testNonMatchingTokenIsNotValid()
122+
{
123+
$this->storage->expects($this->once())
124+
->method('hasToken')
125+
->with('token_id')
126+
->will($this->returnValue(true));
127+
128+
$this->storage->expects($this->once())
129+
->method('getToken')
130+
->with('token_id')
131+
->will($this->returnValue('TOKEN'));
132+
133+
$this->assertFalse($this->generator->isCsrfTokenValid('token_id', 'FOOBAR'));
134+
}
135+
136+
public function testNonExistingTokenIsNotValid()
137+
{
138+
$this->storage->expects($this->once())
139+
->method('hasToken')
140+
->with('token_id')
141+
->will($this->returnValue(false));
142+
143+
$this->storage->expects($this->never())
144+
->method('getToken');
145+
146+
$this->assertFalse($this->generator->isCsrfTokenValid('token_id', 'FOOBAR'));
147+
}
148+
}

0 commit comments

Comments
 (0)