Showing posts with label apache. Show all posts
Showing posts with label apache. Show all posts

Sunday, 19 July 2020

Configure Apache to increase protection against XSS and ClickJacking attacks.

Many software companies now use automated security test tools and many of them do a regular set of scans to ensure their application is well protected and secure.

One of the good tips to help minimized the reported issues and to avoid false positives, is to configure the application reverse proxy in the right way.
In this post, I will focus on Apache and how to add a simple configuration to mitigate multiple reported issues found by automated tools.

In this example, I am scanning a simple Java web form running behind Apache which acts as a reverse proxy in this case.
Below is the initial scan report:


As you can see, there are multiple findings in the scan report, mainly we have problems with the below:
  • X-Frame-Options Header Not Set
  • X-Content-Type-Options Header Missing
  • Absence of Anti-CSRF Tokens
  • Cookie Without SameSite Attribute
  • Charset Mismatch (Header Versus Meta Charset)  (informational finding)
We can mitigate most of those issues by properly setting headers in the Apache configuration, this should avoid most of the above issues.

Below is an example configuration for the domain http://172.17.0.1/:

# Set those headers to enable right CORS headers.
Header always set Access-Control-Allow-Origin "http://172.17.0.1"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"
Header always set Access-Control-Expose-Headers "*"

# Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request.
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

# Set x-frame-options to sameorigin to combat click-jacking.
Header always set X-Frame-Options  SAMEORIGIN

# Set Samesite to strict to counter potential session cookie hijacking, also helps protect 

# from CSRF though session cookie.
Header edit Set-Cookie ^(.*)$ $1;SameSite=strict

# Set X-Content-Type-Options to nosniff to avoid MIME sniffing.
Header always set X-Content-Type-Options nosniff


As you can see, the above rules address multiple issues reported in the scan, mainly to protect agains CORS problems,click-jacking, XSS though cookies and Anti-MIME sniffing.

Once that configuration is in effect, the number of reported issues is much less:


The last problem related to CSRF is an architectural problem in the application.
Modern web applications are encouraged to use tokens injected into the HTML pages and forms, dynamic generated content and on server side to validate that same user with same session is one who is submitting the request without being hijacked by some malicious attack.
Check https://en.wikipedia.org/wiki/Cross-site_request_forgery for more information.

Modsecurity does have rules that work by injecting content to help with CSRF, though, if applied without change, it could break the application functionality.





 

Monday, 24 February 2020

Apache: How to conditionally inject Authorization header

 In one of the cases I was helping with, I ran into a requirement that needed to have a webhook be sent from Jira to Jenkins to trigger a build based on certain condition.
You can trigger a webhook from Jira based on a Jira search query or on a certain action, eg: upon creation of an issue in a project for example.

The problem is that Jira doesn't support using userinfo fields in the URL, which is actually deprecated and should be used as per the RFC https://tools.ietf.org/html/rfc3986, and thus, Jira would have a way to send authentication information out of the box to Jenkins to trigger a build.

To solve this, I proposed to run Jenkins behind an Apache reverse proxy and use a virtual host definition similar to the below:

<VirtualHost *:80>
     ServerName feanor
     DocumentRoot /var/www/html/

     SetEnvIf Remote_Addr "10.0.0.12" buildtriggerjira
     RequestHeader set Authorization "Basic c2hlcmlmOnNoZXJpZg=="   env=buildtriggerjira

     ProxyPreserveHost on
     ProxyVia on
     ProxyPass "/jenkins" "http://feanor:8080/jenkins"
     ProxyPassReverse "/jenkins" "http://feanor:8080/jenkins"
</VirtualHost>

The above configuration will inject an Authorization header in the incoming request on condition that the remote address is the IP address of the Jira server.
This works by setting an Apache environment variable to a certain value in the SetEnvIf condition and then setting the Authorization header if the variable has that value set.

More checks can be made to harden the condition by checking more request fields like the user-agent string or other headers set by the Jira http client sending the webhook request.

Tuesday, 19 March 2019

Setting RequestHeader in Apache

Apache can be used to inject a Request Header in the incoming request that can be either consumed by Apache or forwarded further to another underlying service, in this case Apache works essentially as a reverse proxy.

In a test setup where Apache works as a reverse proxy in front of tomcat, the below Apache configuration is used to implement the reverse proxy functionality and add a Request Header:

<VirtualHost *>
   <Location "/sherif">
      ProxyPass http://127.0.0.1:8080/sherif
      ProxyPassReverse http://127.0.0.1:8080/sherif
      RequestHeader set myh "valueofarequestheader"
   </Location>
</VirtualHost>

In this setup, tomcat is using default port 8080, it has a defined context path for a dummy application defined in tomcat under its context.xml:

<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <Manager pathname="/sherif" />
</Context>


Under tomcat webapps, we have folder created as sherif and has a simple index.html file to support the test:
[root@localhost conf]# ls -lt ../webapps/sherif/
total 4
-rw-r--r-- 1 root root 14 Mar 19 16:27 index.html
[root@localhost conf]#

To verify the header being added, we configure tomcat to log the head myh.
This is done on tomcat server.xml accesslog value as below:

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b %{myh}i" />

Once a request is sent to http://localhost/sherif, tomcat logs the below log showing the request header being added by Apache and reaching tomcat:

127.0.0.1 - - [19/Mar/2019:16:46:47 -0400] "GET /sherif/ HTTP/1.1" 200 17 valueofarequestheader

This configuration is useful in passing headers to backend services in case those are not already sent by the source user agent.


Friday, 9 March 2018

How to check Client SSL cert from certain IPs only


Lately, I had a requirement to configure Apache reverse proxy in such a way to ensure any user logging through the Apache reverse proxy is presenting a valid client certificate if they are not internal users.

Internal users using local network IPs and local domain names should be allowed to access without any certificate validation.

To achieve this, we can use the below simple construct if we are using Apache 2.4.x:


<If "%{REMOTE_ADDR} !~ /^127.0.0.1$/ && %{REMOTE_ADDR} !~ /^192.168.[0-9]+.[0-9]+$/">
        SSLVerifyClient require
        SSLVerifyDepth 2
</If>

In case we are using Apache 2.2, the config needs to involve mod_rewrite as below:

SSLVerifyClient optional

SSLVerifyDepth  2
<Location / >
        Order deny,allow
        Deny from all
        Satisfy any
        Allow from ALL
        RewriteEngine on
        RewriteCond %{SSL:SSL_CLIENT_VERIFY} !^SUCCESS$
        RewriteCond %{REMOTE_ADDR} !^127.0.0.1$
        RewriteCond %{REMOTE_ADDR} !^192.168.1.[1-9]+$
        RewriteRule   ^  -  [F]
</Location>

Using SSL verifyclient optional will still try to verify clients but will not block their access.
This, allows us to test if the verification worked or not with mod_rewrite as seen above.

Thursday, 15 September 2016

Apache / haproxy large Cookie size issues

Although HTTP protocol doesn't have a limit on the cookie size, which is in turn a part of the request/response headers, Apache does impose a limit to protect the webserver from denial of service attaches.

Apache controls this using the "LimitRequestFieldSize" directive which defaults to 8192 byte as a max for a header field.

If larger cookies are needed, we need to bump up this value to a bigger number.
eg:

LimitRequestFieldSize 18192

in order to test this, i set up a simple webserver config with the below config:

RequestHeader unset If-Modified-Since
RequestHeader unset If-None-Match

LimitRequestFieldSize 18192

Header set Set-Cookie "apachenode=node1; path=/;"
Header add Set-Cookie "env=test; path=/;"
Header add Set-Cookie "TimeS=NONE; path=/;"


The LimitRequestFieldSize only works on Requests not on the response, thus apache can set-Cookies with large values with no problems, its the job of the browser to validate this.

To be able to test this from Firefox, I used the Cookie Manager plugin https://addons.mozilla.org/en-US/firefox/addon/cookies-manager-plus/ to set a large cookie that is 24k in size with a random string generated from the site: http://textmechanic.com/text-tools/randomization-tools/random-string-generator/

I managed to prove the compiled limit in Centos 7 is indeed 8192 and expanded that to ~18k as seen above and it worked.

When apache fails to accept the request it responses with an http 400 error as below:


I tried to do the same test with commandline curl but seems curl truncates the large cookie when using the -b option and passing a file as below:

[root@feanor conf]# curl -b ./cookies.txt  http://localhost/new.html
this is a new page :)
[root@feanor conf]# ls -ltrh ./cookies.txt
-rw-r--r-- 1 root root 25K Sep 15 12:28 ./cookies.txt
[root@feanor conf]#


Although the cookie file contains a single cookie that is 24k in size, curl seems to have truncated it and the request went in. 
lowering the LimitRequestFieldSize to something like 4600 managed to have curl reproduce the same behaviors as the browsers:

[root@feanor conf]# curl -b ./cookies.txt  http://localhost/new.html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Size of a request header field exceeds server limit.<br />
<pre>

</pre>
</p>
</body></html>
[root@feanor conf]#

Thus, curl could be tricking you if you are debugging an issue like this from the command line.

Also one more note about large Cookie sizes, if haproxy is used in the setup, eg: as a balancer in front of Apache or for SSL offloading, it could be needed to increase the tune.bufsize so that it can accept larger requests and larger Cookie sizes.

Haproxy checks the size as (tune.bufsize - tune.maxrewrite) where Maxrewrite is adjusted to half buffersize if that is larger than 1024.
Given the above info, tune.bufsize should be set to be double the Apache LimitRequestFieldSize.
Watch out for memory and ensure haproxy has enough resources to work with no issues.



Wednesday, 17 August 2016

Hardening Your Apache Configuration

Exposing apache webserver to the public internet is a serious business.
Any exposed service need to be secured and only expose "the needed" functionality "only" as per the application need.

Apache comes by default with many things disabled, still some more work needs to be done to ensure your server is secure and not open to easy attacks.

Below are some examples of configurations that could help make apache more secure.

Hiding the version of Apache:

We need to hid all the info exposed by apache about the host machine and about itself. To do this we need to set the below in the global config section:

ServerSignature Off
ServerTokens Prod


Also we can try to use mod_headers to unset the server header, though most of apache binary distributions has this hard-coded.

Header unset Server

 

Turn off directory browsing:

Apache by default allows directory listings if we use file system derived URLs under the default document root.
This is an issue as it can expose a lot of info about the local files served by apache. to stop this we need to disable indexes in location scopes:

Options -Indexes

Disabling TRACE method:

We need to disable all the HTTP methods that are not going to be used by the application, most importantly Trace method:

To do this we do either of the below configurations:

TraceEnabled Off

or:

RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]


Enable Mod_reqtimeout to prevent Slowloris attack:


This is to prevent apache resources from being depleted by slow connections that tries to hold resources for longer duration leading eventually to denial of service.
To battle this attack we need to enable mod_reqtimeout and set its parameters with appropriate values according to the appliaction.
Below values are only for demo perposes:

LoadModule reqtimeout_module    "mod_reqtimeout.so"

<IfModule mod_reqtimeout.c>
   RequestReadTimeout header=120,MinRate=500 body=120,MinRate=500
</IfModule>


Remove the default Error document:

Apache uses a set of default error documents that are presented on http error codes like 404 or 500.
Those pages expose a lot of info about apache and it is better to replace them with custom pages.
A minimum config is shown below for most common error pages:

ErrorDocument 500 "Internal Server Error"
ErrorDocument 404 "The Requested URI is not found "
ErrorDocument 503 "Service Not found"
ErrorDocument 403 "Forbidden"

Disable all not needed modules:

Need to disable all the modules not needed by application, simply comment them and only enable the ones that your application needs.


A more comprehensive list that covers further hardening and security issue fixes can be found in the below link:

https://geekflare.com/apache-web-server-hardening-security/

Wednesday, 10 August 2016

Overiding backend service error page when using apache proxypass config

Most of the apps that I have supported before are large enterprise grade apps that use stand alone content management software and a large team of content authoring and publishing.
Most of the time those guys take care of every content related staff like site version notes and error pages.

I came across a smaller project this week and I was asked to handle the errors coming out of the application for security reasons.
The application was hosted by tomcat and had apache infront of it to do proxy pass work and Shibboleth authentication.

Tomcat default error page is is very basic yet give a way a lot of info about the tomcat version your application runs, thus it is a good idea to hid it if possible.

Simplist way to do this is from apache using the ProxyErrorOverride set to on as seen below:


ProxyPass "/" "http://backend_tomcat_host:8080/"
ProxyPassReverse "/" "http://backend_tomcat_host:8080/"

ProxyErrorOverride On
ErrorDocument 500 "Internal Server Error"
ErrorDocument 404 "The Requested URI is not found "
ErrorDocument 503 "Service Not found"
ErrorDocument 403 "Forbidden"

The above will offer the user the most basic error pages possible and will hid all the Tomcat details.
More complex Error pages can be used by replacing the above simple text with a URI of an html error page.

For apache 2.2.x the full documentation is available at: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxyerroroverride