Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Best practice regarding Security-Relevant HTTP Headers
#1
Hi. We have a customer that has performed a penetration test for one of our web applications and claims in one finding that several security-related HTTP header are missing, these are Strict Transport Security, XSS Protection, Content Type Options and Content Security Policy. They recommend that at least the three first are set in order to consider the finding as fixed. We do however set these three at the beginning of a session according to recommendations in a previous forum thread, but I guess that these do not carry over to every response sent by the web application. Our current code looks like this:

Code:
procedure TBaseClientServerController.IWServerControllerBaseNewSession(aSession: TIWApplication);
...
begin
  if Assigned(aSession) and Assigned(aSession.Response) and (SSLOptions.Port <> 0) then
    SetCustomHeadersForHSTS(aSession);
...
end;

{Impl. from Hafedh TRIMECHE, see https://forums.embarcadero.com/thread.jspa?messageID=677727#677727}
procedure SetCustomHeadersForHSTS(aSession:TIWApplication);
type
  TCustomHeader=
  record
    Key   ,
    Value : UnicodeString;
  end;
const
  CustomHeaders : array[1..5] of TCustomHeader =
  (
  (Key:'Strict-Transport-Security' ; Value:'max-age=31536000; includeSubDomains'),
  (Key:'Pragma'                    ; Value:'no-cache'),
  (Key:'Cache-Control'             ; Value:'no-cache, no-store, must-revalidate, private'),
  (Key:'X-Content-Type-Options'    ; Value:'nosniff'),
  (Key:'X-XSS-Protection'          ; Value:'1; mode=block')
  );
var
  iHeaders : Integer;
begin
  aSession.Response.Expires             := EncodeDate(1000,1,1);//31/12/1899 00:00:00;
  aSession.Response.AllowCaching        := False;
  aSession.Response.CacheControlEnabled := False;
  for iHeaders:=Low(CustomHeaders) to High(CustomHeaders) do
  begin
    if CustomHeaders[iHeaders].Value<>'' then
    begin
      aSession.Response.Headers.Values[CustomHeaders[iHeaders].Key] := ' '+CustomHeaders[iHeaders].Value;
    end;
  end;
end;

If you test our web application with SSL Labs, it has always (since we first implemented this) recognized that we use HTTP Strict Transport Security and given us an A+ rating. This leads to a number of questions:

  1. Is it sufficient to set these all these headers at the beginning of an IW session (which would imply that the tester's conclusion is wrong)?
  2. Or should one or more of these headers be set for every response?
  3. If Yes on 2, which are the appropriate ServerController properties and event(s) to use? Example code?
  4. For the fourth header, Content Security Policy, the tester writes the following: "Content Security Policy requires careful tuning and precise definition of the policy. If enabled, CSP has significant impact on the way the browser renders pages (e.g., inline JavaScript is disabled by default and must be explicitly allowed in the policy). CSP prevents a wide range of attacks, including Cross-Site Scripting and other Cross-Site injections.". They recommend using this if this does not interfere with the application, and gives an example header like this: "Content-Security-Policy: default-src 'self'". What is the consequence of using this in an IntraWeb application? I it something we should attempt?

I would be much grateful for feedback on this. The tester regard this as a Medium severity finding that we need to fix in the near future.
 
Best regards

Magnus Oskarsson
Reply
#2
Hi Magnus. I have suggestions to you add in your headers:
Code:
Referrer-Policy  ->  same-origin
Strict-Transport-Security  ->  max-age=15768000; includeSubDomains; preload
X-Content-Type-Options  ->  nosniff
X-Permitted-Cross-Domain-Policies  ->  master-only
X-XSS-Protection  ->  1; mode=block
Now, i suggest you be careful and test all your application running all forms with console opened.
Code:
Content-Security-Policy  ->  default-src 'self' https: 'unsafe-inline' 'unsafe-eval'; img-src 'self' https: data:; object-src https 'self'; media-src 'self' https:; style-src 'self' https: 'unsafe-inline'; script-src 'self' https: 'unsafe-inline' 'unsafe-eval'; frame-ancestors https: 'self' *.facebook.com; base-uri 'self' https:
This is working FOR ME, make all your adjustment for your needs.
Reply
#3
Hi Jose and thanks for sharing your thoughts on this! There is more here for me to look into I see. But it does not explicitly answer my four questions, so it would still be great to have feedback on that (from you or other forum members).

Best regards

Magnus Oskarsson
Reply
#4
What IW version are you using? CSP is a lot of "feel good nonsense" in many areas, but 15.1 has been updated to be CSP compliant.

https://www.atozed.com/2019/07/15-1-0-h/

Content Security Policy (CSP) support. Read more about CSP here. https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
Good part of the rendering engine was refactored to remove inline JavaScript, CSS and unsafe code (according to CSP best practices). A nonce is now included in all scripts/link files.

Cross-Origin Resource Sharing (CORS) support. Read more about CORS here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Reply
#5
Hi. As we finally seem to have fixes and workarounds for a working IntraWeb and CGDevTools combination I am revisiting this issue. I think I will look at this in two steps, first make sure we comply to the first three headers, then look at CSP separately. I still need to know best practice on when and how to set these headers. Should I set them for every reply or is it sufficient at the beginning of the session? In the first case, is e.g. ServerController.OnAfterDispatch a good place? (We already have some code in this event handler) Or are there other properties and/or events that do the job better?

Best regards

Magnus Oskarsson
Reply
#6
If you use 15.1, IW will do the CSP stuff for you without the need to do it manually as its more than simply setting headers.
Reply
#7
(11-01-2019, 03:54 PM)kudzu Wrote: If you use 15.1, IW will do the CSP stuff for you without the need to do it manually as its more than simply setting headers.

Thanks for the info. Yes, this is with 15.1.7 (IW standalone server). Is there some setting for enabling this, I do not see any Content-Security-Policy header being sent by IntraWeb? Or should I add this header myself according to the suggestion above?

Best regards

Magnus Oskarsson
Reply
#8
From the release notes.

"Content Security Policy (CSP) support. Read more about CSP here. https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
Good part of the rendering engine was refactored to remove inline JavaScript, CSS and unsafe code (according to CSP best practices). A nonce is now included in all scripts/link files."

I will ask Alexandre to respond further.
Reply
#9
CSP headers need to be set at each response.

CSP is a royal PITA and, as Chad mentioned, it is mostly nonsense. Most people who implement it are actually forced to comply with some "rules" created by web security audit companies. If that's your case then I believe you have no real choice.

The best place do set this is on ServerController.OnAfterDispatch event, yes.

Here is some code extracted from a test application, which can help you with that:


Code:
procedure TIWServerController.IWServerControllerBaseConfig(Sender: TObject);
begin
  // Content Security Policy - CSP

  FServerList := 'http://127.0.0.1:8888 ';
  // you may add to the server list other domains, for instance, a google API resource:[/font][/size][/color][/font][/size][/color]
  // FServerList := FServerList + ' https://fonts.googleapis.com ';
end;

function TIWServerController.GetCSPValue(aReply: THttpReply): string;
var
  WebApp: TIWApplication;
begin
  // Content Security Policy - CSP
  Result := 'frame-ancestors ' + QuotedStr('none') + '; ' +
            'base-uri ' + FServerList + '; ' +
            'default-src ' + FServerList + ' ' + QuotedStr('nonce-' + aReply.Nonce) + ' ' + QuotedStr('unsafe-eval') + '; ' +
            'script-src ' + FServerList + ' ' + QuotedStr('nonce-' + aReply.Nonce) + ' ' + QuotedStr('unsafe-eval') + ' ' + QuotedStr('unsafe-inline') + '; ' +
            'style-src ' + FServerList + ' ' + QuotedStr('nonce-' + aReply.Nonce) + ' ' + QuotedStr('unsafe-inline') + '; ' +
            'img-src ' + FServerList + ';';
end;

procedure TIWServerController.IWServerControllerBaseAfterDispatch(
  Request: THttpRequest; aReply: THttpReply);
begin
  aReply.AddHeader('Content-Security-Policy', GetCSPValue(aReply));
end;

Have in mind that:

- In IW, CSP is based on nonces. A nonce is a base-64 encoded big 160-bit random number, unique for each response. Each THttpReply instance will generate a new nonce which can be obtained via its Nonce property. Nonce can also be obtained via WebApplication.Nonce property.
- All internal <script> or <style> tags will contain a nonce, which should match with the response header. If not, the browser will block it and something will fail when page is rendering (scripts won't run, styles won't be applied, images won't load, etc). In general a failure is highly visible.
- default-src should be enough in most cases, but Firefox has a serious bug when no script-src is found, so I strongly recommend you to keep them all (i.e. default-src, script-src, style-src and img-src at least - your app might need others)
- unsafe-eval is needed for script processing and that's not going to change. JavaScript doesn't offer a real alternative for eval() when executing code generated on server side on the fly. And the real point is: If one can inject code into an HTTPS response, on the fly, CSP won't actually stop him...
- you can start with more permissive policies (like the one above) and then play with it and see how your application behaves.

Please let me know if you need further information
Reply
#10
Thanks Alexandre for the detailed explanation. Based on your suggestion, Jose's and some other examples I saw I tried with the following code in IWServerControllerBaseBeforeDispatch (I ended up adding custom header code there since I had to make some exceptions based on request contents). (Note that i took away 'frame-ancestor' since I got a message that it was in conflict with X-Frame-Options which I also set).

Code:
  if CSPEnabled then
  begin
    NonceStr := 'nonce-' + aReply.Nonce;
    aReply.Headers.Values['Content-Security-Policy'] := Format(
//      'frame-ancestors ''none''; ' +
      'base-uri ''self''; ' +
      'default-src ''self'' ''%s'' ''unsafe-eval''; ' +
      'script-src ''self'' ''%s'' ''unsafe-eval'' ''unsafe-inline''; ' +
      'style-src ''self'' ''%s'' ''unsafe-inline''; ' +
      'img-src ''self'';', [NonceStr, NonceStr, NonceStr]);
  end{if};

The resulting header looks like this (from IE11 browser network capture):

Code:
Key    Value
Content-Security-Policy    frame-ancestors 'none'; base-uri 'self'; default-src 'self' 'nonce-TktBTfCDkv9C6TZKmz3WgwM6mGs' 'unsafe-eval'; script-src 'self' 'nonce-TktBTfCDkv9C6TZKmz3WgwM6mGs' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'nonce-TktBTfCDkv9C6TZKmz3WgwM6mGs' 'unsafe-inline'; img-src 'self';

But I get errors in Chrome and Firefox already on our start page (a login page). The following is from the Chrome console:

Code:
IWBackIntercept__ED5577121.js:82 Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'nonce-LIVJbVIkgn8QhRhHqbkldhAeZR4' 'unsafe-inline'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
Init @ IWBackIntercept__ED5577121.js:82
(anonymous) @ (index):33
(index):179 Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'nonce-LIVJbVIkgn8QhRhHqbkldhAeZR4' 'unsafe-inline'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
(index):185 Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'nonce-LIVJbVIkgn8QhRhHqbkldhAeZR4' 'unsafe-inline'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
(index):133 Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-LIVJbVIkgn8QhRhHqbkldhAeZR4' 'unsafe-eval' 'unsafe-inline'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.

As I am new to this, I am guessing I am missing some basic thing here. Any feedback is appreciated.
EDIT: If I remove the nonce parts from the header it seems to run without errors in Chrome and Firefox. Is the remaining CSP header still meaningful (i.e. can it still be regarded as a security improvement)?
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)