Apache rewrite XSS protection issues - apache

We are developing a website for a trucking company and it recently been subjected to penetration testing. One of the attacks done was injecting a XSS script into the request url:
ourcompanyhostname.com/abc/authorize<script>alert('xss');</script>
Since our web server is Apache, we have fixed the issue by setting up the ff. in the httpd.conf file. basically, rather reflecting the script in the 404 response erorr, a generic 400 response is thrown instead.
RewriteRule ^/abc/authorize/.*[^A-Za-z0-9./\-_]+ "-" [L,R=400]
The issue is when the attack was changed to the one below, it no longer can be caught:
ourcompanyhostname.com/abc/authorize%3c%3cSCRIPT%3ealert(%22XSS%22)%3b%2f%2f%3c%3c%2fSCRIPT%3e
Response still was 404 instead of 400.
Is there another way to achieve what we want? We already have tried doing the one below but it still won't work. We just want it to return an http 400 error when a XSS attack is done.
RewriteCond %{REQUEST_URI} ^.*(\*|;|<|>|\)|%0A|%0D|%3C|%3E|%00).* [NC]
RewriteCond %{REQUEST_URI} abc
RewriteRule ^(.*)$ "-" [L,R=400]

I don't think the encoding matters, mod_rewrite sees the path in the URL after decoding.
I think you may have missed that your original rule requires matching a trailing slash after "authorize" and the new malicious request doesn't have it.
Your final rule works fine for me, if you get an unexpected result for a particular URL you have to study the rewritelog/logelvel trace8 output.
If the 404 is generated by Apache, just use a custom ErrorDocument for 404.
If it is generated by your EE server, do the same in your web.xml.

Related

POST information getting lost in .htaccess redirect

So, I have a fully working CRUD. The problem is, because of my file structure, my URLs were looking something like https://localhost/myapp/resources/views/add-product.php but that looked too ugly, so after research and another post here, I was able to use a .htaccess file to make the links look like https://localhost/myapp/add-product (removing .php extension and the directories), and I'm also using it to enforce HTTPS. Now, most of the views are working fine, but my Mass Delete view uses POST information from a form on my index. After restructuring the code now that the redirect works, the Mass Delete view is receiving an empty array. If I remove the redirect and use the "ugly URLs" it works fine. Here's how my .htaccess file is looking like:
Options +FollowSymLinks +MultiViews
RewriteEngine On
RewriteBase /myapp/
RewriteRule ^resources/views/(.+)\.php$ $1 [L,NC,R=301]
RewriteCond %{DOCUMENT_ROOT}/myapp/resources/views/$1.php -f
RewriteRule ^(.+?)/?$ resources/views/$1.php [END]
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
I didn't actually write any of it, it's a mesh between answered questions and research. I did try to change the L flag to a P according to this post: Is it possible to redirect post data?, but that gave me the following error:
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator at admin#example.com to inform them of the time this error occurred, and the actions you performed just before this error.
More information about this error may be available in the server error log.
Apache/2.4.52 (Win64) OpenSSL/1.1.1m PHP/8.1.2 Server at localhost Port 443
POST information getting lost in .htaccess redirect
You shouldn't be redirecting the form submission in the first place. Ideally, you should be linking directly to the "pretty" URL in your form action. If you are unable to change the form action in the HTML then include an exception in your .htaccess redirect to exclude this particular URL from being redirected.
Redirecting the form submission is not really helping anyone here. Users and search engines can still see the "ugly" URL (it's in the HTML source) and you are doubling the form submission that hits your server (and doubling the user's bandwidth).
"Redirects" like this are only for when search engines have already indexed the "ugly" URL and/or is linked to by external third parties that you have no control over. This is in order to preserve SEO, just like when you change any URL structure. All internal "ugly" URLs should have already been converted to the "pretty" version. The "ugly" URLs are then never exposed to users or search engines.
So, using a 307 (temporary) or 308 (permanent) status code to get the browser to preserve the request method across the redirect should not be necessary in the first place. For redirects like this it is common to see an exception for POST requests (because the form submission shouldn't be redirected). Or only target GET requests. For example:
RewriteCond %{REQUEST_METHOD} GET
:
Changing this redirect to a 307/8 is a workaround, not a solution. And if this redirect is for SEO (as it only should be) then this should be a 308 (permanent), not a 307 (temporary).
Aside:
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Your HTTP to HTTPS redirect is in the wrong place. This needs to go as the first rule, or make sure you are redirecting to HTTPS in the current first rule and include this as the second rule, before the rewrite (to ensure you never get a double redirect).
By placing this rule last then any HTTP requests to /resources/views/<something>.php (or /<something>) will not be upgraded to HTTPS.

How to redirect 404 errors (and 403) to index.html with a 200 response

I am building a static website that uses JS to parse a URL in order to work out what to display.
I need every URL to actually open index.html where the JS can pull apart the path and act accordingly.
For example http://my.site/action/params will be parsed as an action with some parameters params.
Background, this will be served from AWS S3 via CloudFront using custom error redirection - and this works fine on AWS.
I am, however, trying to build a dev environment under Ubuntu running apache and want to emulate the redirection locally.
I have found a couple of pages that come close, but not quite.
This page shows how to do the redirect to a custom error page on the server housed in a file called "404". As 404 is the actual error response code, the example looks a bit confusing and I am having trouble modifying the example to point to index.html.
The example in the accepted answer suggests:
Redirect 200 /404
ErrorDocument 404 /404
which I have modified to:
Redirect 200 /index.html
ErrorDocument 404 /index.html
However this returns a standard 404 Not Found error page.
If I remove the Redirect line, leaving just the ErrorDocument line, I get the index.html page returned as required, but the https status response is still a 404 code where I need it to be a 200.
If I leave the Redirect line as per the example I actually get the same result as my modified version, so I suspect this is the line that is incorrect, but I can't figure it out.
(I'm using the Chrome Dev Tools console to see the status codes etc).
I think I have found a solution using Rewrite rules instead of error docs.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
The key I was missing in this approach seems to be not including an R=??? status response code at the end of the rewrite rule. It took me a while to find that!
As it uses mod_rewrite rather than defining error pages I assume that the mechanism is different to how CloudFront does it, but for my dev system needs it seems that the result is the same - which means I can work on the site without having to invalidate the CloudFront cache after every code change and upload.

HTTPS redirect fails with .htaccess rewrite for certain URL length

I have an .htaccess file for showing a default image if the requested URL does not exist. I simplified it to this:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . default.png [L]
Using HTTPS, this suddenly stopped working if the URL exceeds a certain length (connection closed).
HTTP always works.
It used to work like this for years and it still does on other servers.
It also seems that the kind of characters matter:
not working:
https://server.abc/images/01234567890123456789012345678901234567890123456789abc.png
https://server.abc/images/012345678901234567890123456789012345678901234567890123456789.png
working:
https://server.abc/images/01234567890123456789012345678901234567890123456789.png
https://server.abc/images/01234567890123456789012345678901234567890123456789123.png
https://server.abc/images/0123456789012345678901234567890123456789012345678912345.png
The redirect works if the condition is removed (second line), so it seems like it has something to do with REQUEST_FILENAME, HTTPS and the byte size (encoding?) of the filename/URL string.
This occurs with Apache/2.4.46 and macOS/10.15.7. It might have started after one of the latest security updates.
Any idea where this is coming from or what kind of configuration could cause this?
Thanks for your help!
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . default.png [L]
It's not clear why this would "fail" for only certain requests over HTTPS only. A "security" update (particularly if it involves mod_security) is a likely cause - although an unusual one.
However, you shouldn't really be doing it this way to begin with. This will result in a request for any non-existent URL being served /default.png with a "200 OK" response and potentially risk being indexed by search engines and abused by a malicious user.
What you are doing here is essentially setting a custom 404 response to an image, which you could do with the following instead and which will also return the "correct" 404 status.
ErrorDocument 404 /default.png
Now, any request that does not map to file (or directory) will be served the image /default.png but with a 404 "Not Found" HTTP response code, so search engines/bots get the "correct" response.
This also naturally gets around the REQUEST_FILENAME issue, assuming these "not working" URLs do ultimately result in a 404 and not some other response (due to the "security" update).

Rewriting a URL causing a 400 Bad Request

I'm trying to get around an HTTP 400 Bad Request with Apache that is caused when this URL is accessed (actual domain redacted):
http://example.com/nw/f/RUD/E.enc<space>
The URL ends with an actual space character, thus giving an HTTP 400 Bad Request. I cannot get the clients requesting this to remove the space in the URL, so I need to rewrite the URL without a space.
I've tried some RewriteRules, like this one (after enabling the RewriteEngine):
RewriteRule "^/nw/f/RUD/E(.*)$" /nw/f/RUD/E.enc [P]
The RewriteRule has no effect and is still giving an HTTP 400.
It even happens when I escape the space character instead of using the wildcard in the rule. Same thing if I try to replace the HTTP 400 error page to lead to the actual content (which wouldn't be ideal, since there's 4 different files).
How can I correctly rewrite the URL, removing the space on it, without getting HTTP 400?
Your RewriteRule produces a redirect loop as .../E.enc<space> and .../E.enc both match .../E(.*)$.
Better use a [R]edirect, though proxying also works.
RewriteEngine On
RewriteRule "^/nw/f/RUD/E\.enc $" /nw/f/RUD/E.enc [R,L]
or shorter
RewriteEngine On
RewriteRule "^(/nw/f/RUD/E\.enc) $" $1 [R,L]

.htaccess redirects if the condition doe not match/ negative condition

I am modifying the .htaccess file of a legacy PHP web application. I am not familiar with apache .htaccess syntax. I found this tutorial. What I am trying to do is that I am trying to redirect all the requests to a URL/ path if the request URL is not a specific URL/ path. For example, all the requests to the website will be redirected to localhost/my-custom-page unless the request URL is localhost/my-custom-page.
I know how to redirect mapping 1 to 1 as follows:
RewriteEngine on
RewriteRule ^my-old-url.html$ /my-new-url.html [R=301,L]
But, what I am trying to do is that redirecting all the requests to the specific page unless the request is to that page. Even the home page will be redirected to that page. How can I do that?
When I tried the following solution
RewriteEngine on
RewriteCond %{REQUEST_URI} !/my-new-url\.html
RewriteRule ^ /my-new-url.html [R=301]
I get the error
I want to check using OR condition as well. For example, if the path is not path-one or path-two, redirect all the requests to path-one.
Your question is a bit vague, due to your wording. But I assume this is what you are actually looking for:
RewriteEngine on
RewriteCond %{REQUEST_URI} !/my-new-url\.html
RewriteRule ^ /my-new-url.html [R=301]
In case you receive an internal server error (http status 500) using the rule above then chances are that you operate a very old version of the apache http server. You will see a definite hint to an unsupported [END] flag in your http servers error log file in that case. You can either try to upgrade or use the older [L] flag, it probably will work the same in this situation, though that depends a bit on your setup.
It is a good idea to start out with a 302 temporary redirection and only change that to a 301 permanent redirection later, once you are certain everything is correctly set up. That prevents caching issues while trying things out...
This rule will work likewise in the http servers host configuration or inside a dynamic configuration file (".htaccess" file). Obviously the rewriting module needs to be loaded inside the http server and enabled in the http host. In case you use a dynamic configuration file you need to take care that it's interpretation is enabled at all in the host configuration and that it is located in the host's DOCUMENT_ROOT folder.
And a general remark: you should always prefer to place such rules in the http servers host configuration instead of using dynamic configuration files (".htaccess"). Those dynamic configuration files add complexity, are often a cause of unexpected behavior, hard to debug and they really slow down the http server. They are only provided as a last option for situations where you do not have access to the real http servers host configuration (read: really cheap service providers) or for applications insisting on writing their own rules (which is an obvious security nightmare).
RewriteCond %{REQUEST_URI} !/my-new-url\.html
RewriteRule ^ /my-new-url.html [R=301]
There are a few potential issues with this, particularly since you hint in a comment that you are perhaps using a front-controller to "route" the URL.
This redirect satisfies the conditions outlined in the question, but does assume that you have no other rewrites, have an essentially "static site" and are not linking to any static resources.
You are missing an L (last) flag, so processing will continue through the file and possibly be rewritten if you have later rewrites.
If you are rewriting the URL to a front-controller in order to route the URL (as you suggest in comments) then this redirect will break, as it will redirect away from the front-controller. You need to only redirect direct requests, ie. when the REDIRECT_STATUS environment variable is empty.
If you are linking to any static resources in the same file space then these will also be redirected. You need to create an exception for any static resources you are using, either by file extension (eg. (css|js|jpg|png)) or by location (eg. /static).
So, try the following instead:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} !\.(js|css|jpg|png)$
RewriteRule !^my-custom-url$ /my-custom-url [R=302,L]
You don't need a separate condition to implement the exception for the URL you are redirecting to. It is more efficient to do this directly in the RewriteRule pattern.
The first condition ensures we are only redirecting direct requests and not rewritten requests to your front-controller.
The second condition avoids any static resources also being redirected. You could alternatively check the filesystem path if all your resources are stored under a common root. Or, as a last resort, implement filesystem checks (ie. RewriteCond %{REQUEST_FILENAME} !-f) if your static resources are too varied - but note that this is less efficient.
You will need to clear your browser cache before testing, since any earlier (erroneous) 301s are cached persistently by the browser.