Web Security & Security Header (Cookie, HSTS, Frame, CSP, CORS)

最後更新: 2024-01-31

目錄

 


HTTP Method

 

/etc/httpd/conf/httpd.conf

TraceEnable Off

 


Cookie

 

常見問題: Session cookie(PHPSESSID)

  • Cookie No HttpOnly Flag
  • Cookie without SameSite Attribute
  • Cookie Without Secure Flag

Syntax

Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly; Secure; SameSite=Strict

說明

HttpOnly Flag

Forbids JavaScript from accessing the cookie.

Note that a cookie that has been created with HttpOnly will still be sent with JavaScript-initiated requests.
(XMLHttpRequest.send() or fetch()) # This mitigates attacks against cross-site scripting (XSS).

Secure Flag

To ensure that the cookie is encrypted (HTTPS)

SameSite Flag

Controls whether or not a cookie is sent with cross-site requests

  • Strict                 # only for same-site requests
  • Lax(default)       # Means that the cookie is not sent on cross-site requests,
                             # such as on requests to load images or frames,
                             # but is sent when a user is navigating to the origin site from an external site
                             # (for example, when following a link)
  • None                 # both cross-site and same-site requests

PHP

php.ini

[Session]
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = "Strict"       # PHP 7.3 才有 cookie_samesite

.htaccess

# 舊版本的 php (Ver<7.3) 要使用 Apache 實現, 因為它沒有 php.ini 設定

 * 注意原來的 "^(.*)$", 別重複設定. 比如原本已經有 "httponly; samesite=lax;"

.htaccess

Header edit Set-Cookie "^(PHPSESSID)=(.*)$" "$1=$2; SameSite=Strict"

Or

Header always edit Set-Cookie ^(.*)$ $1; Secure

 


Frame

 

add_header X-Frame-Options "SAMEORIGIN";

 


HSTS(RFC 6797)

 

HSTS = HTTP Strict Transport Security

[功能]

It against man-in-the-middle attacks

原因: prevent protocol downgrade(https -> http) attacks and cookie hijacking

It forces browsers to open websites with secure HTTPS connections only.

 * unlike any other SSL error, users can’t bypass the HSTS error pages by clicking on "Proceed to x.y (unsafe)"

[運作]

A header over an HTTPS connection (HSTS headers over HTTP are ignored)

http header

 - "Strict-Transport-Security"
 - "Strict-Transport-Security: max-age=31536000"

i.e.

# Apache Settings. 7 days

add_header Strict-Transport-Security "max-age=604800; includeSubDomains";

[前題]

Trust on first use

If your browser has stored HSTS settings for a domain and
you later try to connect over HTTP or a broken HTTPS connection, you will receive an error.

Cleanup hsts on domain

chrome://net-internals/#hsts

Scroll down the page to the "Delete domain security policies" section.

Input domain name

"HSTS preloaded list"

此 list 的目的: The initial request remains unprotected

Which is a list that contains known sites supporting HSTS.

 


CSP(Content-Security-Policy)

 

Header Name: Content-Security-Policy

Syntax:

Content-Security-Policy: <policy-directive>; <policy-directive>

Settings

policy-directive

default-src

'default-src' is used as a fallback.

Content-Security-Policy default-src 'self' 'unsafe-eval' 'unsafe-inline' data:;

'self'

same URL scheme and port number

'unsafe-eval'

Allows the use of eval() # code from strings

'unsafe-inline'

CSP 強迫開發者必須把所有 inline 程式碼移到外部檔案. 加入 unsafe-inline 就可以 inline

data:

圖片以 base64 embed 到 HTML 內

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

script-src

specifies valid sources for JavaScript

script-src <source> <source>;

frame-ancestors, form-action

do not fallback to default-src, missing/excluding them is the same as allowing anything.

frame-ancestors

directive specifies valid parents that may embed a page using <frame>, <iframe>, <object>, <embed>, or <applet>.

form-action

directive restricts the URLs which can be used as the target of form submissions from a given context.

Example1:

Content-Security-Policy default-src 'self' *.example.com 'unsafe-eval' 'unsafe-inline';

*.example.com 係 http:// 及 https://, 沒有包 ws://, wss://

websocket 要另外加

wss://*.example.com
OR
ws://*.example.com

Example2: Apache .htaccess

<ifmodule mod_headers.c>
 # CSP
 Header set Content-Security-Policy: "default-src 'self' data:;\
  script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.gstatic.com/ https://gitcdn.github.io/ https://cdnjs.cloudflare.com/;\
  style-src 'self' 'unsafe-inline' https://gitcdn.github.io/ https://fonts.googleapis.com/ https://unpkg.com/;\
  font-src 'self' data: https://fonts.gstatic.com/ https://unpkg.com/;\
  frame-ancestors 'self';\
  form-action 'self';\
  img-src 'self' data:;"
</ifmodule>

Example3: Apache Config

Header set Content-Security-Policy: "default-src 'self' data: 'unsafe-inline' 'unsafe-eval' 'unsafe-eval' \
        https://fonts.googleapis.com/ \
        https://fonts.gstatic.com/ \
        https://www.google.com/recaptcha/ \
        https://www.gstatic.com/recaptcha/;"

 


X-Content-Type-Options

 

helps prevent browsers from trying to sniff the MIME type

 = instructs browsers to disable "Content or MIME sniffing"

MIME sniffing

In the absence of a MIME type, or in certain cases where browsers believe they are incorrect,

browsers may perform MIME sniffing — guessing the correct MIME type by looking at the bytes of the resource.

Setting

Header set X-Content-Type-Options "nosniff"

 


Cross-origin resource sharing (CORS)

 

A browser mechanism which enables controlled access to resources located outside of a given domain.

An example of a cross-origin request:

The front-end JavaScript code served from https://domain-a.com
   uses XMLHttpRequest to make a request for https://domain-b.com/data.json.

For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts.

Header: Access-Control-Allow-Origin

Browser 的 Requests

一共有兩類 request

1) Simple requests

2) Browser 在 request 之前會先發送 preflight request (預檢請求),
    Let the server know what X

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Credentials

same-origin policy = 相同的 protocol, domain, port

Simple requests

Some requests don't trigger a CORS preflight.

One of the allowed methods: GET, HEAD, POST

Flow

Client -> Server

# HEADER
Origin: https://foo.example

Client <- Server

# HEADER
Access-Control-Allow-Origin: *

Example: .htaccess

  • *              # All
  • URL          #
  • null           # Specifies the origin "null"

# Server accessed by any origin

Header always set Access-Control-Allow-Origin *

# Allow https://mozilla.org    # Apache Example

Header always set Access-Control-Allow-Origin https://mozilla.org

Other

multiple_domain_cors

CORS preflight

A request that doesn’t trigger a CORS preflight—a so-called “simple request”

It is one that meets all the following conditions: ...

Preflight Cache(Access-Control-Max-Age)

Set a cache for the OPTIONS check

You can set a Access-Control-Max-Age for the OPTIONS request,
  so that it will not check the permission again until it is expired.

For Chrome, the maximum seconds for Access-Control-Max-Age is 600

Access-Control-Max-Age only works for one resource every time (same URL & same method)

Preflight Request Method: OPTIONS

<location /gql/>
        Require all granted
        Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, PATCH, HEAD, DELETE"
        Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
        Header always set Access-Control-Max-Age "600"
        Header merge Vary "Origin"
        # Preflight
        RewriteCond %{REQUEST_METHOD} OPTIONS
        RewriteRule .* $1 [R=200,L]
</location>

Notes

1)

不加 rewrite 的話, 會有 error

HTTP/1.1 405 Method Not Allowed

獲得的 Access-Control-Allow-* 會無效

2)

由於用了 Rewrite, 那會沒有了原有的 Header, 所以要用 "always" 加上

# 沒 alway 時

HTTP/1.1 200 OK
Date: Mon, 13 May 2024 02:12:34 GMT
Server:
X-Frame-Options: SAMEORIGIN, SAMEORIGIN
Strict-Transport-Security: max-age=86400; includeSubDomains
X-Content-Type-Options: nosniff
Permissions-Policy: fullscreen=(self), web-share=(self)
Content-Length: 489
Connection: close
Content-Type: text/html; charset=iso-8859-1

Testing

curl -I -X OPTIONS https://URL/gql/

Origin = Request Header

Indicates the origin (scheme, hostname, and port) that caused the request
It is used to provide the "security context" for the origin request

Syntax

  • Origin: null
  • Origin: <scheme>://<hostname>
  • Origin: <scheme>://<hostname>:<port>

測試

# -D, --dump-header <filename>

curl -H "Origin: https://www.google.com.hk" \
    -D
header.txt \
    https://www.google.com.hk/ > /dev/null

cat header.txt

 * 要加上 Origin header 去 check, 因為 Server 有機會因應不同的 request 有無同的 result

 * 要加 http / https

Settings: Apache

.htaccess

Header always set Content-Security-Policy self

 


Referer Header

 

它與 Origin 相似.

The Referer HTTP request header contains an absolute or partial address of the page that makes the request.

This data can be used for analytics, logging, optimized caching, and more.

i.e.

在沒有設定 Referrer-Policy 的情況下

當在 web page "http://example.com/pricing/" 內 clicks link ("https://www.google.com"),
Browser 就會在 Referer header send "full originating URL" ("http://example.com/pricing/")
到 Google("https://www.google.com").

Referrer-Policy

i.e.

  • Referrer-Policy: no-referrer       # do not include any referrer information.
  • Referrer-Policy: origin               # Send only the origin in the Referer header. (https://domain)
  • Referrer-Policy: same-origin      # Don't send the Referer header for cross-origin requests.
  • Referrer-Policy: strict-origin      # Don't send the Referer header to less secure destinations (HTTPS→HTTP).
  • ...

.htaccess

Header always set Referrer-Policy origin

 


Nginx Summary

 

# 7 days
add_header Strict-Transport-Security "max-age=604800; includeSubDomains; preload";

add_header X-Frame-Options sameorigin;

add_header X-Content-Type-Options nosniff;

add_header Content-Security-Policy "default-src https: data: 'unsafe-inline' 'unsafe-eval'";

add_header Referrer-Policy "same-origin";

add_header X-Download-Options noopen;

CORS

# 如果要在 php 自定 "", 那要保留這 header, 不能 Cleanup !

#proxy_set_header Origin '';

HTTP_ORIGIN Header

  • The Origin request header indicates the origin (scheme, hostname, and port) that caused the request.
  • The Origin header is similar to the Referer header, but does not disclose the path, and may be null.
Origin: null
Origin: <scheme>://<hostname>
Origin: <scheme>://<hostname>:<port>

php code

<?
var_dump($_SERVER['HTTP_ORIGIN']);
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN'] . "");
?>

測試

# 模擬 js 由 domainB 連到 domainA

curl -I -H "origin: https://domainB" https://domainA/

access-control-allow-origin: https://domainB