Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for the <iframe> sandbox attribute #135

Closed
kiwiz opened this issue Dec 1, 2021 · 5 comments · Fixed by #136
Closed

Support for the <iframe> sandbox attribute #135

kiwiz opened this issue Dec 1, 2021 · 5 comments · Fixed by #136

Comments

@kiwiz
Copy link
Contributor

kiwiz commented Dec 1, 2021

For my specific usecase, I'd like to enforce the existence of the sandbox attribute on <iframe>s. I haven't dug too deeply into the code, but it looks like patterns like these are usually special cased (ex: AddTargetBlankToFullyQualifiedLinks, RequireNoFollowOnLinks, etc).

Would a general purpose RequiredAttr method be considered for inclusion in the library? If so, it'd allow users to define such behaviours purely within a policy (which I think would be useful). Otherwise, I can work on a PR to just handle <iframe sandbox>.

@kiwiz
Copy link
Contributor Author

kiwiz commented Dec 1, 2021

Looks like this is a dupe of #12, although the last activity on that was in 2015 - lemme know if the thinking on that has changed.

@grafana-dee
Copy link
Contributor

I didn't know of the sandbox attribute, but reading the docs https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox shows that this would be an excellent addition to bluemonday.

I'm not sure I want to have the library scope creep from security sanitizer to HTML rewriter in a general purpose way, those are distinct things and there are good arguments to be made in favour of rewriting untrusted content pre-sanitization as well as rewriting the now trusted content post-sanitization... meaning a general purpose thing would be easy for people to mis-use and accidentally lower their security unintentionally.

But I am very much in favour of adding an option in the spirit of RequireNoFollowOnLinks, i.e. RequireSandboxOnIframe. I'm probably inclined to set up a range of constants for the different values and making that option require one of them, so that it might look like p.RequireSandboxOnIframe(bluemonday.SandboxDefaultEmpty) for the sandbox attribute alone and p.RequireSandboxOnIframe(bluemonday.SandboxAllowModals) for sandbox="allow-modals". Further that should probably consume a slice of such things as I see on https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/ that things like <iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://platform.twitter.com/widgets/tweet_button.html" style="border: 0; width:130px; height:20px;"></iframe> are permitted.

How about something like this:

p.RequireSandboxOnIframe()
p.RequireSandboxOnIframe(SandboxAllowPopups, SandboxAllowPresentation)

And a sketch of that implemented would look like this https://go.dev/play/p/KKyYNeBlu6T :

package main

import (
	"fmt"
	"strings"
)

type SandboxValue int64

const (
	SandboxAllowDownloads SandboxValue = iota
	SandboxAllowDownloadsWithoutUserActivation
	SandboxAllowForms
	SandboxAllowModals
	SandboxAllowOrientationLock
	SandboxAllowPointerLock
	SandboxAllowPopups
	SandboxAllowPopupsToEscapeSandbox
	SandboxAllowPresentation
	SandboxAllowSameOrigin
	SandboxAllowScripts
	SandboxAllowStorageAccessByUserActivation
	SandboxAllowTopNavigation
	SandboxAllowTopNavigationByUserActivation
)

func requireSandboxOnIframe(vals ...SandboxValue) {
	if len(vals) == 0 {
		fmt.Println(`sandbox`)
		return
	}

	var renderedValues []string
	for _, val := range vals {
		switch val {
		case SandboxAllowDownloads:
			renderedValues = append(renderedValues, "allow-downloads")

		case SandboxAllowDownloadsWithoutUserActivation:
			renderedValues = append(renderedValues, "allow-downloads-without-user-activation")

		case SandboxAllowForms:
			renderedValues = append(renderedValues, "allow-forms")

		case SandboxAllowModals:
			renderedValues = append(renderedValues, "allow-modals")

		case SandboxAllowOrientationLock:
			renderedValues = append(renderedValues, "allow-orientation-lock")

		case SandboxAllowPointerLock:
			renderedValues = append(renderedValues, "allow-pointer-lock")

		case SandboxAllowPopups:
			renderedValues = append(renderedValues, "allow-popups")

		case SandboxAllowPopupsToEscapeSandbox:
			renderedValues = append(renderedValues, "allow-popups-to-escape-sandbox")

		case SandboxAllowPresentation:
			renderedValues = append(renderedValues, "allow-presentation")

		case SandboxAllowSameOrigin:
			renderedValues = append(renderedValues, "allow-same-origin")

		case SandboxAllowScripts:
			renderedValues = append(renderedValues, "allow-scripts")

		case SandboxAllowStorageAccessByUserActivation:
			renderedValues = append(renderedValues, "allow-storage-access-by-user-activation")

		case SandboxAllowTopNavigation:
			renderedValues = append(renderedValues, "allow-top-navigation")

		case SandboxAllowTopNavigationByUserActivation:
			renderedValues = append(renderedValues, "allow-top-navigation-by-user-activation")

		default:
		}
	}
	fmt.Println(`sandbox="` + strings.Join(renderedValues, ",") + `"`)
}

func main() {
	// Call it like this to get the default
	// Produces `sandbox`
	requireSandboxOnIframe()

	// Call it like this to declare which values you want for the sandbox attribute
	// Produces `sandbox="allow-popups,allow-presentation"`
	requireSandboxOnIframe(SandboxAllowPopups, SandboxAllowPresentation)
}

I've got a very hectic few weeks leading up to Christmas, but I will get to this by Christmas. If you want to beat me to it and produce a PR that implements the option along those lines and adjusts the sanitizer accordingly... please feel encouraged to do so, otherwise I will get to it eventually.

@grafana-dee
Copy link
Contributor

Also... if you do take my example... I instantly reread it and nit'd on RequireSandboxOnIframe as it should probably be RequireSandboxOnIFrame. Whatever passes a linter works for me.

@kiwiz
Copy link
Contributor Author

kiwiz commented Dec 2, 2021

I'm not sure I want to have the library scope creep from security sanitizer to HTML rewriter

Yup, that sounds perfectly reasonable.


Going off that spirit, I think the list of values that's provided to RequireSandboxOnIFrame() should actually be an allowlist (since the way the sandbox attr works is that security is weakened by adding additional entries). So:

RequireSandboxOnIFrame() on:

  • <iframe sandbox="allow-same-origin"> -> <iframe sandbox="">

RequireSandboxOnIFrame(SandboxAllowPopups) on:

  • <iframe sandbox="allow-same-origin"> -> <iframe sandbox="">

If that sounds reasonable, I can take a stab at it (hopefully) sometime next week.

@grafana-dee
Copy link
Contributor

That does sound reasonable... would merge 👍

@kiwiz kiwiz changed the title Support for requiring attributes on HTML elements Support for the <iframe> sandbox attribute Dec 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants