Need to create a rewrite rule that will check certain characters in a query string variable and redirect it to another page.

Example: http://www.example.com/index.html?test=123$4/56

I should be able to capture this request and redirect to 403 page.

RewriteCond %{QUERY_STRING} ^.*test=($/).*$ [NC]
RewriteRule .* - [F,L]

The problem would seem to be with your regex, rather than the mod_rewrite directives specifically.

The $ is a special character (an "anchor") in the regex, signifying the end of the string, so it must be backslash-escaped in order to match a literal $.

So, to match that specific query string, you would need a condition like the following:

RewriteCond %{QUERY_STRING} ^test=123\$4/56$
RewriteRule .* - [F]

The L flag is superfluous when used with F.

Block any value that contains $ or /

However, it looks like you are trying to match any value that contains $ or / and the test URL parameter can occur anywhere in the query string, in which case you need a character class ([...]) rather than a parenthesised subpattern ((...)). For example:

RewriteCond %{QUERY_STRING} (?:^|&)test=[^&]*[$/][^&]*

Contrary to what I said above about backslash-escaping the $ - when used inside a character class, it does not need to be escaped, as it carries no special meaning here.

The (?:^|&) prefix ensures we only match this specific URL parameter name and not simply one that ends with test. For example, it won't match mytest=123$4/56. The ?: makes this a non-capturing group (no backreferences are generated).

The trailing [^&]* (as opposed to a far more encompassing .*) ensures we don't creep into the next URL parameter (if any). For example, it won't match test=123456&foo=123$4/56.

Add the NC flag only if you specifically need this to be case-insensitive.

Allow positive integers only

You could also approach this the other way and only allow specific characters, rather than blocking those that are not permitted (which could be far greater). It is often better to be as specific as possible with regex. For example, if the text value can only contain digits (as in your example) then check for digits-only instead:

# Reject any request that contains a non-digit in the param value (inc. an empty value)
RewriteCond %{QUERY_STRING} (?:^|&)test=[^&]*[^\d&]
RewriteRule ^ - [F]

[^\d&] is a negated character class that does not match digits and & (URL param delimiter). So, if this matches then we know a non-digit has matched.

This basically checks for a single non-digit (and not &) and matches as soon as it's found, triggering the 403. This doesn't check that the whole value is made up of digits, it simply checks for a single invalid character.

UPDATE: Allow positive or negative integers only

To specifically allow positive or "negative" integers (as mentioned in comments) I would adopt a different approach to the above. Since to allow negative integers we need to permit - (hyphen), but only as the first character, not anywhere. Try the following instead:

RewriteCond %{QUERY_STRING} (?:^|&)test=(?!-?\d+(&|$))
RewriteRule ^ - [F]

This uses a negative lookahead ((?!-?\d+(&|$))) to assert that the value assigned to the test URL param does not match a positive or negative integer. The integer must have at least 1 digit and can be prefixed with an optional hyphen. The negative lookahead is successful when the contained pattern does not match.

(Leaving this here for reference...)

Just another way of doing a similar thing to the above, but using a CondPattern backreference (%1) and negated condition:

# When the test param is present and does not contain only digits (and at least 1)
RewriteCond %{QUERY_STRING} (?:^|&)test=([^&]*)
RewriteCond %1 !^\d+$
RewriteRule ^ - [F]

This will result in a 403 if the test URL param is present in the URL (1st condition), but its value is not 1 or more digits (2nd condition). The %1 is a backreference to the captured test value in the previous CondPattern. The ! prefix on the CondPattern negates the condition (ie. not all digits). \d is the shorthand character class for digits 0-9.

You could replace the 2nd condition to match \D (non-digits), instead of a negated CondPattern on digits, and alternation to match an empty parameter value:

RewriteCond %1 (\D|^$)
  • Updated the 2nd example as the regex was "too greedy" if there were additional URL parameters. Also added a better solution for blocking any non-digits.
    – MrWhite
    Oct 15 '18 at 23:38
  • I realized that I posted a wrong sample but the good thing about it is you elaborated your reply perfectly. Thank you so much for the reply. I tested it and seems working. I will tag this as the answer. Thank you.
    – Alabios
    Oct 16 '18 at 16:00
  • Thanks. I realized that we can further improve this by adding \- to accept negative integer RewriteCond %{QUERY_STRING} (?:^|&)test=[^&]*[^\-{1}\d&] [NC]
    – Alabios
    Oct 17 '18 at 9:56
  • That regex isn't correct - it matches too much. It allows any number of - anywhere in the value, eg. test=12-34-56 - this obviously isn't a valid negative integer. The characters {, 1 and } are also matched literally (when used inside a character class) - although the 1 is already part of the \d shorthand character class - so that regex would also permit something wholly invalid like test={123}-{456}. I've updated my answer with a solution that validates positive and negative integers specifically.
    – MrWhite
    Oct 20 '18 at 22:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.