gerzson Wed Jan 16 05:09:37 2002 EDT
Modified files:
/phpdoc/en/chapters security.xml
Log:
new section added about database security
please someone run a 'make test'
Index: phpdoc/en/chapters/security.xml
diff -u phpdoc/en/chapters/security.xml:1.39 phpdoc/en/chapters/security.xml:1.40
--- phpdoc/en/chapters/security.xml:1.39 Tue Dec 18 18:18:36 2001
+++ phpdoc/en/chapters/security.xml Wed Jan 16 05:09:37 2002
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- $Revision: 1.39 $ -->
+<!-- $Revision: 1.40 $ -->
<chapter id="security">
<title>Security</title>
@@ -484,6 +484,394 @@
reason, it's usually easier to create a policy where you forbid
everything except for what you explicitly allow.
</para>
+ </sect1>
+
+ <sect1 id="security.database">
+ <title>Database Security</title>
+
+ <simpara>
+ Nowadays, databases are cardinal components of any web based application by
+ enabling websites to provide varying dynamic content. Since very sensitive
+ or secret informations can be stored in such database, you should strongly
+ consider to protect them somehow.
+ </simpara>
+ <simpara>
+ PHP can be treated as a bridge between the database and client. Your script
+ processes the client's request, and propagates it in such manner that the
+ database can provide the appropriate response. After that, the script
+ generates its output from the supplied data, probably based on customizeable
+ user preferences stored in database, too.
+ </simpara>
+ <simpara>
+ To retrieve or to store any information you need to connect to the database,
+ send a legitimate query, fetch the result, and close the connecion.
+ Nowadays, the commonly used interface in the interaction with databases is
+ the Structured Query Language (SQL). See how an attacker can <link
+ linkend="security.database.sql-injection">tamper with an SQL query</link>.
+ </simpara>
+ <simpara>
+ As you can realize, PHP cannot protect your database by itself. The
+ following sections aim to be an introduction into the very basics of how to
+ access and manipulate databases within PHP scripts.
+ </simpara>
+ <simpara>
+ Keep in my mind this simple rule: defence in depth. In the more place you
+ take the more action to increase the protection of your database, the less
+ probability of that an attacker succeeds, and exposes or abuse any stored
+ secret information. Good design of the database schema and the application
+ deals with your greatest fears.
+ </simpara>
+
+ <sect2 id="security.database.design">
+ <title>Designing Databases</title>
+ <simpara>
+ The first step is always to create the database, unless you want to use
+ an existing third party's one. When a database is created, it is
+ assigned to an owner, who executed the creation statement. Usually, only
+ the owner (or a superuser) can do anything with the objects in that
+ database, and in order to allow other users to use it, privileges must be
+ granted.
+ </simpara>
+ <simpara>
+ Applications should never connect to the database as its owner or a
+ superuser, because these users can execute any query at will, for
+ example, modifying the schema (e.g. dropping tables) or deleting its
+ entire content.
+ </simpara>
+ <simpara>
+ You may create different database users for every aspect of your
+ application with very limited rights to database objects. The most
+ required privileges should be granted only, and avoid that the same user
+ can interact with the database in different use cases. This means that if
+ intruders gain access to your database using one of these credentials,
+ they can only effect as many changes as your application can.
+ </simpara>
+ <simpara>
+ You are encouraged not to implement all the business logic in the web
+ application (i.e. your script), instead to do it in the database schema
+ using views, triggers or rules. If the system evolves, new ports will be
+ intended to open to the database, and you have to reimplement the logic
+ in each separate database client. Over and above, triggers can be used
+ to transparently and automatically handle fields, which often provides
+ insight when debugging problems with your application or tracing back
+ transactions.
+ </simpara>
+ </sect2>
+
+ <sect2 id="security.database.connection">
+ <title>Connecting to Database</title>
+ <simpara>
+ You may want to estabilish the connections over SSL to encrypt
+ client/server communications for increased security, or you can use ssh
+ to encrypt the network connection between clients and the database server.
+ If either of them is done, then monitoring your traffic and gaining
+ informations in this way will be a hard work.
+ </simpara>
+ <!--simpara>
+ If your database server native SSL support, consider to use <link
+ linkend="ref.openssl">OpenSSL functions</link> in communication between
+ PHP and database via SSL.
+ </simpara-->
+ </sect2>
+
+ <sect2 id="security.database.storage">
+ <title>Encrypted Storage Model</title>
+ <simpara>
+ SSL/SSH protects data traveling from the client to the server, SSL/SSH
+ does not protect the persistent data stored in a database. SSL is an
+ on-the-wire protocol.
+ </simpara>
+ <simpara>
+ Once an attacker gains access to your database directly (bypassing the
+ webserver), the sensitive data stored in it may be exposed or misused,
+ unless the information is protected by the database itself. Encrypting
+ the data is a good way to mitigate this threat, but very few databases
+ offer this type of data encryption.
+ </simpara>
+ <simpara>
+ The easiest way to work around this problem is to first create your own
+ encryption package, and then use it from within your PHP scripts. PHP
+ can assist you in this case with its several extensions, such as <link
+ linkend="ref.mcrypt">Mcrypt</link> and <link
+ linkend="ref.mhash">Mhash</link>, covering a wide variety of encryption
+ algorithms. The script encrypts the data be stored first, and decrypts
+ it when retrieving. See the references for further examples how
+ encryption works.
+ </simpara>
+ <simpara>
+ In case of truly hidden data, if its raw representation is not needed
+ (i.e. not be displayed), hashing may be also taken into consideration.
+ The well-known example for the hashing is storing the MD5 hash of a
+ password in a database, instead of the password itself. See also
+ <function>crypt</function> and <function>md5</function>.
+ </simpara>
+ <example>
+ <title>Using hashed password field</title>
+ <programlisting role="php">
+<![CDATA[
+// storing password hash
+$query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');",
+ addslashes($username), md5($password));
+$result = pg_exec($connection, $query);
+
+// querying if user submitted the right password
+$query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';",
+ addslashes($username), md5($password));
+$result = pg_exec($connection, $query);
+
+if (pg_numrows($result) > 0) {
+ echo "Wellcome, $username!";
+}
+else {
+ echo "Authentication failed for $username.";
+}
+]]>
+ </programlisting>
+ </example>
+ </sect2>
+
+ <sect2 id="security.database.sql-injection">
+ <title>SQL Injection</title>
+ <simpara>
+ Many web applications are unaware of how SQL queries can be tampered with,
+ and assume that an SQL query is a trusted command. It means that SQL
+ queries are able to circumvent access controls, thereby bypassing standard
+ authentication and authorization checks, and sometimes SQL queries even
+ may allow access to host operating system level commands.
+ </simpara>
+ <simpara>
+ Direct SQL Command Injection is a technique where an attacker creates or
+ alters existing SQL commands to expose hidden data, or to override valuable
+ ones, or even to execute dangerous system level commands on the database
+ host.
+ </simpara>
+ <simpara>
+ This is accomplished by the application taking user input and combining
+ it with static parameters to build a SQL query. The following examples
+ are based on true stories, unfortunately.
+ </simpara>
+ <para>
+ Owing to the lack of input validation and connecting to the database on
+ behalf of a superuser or the owner who can create users, the attacker
+ may create a superuser in your database.
+ <example>
+ <title>Splitting the result set into pages - and making superusers</title>
+ <programlisting role="php">
+<![CDATA[
+$offset = argv[0]; // beware, no input validation!
+$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
+$result = pg_exec($conn, $query);
+]]>
+ </programlisting>
+ </example>
+ Normal users click on the 'next', 'prev' links where the offset is
+ appended to the URL. The script expects that the incoming
+ <varname>$offset</varname> is numeric. However, if someone tries to
+ break in with appending <function>urlencode</function>'d form of the
+ following to the URL:
+ <informalexample>
+ <programlisting>
+<![CDATA[
+0;
+insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
+ values ('crack', 31, 't','t','crack');
+--
+]]>
+ </programlisting>
+ </informalexample>
+ or
+ <informalexample>
+ <programlisting>
+<![CDATA[
+0;
+insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
+ select 'crack', usesysid, 't','t','crack'
+ from pg_shadow where usename='postgres';
+--
+]]>
+ </programlisting>
+ </informalexample>
+ then the script will present a superuser access to him. Note that
+ <literal>0;</literal> is to supply a valid offset to the original
+ query and to terminate it.
+ </para>
+ <note>
+ <para>
+ It is common technique to force the SQL parser to ignore the rest of the
+ query written by the developer with <literal>--</literal> which is the
+ comment sign in SQL.
+ </para>
+ </note>
+ <para>
+ A feasible way to gain passwords:
+ <example>
+ <title>Listing out articles - and some passwords</title>
+ <programlisting role="php">
+<![CDATA[
+$query = "SELECT id, name, inserted, size FROM products WHERE size = '$size';";
+]]>
+ </programlisting>
+ </example>
+ The static part of the query can be combined with another
+ <literal>SELECT</literal> statement which reveals all passwords:
+ <informalexample>
+ <programlisting>
+<![CDATA[
+union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable
+]]>
+ </programlisting>
+ </informalexample>
+ If this query were assigned to <varname>$size</varname> (prepended with
+ <literal>'</literal>), the query beast awakened.
+ </para>
+ <para>
+ SQL updates are also subject to attacking your database.
+ <example>
+ <title>From resetting a password to gaining more privileges</title>
+ <programlisting role="php">
+<![CDATA[
+$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
+]]>
+ </programlisting>
+ </example>
+ But a malicious user sumbits the value
+ <literal>' or uid like'%admin%'; --</literal> to <varname>$uid</varname>,
+ and the query will be twisted:
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+$query = "UPDATE usertable SET pwd='...' WHERE uid='' or like '%admin%'; --";
+]]>
+ </programlisting>
+ </informalexample>
+ Now, the admin user password has been changed. Alternatively, the attacker
+ simply sets <varname>$pwd</varname> to
+ <literal>"hehehe', admin='yes', trusted=100 </literal> (with a trailing
+ space) to gain more privileges:
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+$query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;"
+]]>
+ </programlisting>
+ </informalexample>
+ </para>
+ <para>
+ A frightening example how operating system level commands can be accessed
+ on some database hosts.
+ <example>
+ <title>Attacking the database host's operating system</title>
+ <programlisting role="php">
+<![CDATA[
+$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
+$result = mssql_query($query);
+]]>
+ </programlisting>
+ </example>
+ If attacker submits the value
+ <literal>%' exec master..xp_cmdshell 'net user test testpass /ADD' --</literal>
+ to <varname>$prod</varname>, then the <varname>$query</varname> will be:
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+$query = "SELECT * FROM products WHERE id LIKE '%%' exec master..xp_cmdshell 'net
+user test testpass /ADD'--";
+$result = mssql_query($query);
+]]>
+ </programlisting>
+ </informalexample>
+ MSSQL Server executes the SQL statements in the batch including a command
+ to add a new user to the local accounts database. If this application
+ were running as <literal>sa</literal> and the MSSQLSERVER service is
+ running with sufficient privileges, the attacker would now have an
+ account with which to access this machine.
+ </para>
+
+ <sect3 id="security.database.avoiding">
+ <title>Avoiding techniques</title>
+ <simpara>
+ You may plead that the attacker must possess a piece of information
+ about the database schema in most examples. You are right, but you
+ never know when and how it can be taken out, and if it happens,
+ your database may be exposed. If you are using an open source, or
+ publicly available database handling package, which may belong to a
+ content management system or forum, the intruders easily produce
+ a copy of a piece of your code. It may be also a security risk if it
+ is a poorly designed one.
+ </simpara>
+ <simpara>
+ These attacks are mainly based on exploiting the code not being written
+ with security in mind. Never trust on any kind of input, especially
+ which comes from the client side. The first example shows that such a
+ blameless query can cause disasters.
+ </simpara>
+
+ <itemizedlist>
+ <listitem>
+ <simpara>
+ First, check if the given input has the expected data type. PHP has
+ a wide range of input validating functions, from the simplest ones
+ found in <link linkend="ref.variables">Variable Functions</link> and
+ in <link linkend="ref.ctype">Character Type Functions</link>
+ sections, (e.g. <function>is_numeric</function>,
+ <function>ctype_digit</function> respectively) onwards the
+ <link linkend="ref.pcre">Perl compatible Regular Expressions</link>
+ support.
+ </simpara>
+ </listitem>
+ <listitem>
+ <para>
+ If the application waits for numeric input, consider to verify data
+ with <function>is_numeric</function>, or silently change its type
+ using <function>settype</function> or <function>sprintf()</function>.
+ <example>
+ <title>A more secure way to compose a query for paging</title>
+ <programlisting role="php">
+<![CDATA[
+settype($order, 'integer');
+$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
+
+// please note %d in the format string, using %s would be meaningless
+$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
+$offset);
+]]>
+ </programlisting>
+ </example>
+ </para>
+ </listitem>
+ <listitem>
+ <simpara>
+ Quote user input which is passed to the database with
+ <function>addslashes</function> or <function>addcslashes</function>.
+ See <link linkend="security.database.storage">this example</link>.
+ As the examples shows, quotes burnt into the static part of the query
+ is not enough, and can be easily hacked.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ Do not print out any database specific information, especially
+ about the schema, no matter what happens. See also <link
+ linkend="security.errors">Error Reporting</link> and <link
+ linkend="ref.errorfunc">Error Handling and Logging Functions</link>.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ You may use stored procedures and previously defined cursors to abstract
+ data access so that users do not directly access tables or views, but
+ this solution has another impacts.
+ </simpara>
+ </listitem>
+ </itemizedlist>
+
+ <simpara>
+ Besides these, you benefit from logging queries either within your script
+ or by the database itself, if it supports. Obviously, the logging is unable
+ to prevent any harmful attempt, but it can be helpful to trace back which
+ application has been circumvented. The log is not useful by itself, but
+ through the information it contains. The more detail is generally better.
+ </simpara>
+ </sect3>
+ </sect2>
</sect1>
<sect1 id="security.errors">