Приглашаем посетить
Культурология (cult-lib.ru)

Section 7.1.  Brute Force Attacks

Previous
Table of Contents
Next

7.1. Brute Force Attacks

A brute force attack is an attack in which all available options are exhausted with no intelligence regarding which options are more likely. This is more formally known as an enumeration attackthe attack enumerates through all possibilities.

In terms of access control, brute force attacks typically involve an attacker trying to log in with a very large number of attempts. In most cases, known valid usernames are used, and the password is the only thing being guessed.

Section 7.1.  Brute Force Attacks

Although not technically a brute force attack, dictionary attacks are very similar. The biggest difference is that more intelligence is used to make each guess. A dictionary attack enumerates through a list of likely possibilities, rather than enumerating through a list of all possibilities.


Throttling authentication attempts or otherwise limiting the number of failures allowed is a fairly effective safeguard, but the dilemma is to be able to identify and stymie an attacker without adversely affecting your legitimate users.

In these situations, recognizing consistency can help you to distinguish between a particular attacker and everyone else. The idea is very similar to the Defense in Depth approach described in Chapter 4 to help protect against session hijacking, but you're trying to identify an attacker instead of a legitimate user.

Consider the following HTML form:

    <form action="http://example.org/login.php" method="POST">
    <p>Username: <input type="text" name="username" /></p>
    <p>Password: <input type="password" name="password" /></p>
    <p><input type="submit" /></p>
    </form>

An attacker can observe this form and create a script that tries to authenticate by sending the expected POST request to http://example.org/login.php:

    <?php

    $username = 'victim';
    $password = 'guess';

    $content = "username=$username&password=$password";
    $content_length = strlen($content);

    $http_request = '';
    $http_response = '';

    $http_request .= "POST /login.php HTTP/1.1\r\n";
    $http_request .= "Host: example.org\r\n";
    $http_request .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $http_request .= "Content-Length: $content_length\r\n";
    $http_request .= "Connection: close\r\n";
    $http_request .= "\r\n";
    $http_request .= $content;

    if ($handle = fsockopen('example.org', 80))
    {
      fputs($handle, $http_request);

      while (!feof($handle))
      {
        $http_response .= fgets($handle, 1024);
      }

      fclose($handle);

      /* Check Response */
    }
    else
    {
      /* Error */
    }

    ?>

With such a script, an attacker can add a simple loop to continue trying different passwords, and $http_response can be checked after each attempt. When a change in $http_response is observed, the authentication credentials are expected to be valid.

You can implement a number of safeguards to help protect against these types of attacks. It is worth noting that the HTTP requests used in a brute force attack are often identical in every way with one exceptionthe password.

Although a useful defense is to temporarily suspend an account once a maximum number of login failures are recorded, you might consider suspending an account according to certain aspects of the request, so that an attacker is less likely to interfere with a legitimate user's use of your application.

A few other approaches can also be used to make brute force attacks more difficult and less likely to succeed. A simple throttling mechanism can help to eliminate the practicality of such an attack:

    <?php

    /* mysql_connect() */
    /* mysql_select_db() */

    $clean = array();
    $mysql = array();

    $now = time();
    $max = $now - 15;

    $salt = 'SHIFLETT';

    if (ctype_alnum($_POST['username']))
    {
      $clean['username'] = $_POST['username'];
    }
    else
    {
      /* ... */
    }

    $clean['password'] = md5($salt . md5($_POST['password'] . $salt));
    $mysql['username'] = mysql_real_escape_string($clean['username']);

    $sql = "SELECT last_failure, password
            FROM   users
            WHERE  username = '{$mysql['username']}'";

    if ($result = mysql_query($sql))
    {
      if (mysql_num_rows($result))
      {
        $record = mysql_fetch_assoc($result);

        if ($record['last_failure']> $max)
        {
          /* Less than 15 seconds since last failure */
        }
        elseif ($record['password'] == $clean['password'])
        {
          /* Successful Login */
        }
        else
        {
          /* Failed Login */

          $sql = "UPDATE users
                  SET    last_failure = '$now'
                  WHERE  username = '{$mysql['username']}'";

          mysql_query($sql);
        }
      }
      else
      {
        /* Invalid Username */
      }
    }
    else
    {
      /* Error */
    }

    ?>

This throttles the rate with which a user is allowed to try again after a login failure. If a new attempt is made within 15 seconds of a previous failure, authentication fails regardless of whether the login credentials are correct. This is a key point in the implementation. It is not enough to simply deny access when a new attempt is made within 15 seconds of the previous failurethe output in such cases must be consistent regardless of whether the login would otherwise be successful; otherwise, an attacker can simply check for inconsistent output in order to determine whether the login credentials are correct.


Previous
Table of Contents
Next