Thứ Hai, 16 tháng 6, 2014

Keeping Private Data Private with hook_query_alter() [Writing Secure Code]

If you have a varying  number of values  in your SQL that cannot be determined until  runtime, you should use the $query-> condition (field, array of values, ‘IN’) statement to restrict your query to a dynamic list of values as defined in the second
parameter. Anexample of using  this technique is as follows:

// $node_types  is an array  containing one  or  more node type  names
// such  as  article, page,  blog, etc.
$node_types  = array('article', 'page', 'blog');
// Prepare  and execute the  query  using  the  list of  node types
$query  = db_select('node', 'n');
$query->fields('n', array('title'));
$query->condition("n.type",  $node_types, 'IN');
$query->condition("n.status", 1);
$query->addTag('node_access');
$result = $query->execute();

Permissions and Page Callbacks

Another aspect to keep in mind when writing your own modules is the access arguments key of each menu item you define in the menu hook.  In the earlier example demonstrating insecure code, we used the following access arguments:
/*
*   Implements hook_menu().
*/
function  insecure_menu() {
$items['insecure'] = array( 'title' => 'Insecure  Module', 'description' => 'Example of  how not  to  do things.',
'page  callback' => 'insecure_code','access  arguments' => array('access content'),);
return  $items;
}
It’s important to question who is allowed to access this callback. The “access content” permission is a very general
permission. You probably want  to define your own permissions, using hook_permission(), and  use those to protect your
menu callbacks.Permissions are unique strings describing the permission being granted (see the section “Access Control” in
Chapter 4 for more details).
Because your implementation of the menu hook  is the gatekeeper that allows or denies a user the ability to reach the code  behind it (through the callback), it’s especially important to give some thought to the permissions you use here.

Cross-Site Request Forgeries (CSRF)

Suppose that you have logged into drupal.orand  are browsing the forums there. Then  you get off on tangent and  endup browsing at another web site. Someone evil at that web site has crafted an image tag like this:
When  your web browser loads  the image, it will request that path from drupal.org. Because you are currently logged  in to drupal.org, your browser will send your cookie  along  with the request. Here’s question to ponder: when drupal.org
receives therequest, will it consider you a logged-in user with all the access privileges you’ve been given? You bet it will!
The evil person’s image tag has essentially made your user click a link on drupal.org.
The first defense against this type of attack is to never use GET requests to actually change things on the server;  that way, any requests gened this way will be heratarmless. The Drupal form API follows the HTTP/1.1 convention that the GET
method should not take any action other than data retrieval. Drupal uses POST exclusively for actions that make changes
to the server  (see www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1).
Second, the form API uses tokens and  unique IDs to make sure  that submitted form values  from POST requests are
coming from a form that Drupal sent  out (for more on this, see Chapter 11). When  you are writing modules, be sure to
use the formAPI for your forms and  you will gain this protection automatically. Any action that your module takes as a
result of form input should happen in the submit function for the form. That way, you are assured that the form API has protected you. Finally, you can also protect GET requests if necessary by using a token (generated by drupal_get_token())
in the URL and  verifying the token with drupal_valid_token().

File Security

The dangers faced  by Drupal when handling files and  file paths are the same as with other web applications.

File Permissions


File permissions should be set in such a way that the user cannot manipulate (add, rename, or delete) files. The web server  should have read-only access to Drupal files and  directories. The exception is the file system paths. Clearly, the web server must have

access to those directories so it can write uploaded files.


Protected Files


The .htaccess file that ships with Drupal has the following lines:

#  Protect files and directories from prying  eyes.

<FilesMatch  "\.(engine|inc|info|install|make|module|profile|test|po|sh|É.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$"> Order allow,deny</FilesMatch>


The Order directive is set to allow,deny, but no Allow or Deny directives are included. This means that the implicit behavior is to deny.  In other words, reject all requests for the files shown in Table 21-2.


Table 21-2. Files Rejected by the FilesMatch Directive’s Regular Expression in Drupal’s .htaccess File
Files Matched
Description
Ends with .engine
Template engines
Ends with .inc
Library files
Ends with .info
Module and  theme .info files
Ends with .install
Module .install files
Ends with .module
Module files
Ends with .make
Make files
Ends with .profile
Installation profiles
Ends with .po
Portable object files (translations)
Ends with .sh
Shell scripts
Ends with .*sql
SQL files
Ends with .test
Test scripts
Ends with .theme
PHP themes
Ends with .tpl.php
PHP Template template files
Ends with .tpl.php4
PHP Template template files
Ends with .tpl.php5
PHP Template template files
Ends with .xtmpl
XTemplate files
Begins with Entries
CVS  file
Named Repository
CVS  file
Named Root
CVS  file
Named Tag
CVS  file
Named Template
CVS  file


File Uploads

If a module is enabled to allow file uploading, the files should be placed in a specific directory, and access should be enforced by the codeIf file uploads are enabled and  the private download directory is set at Configuration -> File system, the file system path on

that same screen must be set to no public access.


Filenames and  Paths

No filename or file path information from the user can be trusted! When you are writing a module and your code  expects to

receive somefile.txt, realize that it may get something else instead, like

../somefile.txt  // File  in  a  parent  directory.

../settings.php  // Targeted  file.

somefile.txt;  cp ../settings.php  ../settings.txt  // Trying  to  run a  shell  command.


The first two examples try to manipulate the file path by including the two dots that indicate a parent directory to the underlying

operating system. In the last example, the programmer attempts to execute a shell command and  has included a semicolon sothat

after the shell command runs, an additional command will run  that will make settings.php readable and  thus reveal the database

ually has write access to directories other than the file system path.username and  password. All of the preceding examples are

hoping that file permissions are set incorrectly, and  that the webserver  act

Whenever you are using file paths, a call to file_valid_uri() is in order, like this:


if (!file_valid_uri($uri) {

// Abort!  File URI  is not  what was expected!

}

The file_valid_uri() function will find out whether the URI has a valid scheme for file operations. In general, you probably dont  want  the Next Great File Management Module to be your first Drupal project. Instead, study existing file-related modules that

have been around for a while.


Encoding Mail Headers


When  writing any code  that takes  user input and  builds it into an e-mail message, consider the following two facts:

     E-mail headers are separated by line feeds (only line feeds that aren’t followed by a space or tab are treated as header

separators).

     Users can inject their  own headers in the body of the e-mail if you don chec that their input is free of line feeds.


For example, say you expect the user to enter a subject for his or her message, and  the user enters a string interspersed by

escaped line feed (%0A) and  space (%20) characters:

Have a  nice   day%0ABcc:spamtarget@example.com%0A%0AL0w%20c0st%20mortgage!


The result would be as follows:

Subject: Have a  nice   day Bcc:  spamtarget@example.com

L0w  c0st  mortgage!

...
For that reason, Drupal’s built-in mail function drupal_mail() in includes/mail.inc runs all headers through mime_
header_encode() to sanitize headers. Any nonprintable characters will be encoded into ASCII printable characters according
to RFC 2047, and  thus neutralized. This involves prefixing the character with =?UTF-8?B? and  then printing the Base64-encoded character plus ?=You’re encouraged to use drupal_mail(); if you choose not to, you’ll have to make the
mime_header_encode() calls yourself.

Files for Production Environments

Not all files included in the distribution of Drupal are necessary for production sites. For example, making the CHANGELOG.txt
file available on a production site means that anyone on the Web can see what version of Drupal you are running (of course, the
black hats  have other ways of detecting that you are running Drupal; see www.lullabot.com/articles/is-site-running-drupal).
Table 21-3 lists the files and/or directories that are necessary for Drupal to function after it has been installed; the others can be
removed from a production site (keep  a copy, though!). Alternatively, read  access can be denied to the web server.

Table 21-3. Files and Directories That Are Necessary for Drupal to Function
File/Directory
Purpose
.htaccess
Security, clean URL, and  caching support on Apache
cron.php
Allows regularly scheduled tasks to run
includes/
Function libraries
index.php
Main entry point for Drupal requests
misc/
JavaScript and  graphics
modules/
Core modules
robots.txt
Prevents well-behaved robots from hammering your site
sites/
Site-specific modules, themes, and  files
themes/
Core themes
xmlrpc.php
XML-RPC endpoint; necessary only if your site will receive incoming XML-RPC requests
authorize.php
Administrative script for running authorized file operations

SSL Support
By default, Drupal handles user logins in plain text over HTTP. However, Drupal will happily run  over HTTPS if your web server supports it. No modification to Drupal is required.

Stand-Alone  PHP

Occasionally, you might need to write a stand-alone .php file instead of incorporating the code  into a Drupal module.
When  you do, be sure to keep security implications in mind.

Suppose, when you were testing your web site, you wrote some quick and  dirty code  to insert users into the database so you
could test performance with many users. Perhaps you called  it testing.php and put it at the root of your Drupal site, next to
index.php. Then  you bookmarked it in your browser, and every time  you wanted a fresh user table, you selected the bookmark:

<?php
/**
*   This  script  generates users   for  testing  purposes.
*/
// These  lines are  all that   is needed  to  have  full
// access to  Drupal's   functionality.
include_once 'includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
db_delete('users')->condition('uid', '1', '>')->execute();
for  ($i = 2;  $i <= 5000;  $i++)  {
$name = $i;
$pass  = md5(user_password());
$mail  = $name .'@localhost';
$status = 1;
db_insert('users')->fields(array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'status'É
=> $status, 'created' => time(), 'access' => time())),->execute();}
print t('Users  have  been  created.');

That’s useful for testing, but imagine what  would happen if you forgot that the script was there and the script made it onto your
production site! Anyone  who found the URL to your script (http://example.com/testing.php) could delete your users with asingle
request. That’s why it’s important, even in quick one-off scripts, to include a security check, as follows:
<?php
/**
*   This  script  generates users   for  testing  purposes.
*/
// These  lines are  all that   is needed  to  have  full
// access to  Drupal's   functionality.
include_once 'includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// security check;  only  the  site administrator may  execute global $user;
if ($user->uid !=  1)  {
print t('Not authorized.'); exit();
}
db_delete('users')->condition('uid', '1', '>')->execute();
for  ($i = 2;  $i <= 10;  $i++)  {
$name = $i;
$pass  = md5(user_password());
$mail  = $name .'@localhost';
$status = 1;
db_insert('users')->fields(array('name' => $name, 'pass' => $pass, 'mail' => $mail, 'status'É
=> $status, 'created' => time(), 'access' => time())),->execute();
}
print t('Users  have  been  created.');
Here are two take-home lessons:
     Write security checking even into quickl written scripts, preferably working from a template that includes the necessary code.
     Remember that an important part  of deployment is to remove or disable testing code.
AJAX Security,  a.k.a. Request Replay Attack

The main thing to remember about security in connection with AJAX capabilities such as jQuery is that although you usually develop the server  side of the AJAX under the assumption that it will be called  from JavaScript, there’s nothing to prevent a malicious
user from making AJAX calls directly (e.g., from command-line tools like curl  or wget, or even just by typing the URL into
a web browser). Be sure to test your code  from both positions.
Form API Security
One of the benefits of using the form API is that much of the security is handled for you. For example, Drupal checks to make
sure that the value the user chose from a drop-down selection field was actually a choice that Drupal presented. The form API uses
set sequence of events, such as form building, validation, and execution. You should not use user  input before the validation phase because, well, it hasn’t been validated. For example, if youre using a value from $_POST, you have no guarantee that the user
hasn’tmanipulated that value. Also, use the #value element to pass information along  in the form instead of using hidden fields
whenever possible, as malicious users can manipulate hidden fields but have no access to #value elements.
Any user-submitted data that is used to build a form must be properly sanitized like any other user- submitted data, as in the
following example. Unsafe:
$form['foo'] = array( '#type' => 'textfield','#title' => $node->title, // XSS  vulnerability!
'#description' => 'Teaser is: '. $node->teaser, // XSS  vulnerability!
'#default_value' => check_plain($node->title), // Unnecessary.);
Safe:
$form['foo'] = array( '#type' => 'textfield','#title' => check_plain($node->title),
'#description' => t('Teaser is:  @teaser', array('@teaser' => $node->teaser)), '#default_value' => $node->title,);
It is not necessary to run  the default value through check_plain() because the theme function for the form element type (in this
case, theme_textfield() in includes/form.inc) does that.
See Chapter 11 for more about the form API.

Protecting the Superuser Account

The easiest way to obtain credentials for a Drupal web site is probably to call a naïve secretary somewhere and  say, “Hi, this is
Joe. <Insert small talk here.> I’m with the computer support team, and we’re having some problems with the web site. What is
theusername and  password you usually log in with?” Sadly, many people will simply  give out such information when asked. While
technology can help, user education is the best  defense against such attacks. This is why it is a good idea to never assign user 1
(the superuser) to anyone as a matter of course. Instead, each  person who will be maintaining a web site should be given only the permissions needed to perform the tasks for which he or she isauthorized. That way, if a security breach happens, damage may be contained.

Summary
After reading this chapter, you should know
     That you should never, ever trust input from the user.
     How you can transform user input to make it safe for display.
     How to avoid XSS attacks.
     How to avoid SQL injection attacks.
     How to write code  that respects node access modules.
     How to avoid CSRF attacks.
     How Drupal protects uploaded files.
     How to avoid e-mail header injections.

Không có nhận xét nào:

Đăng nhận xét