github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/plugins/fetchmetadata/framing.go (about)

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fetchmetadata
    16  
    17  import (
    18  	"log"
    19  
    20  	"github.com/google/go-safeweb/safehttp"
    21  	"github.com/google/go-safeweb/safehttp/plugins/framing/internalunsafeframing"
    22  )
    23  
    24  var (
    25  	frameableDests = map[string]bool{
    26  		"frame":  true,
    27  		"iframe": true,
    28  		"embed":  true,
    29  		"object": true,
    30  	}
    31  )
    32  
    33  // FramingIsolationPolicy protects from framing attacks.
    34  //
    35  // See https://xsleaks.dev/docs/defenses/isolation-policies/framing-isolation/#implementation-with-fetch-metadata
    36  func FramingIsolationPolicy() *Policy {
    37  	return &Policy{
    38  		isAllowed: func(r *safehttp.IncomingRequest) bool {
    39  			h := r.Header
    40  			mode := h.Get("Sec-Fetch-Mode")
    41  			dest := h.Get("Sec-Fetch-Dest")
    42  			site := h.Get("Sec-Fetch-Site")
    43  			if mode == "" || dest == "" || site == "" {
    44  				return true
    45  			}
    46  			if !navigationalModes[mode] {
    47  				// Allow non-navigational requests.
    48  				return true
    49  			}
    50  			if !frameableDests[dest] {
    51  				// Allow non-frameable requests.
    52  				return true
    53  			}
    54  			if site == "same-origin" || site == "none" {
    55  				return true
    56  			}
    57  			if safehttp.IsLocalDev() {
    58  				log.Println("fetchmetadata plugin framing protection detected a potentially malicious request")
    59  			}
    60  			return false
    61  		},
    62  		match: func(cfg safehttp.InterceptorConfig) bool {
    63  			switch cfg.(type) {
    64  			case internalunsafeframing.Disable, internalunsafeframing.AllowList:
    65  				return true
    66  			}
    67  			return false
    68  		},
    69  		skip: func(cfg safehttp.InterceptorConfig) (skip, skipReports bool) {
    70  			switch c := cfg.(type) {
    71  			case internalunsafeframing.Disable:
    72  				return true, c.SkipReports
    73  			case internalunsafeframing.AllowList:
    74  				// It is intended to get someone to frame us.
    75  				// Since Fetch Metadata is not fine-grained we cannot tell if
    76  				// a violation is intended, so we should just disable this completely.
    77  				return true, true
    78  			}
    79  			return false, false
    80  		},
    81  		signal: "FRAMING_ISOLATION",
    82  	}
    83  }