Skip to content

Commit 811671d

Browse files
committed
Merge branch '2.8' into 3.0
* 2.8: Hash nonce when using as file name File System Security Issue in Custom Auth Article [Cookbook] Tweaking registration_form (e.g. bcrypt)
2 parents 266f7bf + 52f14f8 commit 811671d

File tree

2 files changed

+70
-16
lines changed

2 files changed

+70
-16
lines changed

Diff for: cookbook/doctrine/registration_form.rst

+64-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
.. index::
22
single: Doctrine; Simple Registration Form
33
single: Form; Simple Registration Form
4+
single: Security; Simple Registration Form
45

5-
How to Implement a simple Registration Form
6+
How to Implement a Simple Registration Form
67
===========================================
78

89
Creating a registration form is pretty easy - it *really* means just creating
9-
a form that will update some ``User`` model object (a Doctrine entity in this example)
10-
and then save it.
10+
a form that will update some ``User`` model object (a Doctrine entity in this
11+
example) and then save it.
1112

1213
.. tip::
1314

14-
The popular `FOSUserBundle`_ provides a registration form, reset password form
15-
and other user management functionality.
15+
The popular `FOSUserBundle`_ provides a registration form, reset password
16+
form and other user management functionality.
1617

1718
If you don't already have a ``User`` entity and a working login system,
1819
first start with :doc:`/cookbook/security/entity_provider`.
@@ -61,27 +62,27 @@ With some validation added, your class may look something like this::
6162
private $id;
6263

6364
/**
64-
* @ORM\Column(type="string", length=255)
65+
* @ORM\Column(type="string", length=255, unique=true)
6566
* @Assert\NotBlank()
6667
* @Assert\Email()
6768
*/
6869
private $email;
6970

7071
/**
71-
* @ORM\Column(type="string", length=255)
72+
* @ORM\Column(type="string", length=255, unique=true)
7273
* @Assert\NotBlank()
7374
*/
7475
private $username;
7576

7677
/**
7778
* @Assert\NotBlank()
78-
* @Assert\Length(max = 4096)
79+
* @Assert\Length(max=4096)
7980
*/
8081
private $plainPassword;
8182

8283
/**
8384
* The below length depends on the "algorithm" you use for encoding
84-
* the password, but this works well with bcrypt
85+
* the password, but this works well with bcrypt.
8586
*
8687
* @ORM\Column(type="string", length=64)
8788
*/
@@ -124,6 +125,13 @@ With some validation added, your class may look something like this::
124125
$this->password = $password;
125126
}
126127

128+
public function getSalt()
129+
{
130+
// The bcrypt algorithm don't require a separate salt.
131+
// You *may* need a real salt if you choose a different encoder.
132+
return null;
133+
}
134+
127135
// other methods, including security methods like getRoles()
128136
}
129137

@@ -146,8 +154,10 @@ example, see the :ref:`Entity Provider <security-crete-user-entity>` article.
146154
only place where you don't need to worry about this is your login form,
147155
since Symfony's Security component handles this for you.
148156

149-
Create a Form for the Model
150-
---------------------------
157+
.. _create-a-form-for-the-model:
158+
159+
Create a Form for the Entity
160+
----------------------------
151161

152162
Next, create the form for the ``User`` entity::
153163

@@ -195,8 +205,9 @@ There are just three fields: ``email``, ``username`` and ``plainPassword``
195205
Handling the Form Submission
196206
----------------------------
197207

198-
Next, you need a controller to handle the form. Start by creating a simple
199-
controller for displaying the registration form::
208+
Next, you need a controller to handle the form rendering and submission. If the
209+
form is submitted, the controller performs the validation and saves the data
210+
into the database::
200211

201212
// src/AppBundle/Controller/RegistrationController.php
202213
namespace AppBundle\Controller;
@@ -222,6 +233,7 @@ controller for displaying the registration form::
222233
// 2) handle the submit (will only happen on POST)
223234
$form->handleRequest($request);
224235
if ($form->isSubmitted() && $form->isValid()) {
236+
225237
// 3) Encode the password (you could also do this via Doctrine listener)
226238
$password = $this->get('security.password_encoder')
227239
->encodePassword($user, $user->getPlainPassword());
@@ -245,6 +257,45 @@ controller for displaying the registration form::
245257
}
246258
}
247259

260+
To define the algorithm used to encode the password in step 3 configure the
261+
encoder in the security configuration:
262+
263+
.. configuration-block::
264+
265+
.. code-block:: yaml
266+
267+
# app/config/security.yml
268+
security:
269+
encoders:
270+
AppBundle\Entity\User: bcrypt
271+
272+
.. code-block:: xml
273+
274+
<!-- app/config/security.xml -->
275+
<?xml version="1.0" charset="UTF-8" ?>
276+
<srv:container xmlns="http://symfony.com/schema/dic/security"
277+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
278+
xmlns:srv="http://symfony.com/schema/dic/services"
279+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
280+
281+
<config>
282+
<encoder class="AppBundle\Entity\User">bcrypt</encoder>
283+
</config>
284+
</srv:container>
285+
286+
.. code-block:: php
287+
288+
// app/config/security.php
289+
$container->loadFromExtension('security', array(
290+
'encoders' => array(
291+
'AppBundle\Entity\User' => 'bcrypt',
292+
),
293+
));
294+
295+
In this case the recommended ``bcrypt`` algorithm is used. To learn more
296+
about how to encode the users password have a look into the
297+
:ref:`security chapter <book-security-encoding-user-password>`.
298+
248299
.. note::
249300

250301
If you decide to NOT use annotation routing (shown above), then you'll

Diff for: cookbook/security/custom_authentication_provider.rst

+6-3
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ set an authenticated token in the token storage if successful.
135135
{
136136
$request = $event->getRequest();
137137
138-
$wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
138+
$wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([a-zA-Z0-9+/]+={0,2})", Created="([^"]+)"/';
139139
if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
140140
return;
141141
}
@@ -260,14 +260,17 @@ the ``PasswordDigest`` header value matches with the user's password.
260260
261261
// Validate that the nonce is *not* used in the last 5 minutes
262262
// if it has, this could be a replay attack
263-
if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) {
263+
if (
264+
file_exists($this->cacheDir.'/'.md5($nonce))
265+
&& file_get_contents($this->cacheDir.'/'.md5($nonce)) + 300 > time()
266+
) {
264267
throw new NonceExpiredException('Previously used nonce detected');
265268
}
266269
// If cache directory does not exist we create it
267270
if (!is_dir($this->cacheDir)) {
268271
mkdir($this->cacheDir, 0777, true);
269272
}
270-
file_put_contents($this->cacheDir.'/'.$nonce, time());
273+
file_put_contents($this->cacheDir.'/'.md5($nonce), time());
271274
272275
// Validate Secret
273276
$expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));

0 commit comments

Comments
 (0)