configure error page to show the log of modsecurity - apache

I looking for a way to make the msg information of the rule (which rule had been triggered) to appears in the error and/or audit log files and sent back to the client in response headers.
I understand that there is phase "msg" but it doesn't sent back to the client in response headers the information so it's doesn't help me.
I want to see the information of the log in the error page in html, what can I do?
thank you for help,
Vladi.

It's a bad idea to let the client know what exactly went wrong. A hacker could use that to work around your security framework. A much better approach is a combination of mod_unique_id and customized error pages. Steps to follow:
enable mod_unique_id with your apache configuration
create customized error pages for the http return codes you're interested in (example below)
enable those in your apache config (ErrorDocument 403 /<url_path_to>/403.php for this example)
Here's an example for a 403 error page, let's call it 403.php (and no, a pure static page won't work):
<?php
$protocol = $_SERVER['SERVER_PROTOCOL'];
header("$protocol 403 Forbidden");
header("Status: 403 Forbidden");
header("Connection: close");
$msg = $_SERVER["UNIQUE_ID"];
?>
<HTML><HEAD>
<TITLE>You have no access to this resource (403)</TITLE>
</HEAD><BODY>
<P>An error occured. Please tell the admin the error code: <?php echo $msg; ?></P>
</BODY></HTML>
That's just a very abbreviated variant with no styling etc (you might want to enhance this), but I incidentally kept it simple for understanding. The $msg will print a unique code. The client can tell you this code, and you can use it to look up the exact line in your error log, where you will see which rule triggered it etc.

If you don't want to use external stuff (mod_perl, mod_php, etc) because, for example, you are on a front end reverse proxy and you don't want to expose a larger attack surface, you can use SSI (Server Side Include), since apache supports SSI internally with mod_include.
Just load mod_include, then add this to your virtualhosts:
# Custom 403 HTML error with base64 encoded date + uniqueid on response
<Directory /var/www/html/common/_Errors>
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml
Options +Includes
</Directory>
ErrorDocument 403 /common/_Errors/403.shtml
<Location "/common/_Errors/403.shtml">
# don't block redirected error page due rule to correlation
SecRuleRemoveById 980130
</Location>
Then, create a HTML file /var/www/html/common/_Errors/403.shtml containing something like this:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>Your request was denied.</p>
<!--#config timefmt="%Y-%m-%d %H:%M:%S" -->
<p><pre><!--#set var="ERR" value="${DATE_LOCAL} - ${UNIQUE_ID}" --></pre></p>
<p><pre><!--#echo encoding="base64" var="ERR" --></pre></p>
</body></html>
If you want you can change #config timefmt to fit your date time format.
SSI on mod_include will create a HTML response expanding the ERR variable with DATE_LOCAL and UNIQUE_ID and will encode the output as a base64 string. Just enough for me to get the uniqueid for the rule that was fired and its date.

Related

How to forward user back in case not pass the auth

I need to set auth on few pages and forward user back in case not authorized. For now, it display destination page with error.
Unauthorized
This server could not verify that you are authorized to access the
document requested. Either you supplied the wrong credentials (e.g.,
bad password), or your browser doesn't understand how to supply the
credentials required.
I have 0 knowledges in basic auth and apache conf. I have google deep and didn't find any solution, please advice.
Thank you
SetEnvIf Request_URI ^/en auth=1
AuthName "Please login to access english part"
AuthType Basic
AuthUserFile "/path/to/my/.htpasswd"
# first, allow everybody
Order Allow,Deny
Satisfy any
Allow from all
Require valid-user
# then, deny only if required
Deny from env=auth
This is really a limitation of HTTP Basic Authentication.
However, you could customise the 401 response (which I assume is what you are seeing, as opposed to a 403) the server would otherwise send back. You could directly redirect from the custom 401, however, that would result in the client receiving a 3xx response, rather than a 401, which is not as informative and confusing for users. Or, you present a "friendly" message, with a link back to where they came from.
The additional problem is knowing which page to send the user back to. Unless you are storing this information in the session, then you'll need to examine the Referer HTTP request header, which might not be set at all.
For example... at the top of your .htaccess file, define your custom error document for the 401 response:
ErrorDocument 401 /errordocs/e401.php
In /errordocs/e401.php, you would have something like:
<?php
/**
* 401 Unauthorized - Error Document
*/
// Get the HTTP Referer (if set at all)
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
// Immediately redirect back to the referring page if known
// But the client then sees a 3xx response, without any error, which could be confusing for users
if (!empty($referer)) {
header('Location: '.$referer,true,302);
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>401 Unauthorized</title>
</head>
<body>
<h1>401 Unauthorized</h1>
<p>Sorry, you do not have permission to view that resource.</p>
<p>
<?php if (empty($referer)): ?>
Go back to the home page
<?php else: ?>
Go back to <?=$referer?>
<?php endif; ?>
</p>
</body>
</html>
To "automate" this, you could perhaps implement a meta refresh (or JS "redirect") back to the referring page (if set) after so many seconds.

Garbled text when including remote .shtml file using mod_include and mod_proxy

I'm experimenting with apache mod_include.
I got two servers running apache: I'm trying to include in my test_local.shtml (server1) some simple text from test_remote.shml (server2).
test_local.shtml:
<html>
<head>
<title></title>
</head>
<body>
<!--#include virtual="http://www.server2.com/test_remote.shtml"-->
</body>
</html>
test_remote.shtml:
<b>this is a test</b>
At first it didn't work (got "File does not exist" error in error_log).
It looks like that for security reasons the only files I manage to include are on my local server (server1), with a local path, but not a remote url.
Then I understood that I needed to use mod_proxy (and mod_proxy_html) in combination with mod_include to make remote inclusion work.
So I added the following to my httpd.conf (on server1):
ProxyPass /server2 http://www.server2.com
Then I changed the include line in test_local.shtml to:
<!--#include virtual="/server2/test_remote.shtml"-->
No errors this time, something gets included, but the resulting text is all garbled:
‹³I²+ÉÈ,V¢D…’Ôâý$;.j¿è
Am I missing something in my configuration? What's wrong?
UPDATE: I suspect it's something about the way data is sent (and then read) between the two servers.. such as compression or similar. I checked mod_deflate configuration section, which is included and working in both servers, and it's the same. Any idea? Thanks
UPDATE 2: disabling SetOutputFilter DEFLATE on server2, the text included with mod_include on server1 is perfectly readable. So that's the source of the issue: how can I configure server1 to handle the gzipped content and display it correctly? (Hypotetically I'd imagine some sort of inputfilter opposed to outputfilter..)
I found two solutions, but I prefer the second one because it doesn't need to change the configuration of the remote server.
Solution 1:
By adding the following to the remote server configuration, we disable the gzip compression for .shtml files:
<IfModule mod_deflate.c>
SetEnvIfNoCase Request_URI \.shtml$ no-gzip dont-vary
</IfModule>
This is not the best solution for me, because I don't have always access to the remote server from which I include contents.
Solution 2:
On the "local" server (the one hosting pages that use SSI inclusion), adding the following:
ProxyPass /server2 http://www.server2.com/
ProxyPassReverse /server2 http://www.server2.com/
<Location "/server2/">
RequestHeader unset Accept-Encoding
</Location>
Basically, I'm telling Apache to disable the Accept-Encoding request header; when requesting .shtml pages to the remote server, we ask the page without compression. Consequently, we get plain text, avoiding the garbled content.
Further info: http://wiki.apache.org/httpd/ReInflating

Dynamic inclusion of piwik code by means of server side filtering inside apache server

After a few hours of fruitless attempts I would like to ask you all for a little assistance with a simple setup:
For a bunch of web applications and sites I run using the apache2 http server I use Piwik for a simple statistical overview. That works fine. What annoys me is that with every additional app/page and every update/upgrade I have to take care to manually maintain the javascript code required by piwik to be included into the html markup.
To simplify that maintainance I want have that code added by apache: I want to use some apache modules: mod_filter, mod_env, mod_substitute and mod_include. The idea is to have all html files delivered injected with the piwik code by means of a filter. That filter uses substitution to prepend the </body> tag with a placeholder. That placeholder again is a sgml tag processed by server side inclusion. That inclusion finally replaces the placeholder with the piwik code which is given the required numerical site id in a dynamic way.
So all that is left when configuring a new virtual host will be to specify two lines inside the apache configuration:
include the filter setup stored in a small file
specify the numerical piwik site id by setting an environment variable
No more fiddling around with template files inside the apps!
I am very close to a solution, one step is missing...
Maybe some of you gurus here spot what I am missing !
The (much simplyfied) configuration of the virtual host:
<VirtualHost *:80>
ServerAdmin www#domain.here
ServerName some.domain.here
# include local piwik setup
SetEnv PIWIK_ID 5
Include /etc/apache2/vhosts.d/_internal.inc
DocumentRoot /some/path/here
<Directory "/some/path/here">
Options None
AllowOverride none
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
The (shortened) configuration file included above:
# some internal resources
<Directory "/srv/www/internal">
Options None
AllowOverride None
Order deny,allow
Allow from all
</Directory>
# ----------
# internal requests to include the piwik tracking code at the bottom of every html page
# prepare required filters
FilterDeclare filter_PIWIK
FilterProvider filter_PIWIK SUBSTITUTE resp=Content-Type $text/html
FilterProvider filter_PIWIK INCLUDES resp=Content-Type $text/html
FilterChain filter_PIWIK SUBSTITUTE 's|</body>|<!--#include virtual="/piwik" --></body>|ni'
# map virtual request to the file system
Alias /piwik /srv/www/internal/piwik.php
Last file /srv/www/internal/piwik.php referenced in the Alias above:
This is the file providing the piwik code.
It is like those computed by piwik itself, except for one thing:
The numerical site index is dynamically replaced with a value from the environment variable
<?php
define('piwikBase','domain.here/stats/');
define('piwikSite',apache_getenv('PIWIK_ID'));
if(is_numeric(piwikSite)){
?>
<!-- Piwik: begin tracking code -->
<script type="text/javascript">
var pkBaseURL = (("https:" == document.location.protocol) ? "https://<?php echo piwikBase; ?>" : "http://<?php echo piwikBase; ?>");
document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
</script><script type="text/javascript">
try {
var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", <?php echo piwikSite; ?>);
piwikTracker.trackPageView();
piwikTracker.enableLinkTracking();
} catch( err ) {}
</script><noscript><p><img src="http://<?php echo piwikBase; ?>piwik.php?idsite=<?php echo piwikSite; ?>" style="border:0" alt="" /></p></noscript>
<!-- Piwik: end tracking code -->
<?php } else { ?>
<!-- invalid piwik site id: <?php echo piwikSite;?> -->
<?php } ?>
What I got working so far:
The php based dynamic piwik scniplet is working, I can call it with a browser and see the code in the html markup source complete with a numerical id taken from an environment variable defined in the configuration for testing purposes. Also, when I specify a nin-numerical id that way I get the expected placeholder <!-- invalid piwik site id: ... -->
The filter works (is applied) in general: the </body> tag is replaced by the placeholder . I can check this by commenting out the FilterProvider adding the INCLUDES directive at the beginning. In that case I see the placeholder in the generated html markup.
That FilterProvider is actually recognized and executed by apache: before I had errors about a missing Options +Includes in the configuratioon and the like, that is sorted out, apache does not complain any more. Also the placeholder is removed again, when the FilterProvider mentioned above is not commented out. So I assume the include process is working.
Aparently the problem is not that the server side inclusion refers a php script. I get no error messages and tests with a static file instead showed no difference.
It appears as if the setup works fine, no errors thrown, nothing strange in the rewrite log. However although the include filter aparently works the content I would expect to be included is empty. The placeholder inserted by the first SUBSTITUTE step is replaced again by the second INCLUDE step. but unfortunately by empty content, so it is removed. I have no idea why.
Ok, I found the answer myself. This was the sixth attempt to dig through that and finally I succeeded. All I had to do was change the definition and usage of the filter codes. Everything else was just fine:
FilterDeclare PIWIK_token
FilterProvider PIWIK_token SUBSTITUTE resp=Content-Type $text/html
SUBSTITUTE 's|</body>|<!--#include virtual="/piwik" --></body>|ni'
FilterDeclare PIWIK_code
FilterProvider PIWIK_code INCLUDES resp=Content-Type $text/html
FilterChain PIWIK_token PIWIK_code
I am surprised no one else tries stuff like this. Now I can remove all those script snippets inserted and so many places by hand without having to think of them again when doing an upgrade or when I add pages or whole sites. All I need is to specify the sites piwik id using those two lines mentioned. Great.
Update on 2013-01-23:
I have been using this solution for a few weeks now and did not enounter a single problem with the setup. So I made a short description. Mabe this this helps someone:
> Piwik tracking & automatic snippet inclusion
Seems like a good idea.. but what about ajax-loaded data? Adding to xml or json return values could be bad, making data invalid.
Have you tried
<?php include (filewithpiwik); ?>
or better yet, if you can use Wordpress, there's a plugin that will automatically add it to all pages.

Why output content with a redirect?

When you configure Apache to do a redirect, by default it outputs not just the Location header but also some content, presumably for the benefit of user agents which do not support the Location header:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>301 Moved Permanently</TITLE>
</HEAD><BODY>
<H1>Moved Permanently</H1>
The document has moved here.<P>
<HR>
<ADDRESS>Apache/1.3.37 Server at example.com Port 80</ADDRESS>
</BODY></HTML>
Is this necessary? Is there any user agent, anywhere, that I need to worry about that won't understand a Location header and a blank body?
I suspect not, as example.net itself outputs a blank body. In that case, why does Apache do this?
A number of programmatic tools for accessing websites will not follow redirects by default without explicit configuration. Returning content like this makes it a lot easier to diagnose problems caused by not following a redirect. For example, curl, commonly used in scripts, will only follow redirects if you pass it the -L option.

css was not loaded because its MIME type, "text/html", is not "text/css"

I got this error while working with web app.
This is my master page
<head runat="server">
<link href="Styles/Site.css" rel="stylesheet" type="text/css" />
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
This is the error:
Error: The stylesheet http://localhost:55381/Login.aspx?ReturnUrl=%2fStyles%2fSite.css was not loaded because its MIME type, "text/html", is not "text/css".
Source File: http://localhost:55381/Login.aspx
Line: 0
Looks like your code is requiring a login to access the CSS stylesheet, and returning a HTML login page instead of the CSS.
To verify, try pasting the URL to the stylesheet into your browser, for instance http://localhost:55381/Styles/Site.css - if you get a login page instead of CSS, that's what you need to fix.
Try this:
<location path="~/Styles">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
Where Styles is the folder that contains style sheet. I solved it this way
#DavidPrecious gave a great answer that led me to the solution.
In my case, the local computer's Users group needed to be given Read permissions to the c:\Inetpub folder in order to allow the static content to be delivered properly.
This is more likely an issue at your server side. You request style sheet page Styles/Site.css from the server of type text/css, but your server might be responding to this request with test/html. I had this issue when my server was running in Python and my server was replying to requested css files with header text/html (as that of my index.html file). I re-arranged my server code and assigned the correct headers to its corresponding pages and my issue got resolved.
Another possibility: you've modified your .htaccess file to serve css as html. Maybe something like this, for example:
<filesMatch "\.(htm|html|css|js)$">
ForceType 'text/html; charset=UTF-8'
</filesMatch>
You will want to remove the css from the first line if you've done this.
For me it was an nginx configuration problem, in the file where you declare the path to your static content. I had to move /etc/nginx/mime.types out of the http{} block and further down into where I was serving the static content from. It could similarly be an apache or IIS problem as well, depending on your technology stack.
location / {
include /etc/nginx/mime.types;
root /path/to/static/content;
try_files $uri /index.html = 404;
}