| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.auth.adapter.dbtable">
- <title>Database Table Authentication</title>
- <sect2 id="zend.auth.adapter.dbtable.introduction">
- <title>Introduction</title>
- <para>
- <classname>Zend_Auth_Adapter_DbTable</classname> provides the ability to
- authenticate against credentials stored in a database table. Because
- <classname>Zend_Auth_Adapter_DbTable</classname> requires an instance of
- <classname>Zend_Db_Adapter_Abstract</classname> to be passed to its
- constructor, each instance is bound to a particular database
- connection. Other configuration options may be set through the
- constructor and through instance methods, one for each option.
- </para>
- <para>
- The available configuration options include:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis><property>tableName</property></emphasis>: This is the name of the
- database table that contains the authentication credentials,
- and against which the database authentication query is
- performed.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis><property>identityColumn</property></emphasis>: This is the name of
- the database table column used to represent the identity.
- The identity column must contain unique values, such as
- a username or e-mail address.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis><property>credentialColumn</property></emphasis>: This is the name
- of the database table column used to represent the credential.
- Under a simple identity and password authentication
- scheme, the credential value corresponds to the
- password. See also the <property>credentialTreatment</property>
- option.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis><property>credentialTreatment</property></emphasis>: In many cases,
- passwords and other sensitive data are encrypted,
- hashed, encoded, obscured, salted or otherwise treated
- through some function or algorithm. By specifying a
- parameterized treatment string with this method, such as
- '<methodname>MD5(?)</methodname>' or
- '<methodname>PASSWORD(?)</methodname>', a
- developer may apply such arbitrary <acronym>SQL</acronym> upon input
- credential data. Since these functions are specific to
- the underlying <acronym>RDBMS</acronym>, check the database manual for the
- availability of such functions for your database system.
- </para>
- </listitem>
- </itemizedlist>
- <example id="zend.auth.adapter.dbtable.introduction.example.basic_usage">
- <title>Basic Usage</title>
- <para>
- As explained in the introduction, the
- <classname>Zend_Auth_Adapter_DbTable</classname> constructor requires an
- instance of <classname>Zend_Db_Adapter_Abstract</classname> that serves as
- the database connection to which the authentication adapter
- instance is bound. First, the database connection should be
- created.
- </para>
- <para>
- The following code creates an adapter for an in-memory database,
- creates a simple table schema, and inserts a row against
- which we can perform an authentication query later. This example
- requires the <acronym>PDO</acronym> SQLite extension to be available:
- </para>
- <programlisting language="php"><![CDATA[
- // Create an in-memory SQLite database connection
- $dbAdapter = new Zend_Db_Adapter_Pdo_Sqlite(array('dbname' =>
- ':memory:'));
- // Build a simple table creation query
- $sqlCreate = 'CREATE TABLE [users] ('
- . '[id] INTEGER NOT NULL PRIMARY KEY, '
- . '[username] VARCHAR(50) UNIQUE NOT NULL, '
- . '[password] VARCHAR(32) NULL, '
- . '[real_name] VARCHAR(150) NULL)';
- // Create the authentication credentials table
- $dbAdapter->query($sqlCreate);
- // Build a query to insert a row for which authentication may succeed
- $sqlInsert = "INSERT INTO users (username, password, real_name) "
- . "VALUES ('my_username', 'my_password', 'My Real Name')";
- // Insert the data
- $dbAdapter->query($sqlInsert);
- ]]></programlisting>
- <para>
- With the database connection and table data available, an
- instance of <classname>Zend_Auth_Adapter_DbTable</classname> may be
- created. Configuration option values may be passed to the
- constructor or deferred as parameters to setter methods after
- instantiation:
- </para>
- <programlisting language="php"><![CDATA[
- // Configure the instance with constructor parameters...
- $authAdapter = new Zend_Auth_Adapter_DbTable(
- $dbAdapter,
- 'users',
- 'username',
- 'password'
- );
- // ...or configure the instance with setter methods
- $authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
- $authAdapter
- ->setTableName('users')
- ->setIdentityColumn('username')
- ->setCredentialColumn('password')
- ;
- ]]></programlisting>
- <para>
- At this point, the authentication adapter instance is ready to
- accept authentication queries. In order to formulate an
- authentication query, the input credential values are passed to
- the adapter prior to calling the <methodname>authenticate()</methodname>
- method:
- </para>
- <programlisting language="php"><![CDATA[
- // Set the input credential values (e.g., from a login form)
- $authAdapter
- ->setIdentity('my_username')
- ->setCredential('my_password')
- ;
- // Perform the authentication query, saving the result
- ]]></programlisting>
- <para>
- In addition to the availability of the
- <methodname>getIdentity()</methodname> method upon the authentication result
- object, <classname>Zend_Auth_Adapter_DbTable</classname> also supports
- retrieving the table row upon authentication success:
- </para>
- <programlisting language="php"><![CDATA[
- // Print the identity
- echo $result->getIdentity() . "\n\n";
- // Print the result row
- print_r($authAdapter->getResultRowObject());
- /* Output:
- my_username
- Array
- (
- [id] => 1
- [username] => my_username
- [password] => my_password
- [real_name] => My Real Name
- )
- ]]></programlisting>
- <para>
- Since the table row contains the credential value, it is
- important to secure the values against unintended access.
- </para>
- </example>
- </sect2>
- <sect2 id="zend.auth.adapter.dbtable.advanced.storing_result_row">
- <title>Advanced Usage: Persisting a DbTable Result Object</title>
- <para>
- By default, <classname>Zend_Auth_Adapter_DbTable</classname> returns the
- identity supplied back to the auth object upon successful
- authentication. Another use case scenario, where developers want to
- store to the persistent storage mechanism of <classname>Zend_Auth</classname>
- an identity object containing other useful information, is solved by
- using the <methodname>getResultRowObject()</methodname> method to return a
- <emphasis>stdClass</emphasis> object. The following code snippet illustrates
- its use:
- </para>
- <programlisting language="php"><![CDATA[
- // authenticate with Zend_Auth_Adapter_DbTable
- $result = $this->_auth->authenticate($adapter);
- if ($result->isValid()) {
- // store the identity as an object where only the username and
- // real_name have been returned
- $storage = $this->_auth->getStorage();
- $storage->write($adapter->getResultRowObject(array(
- 'username',
- 'real_name',
- )));
- // store the identity as an object where the password column has
- // been omitted
- $storage->write($adapter->getResultRowObject(
- null,
- 'password'
- ));
- /* ... */
- } else {
- /* ... */
- }
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.auth.adapter.dbtable.advanced.advanced_usage">
- <title>Advanced Usage By Example</title>
- <para>
- While the primary purpose of <classname>Zend_Auth</classname> (and consequently
- <classname>Zend_Auth_Adapter_DbTable</classname>) is primarily
- <emphasis>authentication</emphasis> and not
- <emphasis>authorization</emphasis>, there are a few
- instances and problems that toe the line between which domain they fit
- within. Depending on how you've decided to explain your problem, it
- sometimes makes sense to solve what could look like an
- authorization problem within the authentication adapter.
- </para>
- <para>
- With that disclaimer out of the way, <classname>Zend_Auth_Adapter_DbTable</classname>
- has some built in mechanisms that can be leveraged for additional checks at
- authentication time to solve some common user problems.
- </para>
- <programlisting language="php"><![CDATA[
- // The status field value of an account is not equal to "compromised"
- $adapter = new Zend_Auth_Adapter_DbTable(
- $db,
- 'users',
- 'username',
- 'password',
- 'MD5(?) AND status != "compromised"'
- );
- // The active field value of an account is equal to "TRUE"
- $adapter = new Zend_Auth_Adapter_DbTable(
- $db,
- 'users',
- 'username',
- 'password',
- 'MD5(?) AND active = "TRUE"'
- ]]></programlisting>
- <para>
- Another scenario can be the implementation of a salting mechanism.
- Salting is a term referring to a technique which can highly improve
- your application's security. It's based on the idea that
- concatenating a random string to every password makes it impossible
- to accomplish a successful brute force attack on the database using
- pre-computed hash values from a dictionary.
- </para>
- <para>
- Therefore, we need to modify our table to store our salt string:
- </para>
- <programlisting language="php"><![CDATA[
- $sqlAlter = "ALTER TABLE [users] "
- . "ADD COLUMN [password_salt] "
- . "AFTER [password]";
- ]]></programlisting>
- <para>
- Here's a simple way to generate a salt string for every user at
- registration:
- </para>
- <programlisting language="php"><![CDATA[
- for ($i = 0; $i < 50; $i++) {
- $dynamicSalt .= chr(rand(33, 126));
- ]]></programlisting>
- <para>
- And now let's build the adapter:
- </para>
- <programlisting language="php"><![CDATA[
- $adapter = new Zend_Auth_Adapter_DbTable(
- $db,
- 'users',
- 'username',
- 'password',
- "MD5(CONCAT('"
- . Zend_Registry::get('staticSalt')
- . "', ?, password_salt))"
- );
- ]]></programlisting>
- <note>
- <para>
- You can improve security even more by using a static salt value
- hard coded into your application. In the case that your database
- is compromised (e. g. by an <acronym>SQL</acronym> injection attack) but your web
- server is intact your data is still unusable for the attacker.
- </para>
- </note>
- <para>
- Another alternative is to use the <methodname>getDbSelect()</methodname> method
- of the <classname>Zend_Auth_Adapter_DbTable</classname> after the adapter has been
- constructed. This method will return the <classname>Zend_Db_Select</classname> object
- instance it will use to complete the <methodname>authenticate()</methodname> routine.
- It is important to note that this method will always return the same object regardless
- if <methodname>authenticate()</methodname> has been called or not. This object
- <emphasis>will not</emphasis> have any of the identity or credential information in it
- as those values are placed into the select object at
- <methodname>authenticate()</methodname> time.
- </para>
- <para>
- An example of a situation where one might want to use the
- <methodname>getDbSelect()</methodname> method would check the status of a user, in
- other words to see if that user's account is enabled.
- </para>
- <programlisting language="php"><![CDATA[
- // Continuing with the example from above
- $adapter = new Zend_Auth_Adapter_DbTable(
- $db,
- 'users',
- 'username',
- 'password',
- 'MD5(?)'
- );
- // get select object (by reference)
- $select = $adapter->getDbSelect();
- $select->where('active = "TRUE"');
- // authenticate, this ensures that users.active = TRUE
- $adapter->authenticate();
- ]]></programlisting>
- </sect2>
- </sect1>
|