Customizing Error Page

HTTP error response

In the NetScaler WAF you have the ability to configure a custom response when blocking traffic. This is valid for all 3 types of traffic - HTML, XML, JSON. For error handling you can use redirect URL or an error object. The error configuration consist of "error content" and response status code. We're going to discuss customization options for local error object, why we have them and should we configure them. This is configurable under "Security > Citrix Web App Firewall > Imports" and inside your AppFW Profile in Profile Settings:

Image

I would use Status Code "403 Forbidden", but you can configure any code you want.

You generally want to configure two error pages.

  • NON-PROD Error page

This error page will contain additional information regarding the blocked request and should be used only during the configuration phase of the WAF. This is because the page will contain the category and the exact rule blocking the request. Such information should not be provided to the threat actors.

  • PROD Error page

This error page will be with limited information like general blocking message and service desk URL.

You can always choose not to provide any error in response to a blocked traffic. This is probably best from protection point of view, because block feedback (error page) is still a feedback. "No feedback" does not disclose any information and hides the operational ways of working. Same difference as to "DROP" and "RESET" (RST packet is still a valuable information). You have to make a trade off, if you want error page for reporting false-positives.

Error Page

Below you can find the error page provided by Citrix. Original citrix documentation about this here

<html>
<head>
<title>Application Firewall Block Page</title>
</head>
<body>
<h1><B>Your request has been blocked by a security policy<B><BR></H1>
<H3>Access has been blocked - if you feel this is in error, please contact the site
administrators quoting the following: </H3> <UL>
<li>NS Transaction ID: ${NS_TRANSACTION_ID}
<li>AppFW Session ID: ${NS_APPFW_SESSION_ID}
<li>Violation Category: ${NS_APPFW_VIOLATION_CATEGORY}
<li>Violation Details: ${NS_APPFW_VIOLATION_LOG} </UL>
</body>
</html>

This response contains the additional information to identify the rule quickly and configure relaxation rule.

  • NS Transaction ID
  • AppFW Session ID
  • Violation Category
  • Violation Details

Depending on SYSLOG config format this would look like this:

Image

You would notice the Category "APPFW_DENYURL" and the Violation Details where the actual rule pattern is displayed "Disallow Deny URL for rule pattern="^.*generic.html$"

Customization

All this content is saved to a file in /var/tmp directory where the rest of your configured HTML responder pages live.

The NetScaler does not provide a build-in way for customization of this error page like including a logo and it doesn't need to as we can quite easily do it. Any formatiing rely on your HTML CSS Style config in that error page.

For our customization purposes we can embedd an image in the HTML document. Images can be emdedded into HTML and JSON files under base64 encoding. Base64 encoding makes file sizes roughly 33% larger than their original binary representations, which means more data down the wire. This isn't really a concern as we're using it only for a simple error page.

Go to base64-image.de and convert your image to base64. After that simply place it in your error page in the img tag.

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIEAAAACYII=" />

We can add simple formating, styling and embedd the image. Nothing too fancy it just feels better.

Image

I've inlcuded a Service Desk button for reporting the issue. Any ticket creation autmation could be included there.

Image

You can see that I have an additional field on the feedback part - Cookie. In this case I've configured the NetScaler's authentication cookie to be logged. The WAF protected resource is also protected by NetScaler AAA. In the past this was perfectly working, for some reason currently it does not. Intrestingly this has been an issue in build 11.0 for NS_APPFW_SESSION_ID:

From Release Notes: When the application firewall redirects a blocked request to a customized error page, the NS_APPFW_SESSION_ID;variable on the error page might not display the session ID accurately. If the request does not contain a session cookie, the variable might display a hyphen (-) instead of the session ID. [From Build 64.34] [# 599052]

Below you can find my error page:

<!DOCTYPE html>
<html class="">

<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		
	<title>alekaf.com</title>
	<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
		
	<style>			.error-msg,.error-title{display:block;text-align:center}.error-container{left:auto;width:90%;padding:5%;color:#333;margin:0;background-color:#fff}.error-title{font-size:30px;color:#555;margin:0 0 50px}.error-msg{line-height:1.5em;margin:0}@media all and (min-width:750px){.error-msg{text-align:left}}@media all and (min-width:900px){.error-msg,.error-title{text-align:left}.error-container{position:relative;width:840px;margin-left:50%;left:-440px;top:50%;margin-top:2%;padding:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 0 5px 0 rgba(0,0,0,.18);-moz-box-shadow:0 0 5px 0 rgba(0,0,0,.18);box-shadow:0 0 5px 0 rgba(0,0,0,.18)}body{background-color:#f1f1f1}.error-msg{margin-bottom:30px;margin-left:40px;margin-right:40px}}.service-desk{text-align:center;padding-top:50px;}.company-logo{text-align:center;padding-top:50px;}.exclam{background-color:#113797;}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:"Trebuchet MS",arial,sans-serif;margin:0;font-family:'Poppins', sans-serif;display: flex;align-items:center;justify-content: center;color:#333C48;}.alert{display: flex;align-items:center;padding: 0.55rem 0.65rem 0.55rem 0.75rem;border-radius:1rem;min-width:400px;justify-content: space-between;margin-bottom: 2rem;box-shadow:0px 3.2px 13.8px rgba(0, 0, 0, 0.02),0px 7.6px 33.3px rgba(0, 0, 0, 0.028),0px 14.4px 62.6px rgba(0, 0, 0, 0.035),0px 25.7px 111.7px rgba(0, 0, 0, 0.042),0px 48px 208.9px rgba(0, 0, 0, 0.05),0px 115px 500px rgba(0, 0, 0, 0.07)}.content{display: flex;  align-items:center;}.icon{padding: 0.5rem;margin-right: 1rem;border-radius:39% 61% 42% 58% / 50% 51% 49% 50%;box-shadow:0px 3.2px 13.8px rgba(0, 0, 0, 0.02),0px 7.6px 33.3px rgba(0, 0, 0, 0.028),0px 14.4px 62.6px rgba(0, 0, 0, 0.035), 0px 25.7px 111.7px rgba(0, 0, 0, 0.042),0px 48px 208.9px rgba(0, 0, 0, 0.05),0px 115px 500px rgba(0, 0, 0, 0.07)}.danger{  background-color: rgba(236, 77, 43, 0.2);border:2px solid #EF9400;}.danger .icon{background-color: #EC4D2B;}table {width:100%;border-collapse:separate;border:solid #EF9400 2px; border-radius:6px;}td {	background-color: #ec4d2b33;border-top: none;border-left: none;}.button-19 { appearance: button; background-color: #ec4d2b33; border: solid transparent; border-radius: 16px; border-width: 0 0 4px;  box-sizing: border-box;color: #FFFFFF;cursor: pointer;display: inline-block;font-family: din-round,sans-serif;font-size: 15px;font-weight: 700;letter-spacing: .8px;line-height: 20px; margin: 0; outline: none;overflow: visible; padding: 13px 16px;text-align: center;text-transform: uppercase;touch-action: manipulation;transform: translateZ(0);transition: filter .2s;user-select: none;  -webkit-user-select: none;vertical-align: middle;white-space: nowrap;width: 25%;}.button-19:after { background-clip: padding-box; background-color: #E9967A; border: solid transparent;  border-radius: 16px; border-width: 0 0 4px; bottom: -4px; content: ""; left: 0; position: absolute; right: 0; top: 0; z-index: -1;}.button-19:main,.button-19:focus { user-select: auto;}.button-19:hover:not(:disabled) {filter: brightness(1.1); -webkit-filter: brightness(1.1);}.button-19:disabled { cursor: auto;}
	</style>
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="cleartype" content="on"> <meta name="HandheldFriendly" content="True"> <meta name="MobileOptimized" content="width"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=2.0">
	
<script>


</script>

	
</head>

<body>

<div class="error-container">
<div class="container">
  <div class="danger alert">
    <div class="content">
      <div class="icon">
        <svg height="50" viewBox="0 0 512 512" width="50" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M449.07,399.08,278.64,82.58c-12.08-22.44-44.26-22.44-56.35,0L51.87,399.08A32,32,0,0,0,80,446.25H420.89A32,32,0,0,0,449.07,399.08Zm-198.6-1.83a20,20,0,1,1,20-20A20,20,0,0,1,250.47,397.25ZM272.19,196.1l-5.74,122a16,16,0,0,1-32,0l-5.74-121.95v0a21.73,21.73,0,0,1,21.5-22.69h.21a21.74,21.74,0,0,1,21.73,22.7Z"/></svg>
    </div>
      <p>Your request has been blocked by a security policy!</p>
    </div>
  </div>
</div>
			
<span class="error-msg">If you feel this is in error, please contact the site administrators quoting the following:</span>

<div>
	<table>
        <tbody>
          <tr >
            <td>
              ID
            </td>
            <td>
              ${NS_TRANSACTION_ID}
            </td>
          </tr>
          <tr >
            <td>
              Session
            </td>
            <td>
              ${NS_APPFW_SESSION_ID}
            </td>
          </tr>
          <tr >
            <td>
              Cookie
            </td>
            <td>
              ${COOKIE("nsc_tmas")}
            </td>
          </tr>
          <tr >
            <td>
              Category
            </td>
            <td>
              ${NS_APPFW_VIOLATION_CATEGORY}
            </td>
          </tr>
          <tr >
            <td>
              Violation
            </td>
            <td>
              ${NS_APPFW_VIOLATION_LOG}
            </td>
          </tr>
        </tbody>
      </table>
</div>

<div class="service-desk">
<a href="https://servicedesk.com" class="button-19" role="button">Report</a>
</div>

<div class="company-logo">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPoAAABeCAMAAAA9v/n3AAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAL9UExURQAAAAQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHBwQHB////zHxYmEAAAD9dFJOUwAAAQ0TDwoFEBIJAj2Vw8zCPCWRxbllCA5cqsjLunsiB3Xq6Re7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" />
</div>

</body>

</html>

Even if you decide the provide your clients with an error message. One issue remains for non-browser API clients. What if legit request is blocked? How to report this with API clients and how long it will take you to detect the false-positive block?

I hope this has been informative to you and thank you for reading!