diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc
index b7af4d0..16bcd68 100644
--- a/modules/search/search.extender.inc
+++ b/modules/search/search.extender.inc
@@ -123,6 +123,14 @@ class SearchQuery extends SelectQueryExtender {
   protected $multiply = array();
 
   /**
+   * Indicates whether the number of AND/OR combinations exceeded the configured
+   * limit. Expressions beyond the limit are ignored.
+   *
+   * @var boolean
+   */
+  protected $expressionsIgnored = FALSE;
+
+  /**
    * Sets up the search query expression.
    *
    * @param $query
@@ -183,7 +191,17 @@ class SearchQuery extends SelectQueryExtender {
     // Classify tokens.
     $or = FALSE;
     $warning = '';
+    $limit_combinations = variable_get('search_and_or_limit', 7);
+    // The first search expression shall not count as AND.
+    $and_count = -1;
+    $or_count = 0;
     foreach ($keywords as $match) {
+      if ($or_count && $and_count + $or_count >= $limit_combinations) {
+        // Ignore all further search expressions to prevent DoS attacks using a
+        // high number of AND/OR combinations.
+        $this->expressionsIgnored = TRUE;
+        break;
+      }
       $phrase = FALSE;
       // Strip off phrase quotes.
       if ($match[2]{0} == '"') {
@@ -212,6 +230,7 @@ class SearchQuery extends SelectQueryExtender {
         }
         $this->keys['positive'][] = $last;
         $or = TRUE;
+        $or_count++;
         continue;
       }
       // AND operator: implied, so just ignore it.
@@ -231,6 +250,7 @@ class SearchQuery extends SelectQueryExtender {
         }
         else {
           $this->keys['positive'] = array_merge($this->keys['positive'], $words);
+          $and_count++;
         }
       }
       $or = FALSE;
@@ -323,6 +343,9 @@ class SearchQuery extends SelectQueryExtender {
       form_set_error('keys', format_plural(variable_get('minimum_word_size', 3), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.'));
       return FALSE;
     }
+    if ($this->expressionsIgnored) {
+      drupal_set_message(t('Your search exceeded the maximum allowed amount of @count AND/OR combined expressions. Terms beyond that limit have been ignored.', array('@count' => variable_get('search_and_or_limit', 7))), 'warning');
+    }
     $this->executedFirstPass = TRUE;
 
     if (!empty($this->words)) {
diff --git a/modules/search/search.test b/modules/search/search.test
index a6ed0df..e0fa720 100644
--- a/modules/search/search.test
+++ b/modules/search/search.test
@@ -290,6 +290,20 @@ class SearchPageText extends DrupalWebTestCase {
     $this->drupalGet('search/node/' . $arg);
     $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']");
     $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.');
+
+    // Test a search input exceeding the limit of AND/OR combinations to test
+    // the DoS protection.
+    $limit = variable_get('search_and_or_limit', 7);
+    $keys = array();
+    for ($i = 0; $i < $limit + 1; $i++) {
+      $keys[] = $this->randomName(3);
+      if ($i % 2 == 0) {
+        $keys[] = 'OR';
+      }
+    }
+    $edit['keys'] = implode(' ', $keys);
+    $this->drupalPost('search/node', $edit, t('Search'));
+    $this->assertRaw(t('Your search exceeded the maximum allowed amount of @count AND/OR combined expressions. Terms beyond that limit have been ignored.', array('@count' => $limit)));
   }
 }
 
@@ -450,7 +464,7 @@ class SearchRankingTestCase extends DrupalWebTestCase {
   function testHTMLRankings() {
     // Login with sufficient privileges.
     $this->drupalLogin($this->drupalCreateUser(array('create page content')));
-    
+
     // Test HTML tags with different weights.
     $sorted_tags = array('h1', 'h2', 'h3', 'h4', 'a', 'h5', 'h6', 'notag');
     $shuffled_tags = $sorted_tags;
@@ -482,7 +496,7 @@ class SearchRankingTestCase extends DrupalWebTestCase {
 
     // Refresh variables after the treatment.
     $this->refreshVariables();
-    
+
     // Disable all other rankings.
     $node_ranks = array('sticky', 'promote', 'recent', 'comments', 'views');
     foreach ($node_ranks as $node_rank) {
@@ -520,7 +534,7 @@ class SearchRankingTestCase extends DrupalWebTestCase {
 
       // Assert the results.
       $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search tag ranking for "&lt;' . $tag . '&gt;" order.');
-      
+
       // Delete node so it doesn't show up in subsequent search results.
       node_delete($node->nid);
     }
@@ -861,7 +875,7 @@ class SearchCommentTestCase extends DrupalWebTestCase {
     $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, TRUE);
     $this->setRolePermissions($this->admin_role, TRUE, FALSE);
     $this->checkCommentAccess('Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search', TRUE);
-    
+
   }
 
   /**
