Skip to main content

Third-Party Content Security

Imagine you invite a guest into your home. You know your own family, but you do not know everything about your guest, or the friends they bring. If a guest is careless, or has bad intentions, they can make a mess of your house, or watch what your family does, and your visitors will blame you.

Every time your page loads something from another website (a video player, a map, an analytics script, a font, a chat widget, an advert, a library from a CDN), you are inviting a guest into your page. This final lesson in the section teaches you how to be a careful host.


What You Will Learn​

  • What counts as third-party content, and what can go wrong
  • Controlling what may load, with Content-Security-Policy
  • Checking that a file has not changed, with Subresource Integrity
  • Restricting frames with sandbox, allow, and Permissions-Policy
  • Controlling what other sites learn, with referrer rules
  • Consent, privacy, and the click-to-load pattern
  • Reviewing, monitoring, and planning for incidents

What Is Third-Party Content?​

First-party content is served by you, from your own site. Third-party content is served by someone else, but shown on your page.

KindExamples
ScriptsAnalytics, advertising, chat widgets, tag managers, libraries from a CDN
FramesVideo players, maps, payment forms, social posts, booking widgets
Styles and fontsFont services, CSS libraries
Images and pixelsAdverts, tracking pixels, images hosted elsewhere
Data callsRequests from your scripts to someone else's API
Links and redirectsLinks out, or short-link services

Much of the modern web is made of these. They save time and give features you could not build yourself. But each one comes with trust attached.

What Can Go Wrong?​

RiskWhat happens
A script is changed or hacked (a "supply chain" attack)Harmful code runs on your page, with the same power as your own: it can read what visitors type, change the page, and send data away
TrackingThe third party sees who visited which page, and sets cookies, often across many sites
Slow or broken servicesYour page hangs, or breaks, because someone else's server is down
Malicious adverts or widgetsPop-ups, redirects, fake warnings, or downloads appear on your page
ClickjackingSomeone frames your page invisibly, and tricks visitors into clicking on it
Excess permissionsA frame asks for the camera, microphone, or location
Leaked informationThe address of the page, including private parts, is sent to the third party in the referrer
Privacy and legal problemsLoading trackers without consent can break the law in many places

A third-party script has the same power as your own code. If you load it, you trust it with your visitors' data. That is why the rest of this lesson matters.

Defence in Layers​

No single tool solves everything. Good security uses layers, so that one failure does not mean total failure:

LayerToolWhat it does
1. Choose wellReview, remove, self-hostFewer guests, and ones you trust
2. Limit what may loadContent-Security-PolicyOnly allowed sources can run or show
3. Check what loadsSubresource IntegrityA changed file is refused
4. Fence it insandbox, allow, Permissions-PolicyEmbedded content gets only the permissions it needs
5. Say lessReferrer-Policy, rel, cookiesOther sites learn less about your visitors
6. Ask firstConsent, click-to-loadNothing loads until the visitor agrees
7. Plan for failureFallbacks, monitoring, incident planThe page still works, and you notice problems

Layer 2: Content-Security-Policy​

A Content-Security-Policy (CSP) is a rule list, sent by your server in a response header. It tells the browser where each kind of content is allowed to come from. Anything else is blocked, even if an attacker managed to put it on your page.

Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
frame-src https://player.example.com;
img-src 'self' data:;
connect-src 'self' https://api.shop.example;
object-src 'none';
base-uri 'self';
frame-ancestors 'self'
DirectiveControls
default-srcThe fallback for any directive you do not set
script-srcWhere scripts may come from, and whether inline scripts run
style-srcStylesheets
img-srcImages
connect-srcfetch, XHR, WebSocket, and similar calls
frame-srcWhich pages you may embed in frames
frame-ancestorsWhich sites may embed your page (the defence against clickjacking)
object-srcobject and embed content. 'none' is a good choice
base-uriWhich addresses <base> may use

Common source values: 'self' (your own origin), 'none' (nothing), https: (any secure address), a specific origin such as https://cdn.example.com, and data:. Note the quotes around 'self' and 'none'.

Try It: Write a Policy​

Edit the allowed sources. The table shows what would happen to ten typical resources. Try the too-loose policy, the report-only switch, and removing 'unsafe-inline'.

Response header
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; frame-src https://player.example.com; img-src 'self' data:; connect-src 'self'
Or, as a fallback, in the page (some directives are ignored here)
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://cdn.example.com">

CSP in Practice​

  • Prefer the header to the <meta> tag. The meta version cannot use some directives, such as frame-ancestors, and it only applies after the browser has read it.
  • Avoid 'unsafe-inline' and 'unsafe-eval' for scripts. They allow the very kind of code that attacks use. If you must have inline scripts, use a nonce (a random value, new on every response: script-src 'nonce-R4nd0m', and <script nonce="R4nd0m">) or a hash.
  • Start in report-only mode. Send Content-Security-Policy-Report-Only first. The browser allows everything, but reports what would be blocked. Fix the problems, then switch to the enforcing header.
  • Watch the console. Blocked resources appear as errors in the browser's developer tools.
  • Be specific. https: or * allows far more than you intend.
  • Keep it up to date. Each new widget you add may need a new entry, which is a good moment to ask whether you need it.
  • CSP limits damage, but is not a cure. Keep your own code safe too (escape output, validate input).

Layer 3: Subresource Integrity​

When you load a library from a CDN, you trust that the CDN serves the same file tomorrow as today. Subresource Integrity (SRI) removes that assumption. You put a hash (a fingerprint) of the file in your page. If the file changes by even one character, the fingerprint no longer matches, and the browser refuses to run it.

<script
src="https://cdn.example.com/lib@1.2.3/lib.min.js"
integrity="sha384-BASE64HASHGOESHERE"
crossorigin="anonymous"></script>

<link
rel="stylesheet"
href="https://cdn.example.com/lib@1.2.3/lib.min.css"
integrity="sha384-BASE64HASHGOESHERE"
crossorigin="anonymous">

Try It: Break the Fingerprint​

The demo below really calculates the hashes in your browser. Press Simulate a tampered file and watch the second hash change, and the verdict flip.

Make a hash on the command line
curl -s https://cdn.example.com/lib@1.2.3/lib.min.js \
| openssl dgst -sha384 -binary \
| openssl base64 -A
# then write: integrity="sha384-" followed by the output
Or in JavaScript
const bytes = new TextEncoder().encode(fileText);
const buffer = await crypto.subtle.digest("SHA-384", bytes);
const hash = btoa(String.fromCharCode(...new Uint8Array(buffer)));
console.log("sha384-" + hash);

Rules for SRI:

  • Pin the exact version in the address (lib@1.2.3), never "latest". A file that is meant to change will fail its own hash.
  • Use it only for files that do not change, such as versioned libraries. It does not suit analytics or advert scripts, which change often by design.
  • Add crossorigin="anonymous". The CDN must send CORS headers, or the check will fail.
  • Have a plan for a failed check. The script will not run, so the page needs a fallback.
  • Better still, host the file yourself when you can. Then you control it.

Layer 4: Fence It In​

Frames: sandbox and allow​

As you saw in Iframe, the sandbox attribute removes permissions from a frame, and you add back only what is needed. The allow attribute grants powerful features such as fullscreen, camera, microphone, or location, which are off by default for frames from other sites.

<iframe
src="https://player.example.com/embed/42"
title="Interview with a baker (video, 12 minutes)"
sandbox="allow-scripts allow-same-origin allow-presentation"
allow="fullscreen; picture-in-picture"
referrerpolicy="strict-origin-when-cross-origin"
loading="lazy"></iframe>

Remember the warning: do not combine allow-scripts and allow-same-origin for content from your own origin.

Permissions-Policy​

The Permissions-Policy response header sets, for your whole page, which powerful features may be used at all, by you and by any frames:

Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=()

An empty list () means "nobody, including me". (self) means only your own origin. It is a useful safety net: even if a third-party widget asks for the camera, the answer is no.

Your own pages: frame-ancestors​

To stop others from framing your pages (clickjacking), send:

Content-Security-Policy: frame-ancestors 'self'

X-Frame-Options: DENY or SAMEORIGIN is an older header that does a similar job.

Layer 5: Say Less​

Referrer-Policy​

When a visitor's browser loads a third-party resource or follows a link, it may tell the other site which page it came from. That address can include private information (a search term, an account number, a reset token). Limit it:

Referrer-Policy: strict-origin-when-cross-origin

This sends the full address only within your own site, and only the origin (the site name) to others, and nothing at all on a drop from https to http. For something sensitive, use no-referrer. You can also set it per element with referrerpolicy="no-referrer" on iframes, images, scripts, and links.

Never put secrets in addresses (tokens, passwords, personal data). Addresses leak into logs, history, and referrers.

<a href="https://other.example" target="_blank" rel="noopener noreferrer">Visit their site</a>
  • rel="noopener" stops the new tab from controlling your page through window.opener. Current browsers do this by default for target="_blank", but writing it costs nothing, and helps older ones.
  • rel="noreferrer" also stops the referrer from being sent.
  • For links in user-written content, use rel="ugc nofollow noopener noreferrer", and for adverts or paid links, rel="sponsored".

Cookies​

Cookies set by third parties can follow visitors from site to site. For your own cookies, use the safe attributes: Secure, HttpOnly, and SameSite=Lax (or Strict).

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/

Many browsers now block or limit third-party cookies, but do not rely on that. Plan as if trackers can still see your visitors.

Layer 6: Ask First, and Load Later​

  • Consent. In many places (the EU, the UK, and increasingly elsewhere), the law requires you to get a visitor's agreement before loading trackers and some third-party content, and to make "reject" as easy as "accept". Rules vary by place and over time, so check what applies to you. This lesson is not legal advice.
  • Click-to-load. Show a placeholder, and create the frame or load the script only when the visitor asks. Nothing is sent to the third party until then. See the example in Maps.
  • Make consent accessible. A cookie banner must work with a keyboard and a screen reader, must not trap focus, and must not cover the page's content.
  • Be honest in your privacy notice. List the third parties, what they receive, and why.

Layer 1 and 7: Choose Well, and Plan for Failure​

  • Keep an inventory. List every third party your site loads from. Open the browser's network panel, and see who is really there. Sites are often surprised.
  • Remove what you do not need. Every widget is a risk, and a cost in speed.
  • Self-host where you can: libraries, fonts, and images. You control them, and you save a connection.
  • Be careful with tag managers. They let people add scripts without a code review. Restrict who can publish, and review what is added.
  • Choose reputable providers, and read their security and privacy information. Check that they are accessible, too.
  • Load third-party scripts with async or defer, so a slow server does not block your page.
  • Make the page work without them. If the chat widget, the map, or the analytics script fails, the page should still do its job.
  • Watch for changes. Use CSP reports, and check your page regularly for new requests.
  • Have an incident plan. Know how you would remove a compromised script quickly, who decides, and how you would tell your visitors.

Try It: Review Before You Embed​

Use this checklist whenever you add something to your page, and again every few months.

http://127.0.0.1:5500/index.html
Before you add third-party content

0 of 12

Tick each item you have really checked.

Untrusted Content from Your Own Visitors​

Third-party content is not only other companies. Your visitors' words are also untrusted: comments, profile names, uploaded files.

  • Show text with textContent, never innerHTML.
  • If you must allow some HTML, clean it with a well-tested sanitiser library, on the server, and again before inserting it.
  • Show uploaded SVG with img, not inline or in object, and clean uploads on the server.
  • Escape values when you put them into attributes, links, and scripts.
  • Check file type and size on the server, and serve uploads from a separate domain, so a harmful file cannot reach your main site's cookies.

Accessibility at a Glance​

  • You inherit the accessibility of what you embed. A widget that traps keyboard focus, or has no labels, makes your page inaccessible, whoever wrote it.
  • Ask vendors for an accessibility statement, and test their widgets with a keyboard and a screen reader before adding them.
  • Give every frame a title.
  • Make security tools accessible: cookie banners, consent buttons, and CAPTCHAs must be usable by everyone, with an alternative for CAPTCHAs.
  • Provide a text alternative if a third-party service fails or is blocked.
  • Do not let sandboxing or blocking silently break features people rely on. Provide a link to the content.

Global Audience Notes​

  • Laws differ: consent, data transfer, and tracking rules change from region to region. A design that works in one country may not be enough in another.
  • Some services are blocked or slow in some countries. If a blocked script stops your page working, visitors there get nothing. Make third-party content optional, and give plain links.
  • Third-party embeds can make pages heavy. Many visitors are on slow or metered connections. Click-to-load and lazy loading help them most.
  • Tell visitors in their language what is loaded and why. Translate your consent text and privacy notice.
  • Data may cross borders when you use a third-party service. Know where it goes, and say so.
  • Test from different regions and networks, not only your own.

Common Mistakes to Avoid​

  1. Loading third-party scripts without knowing what they do.
  2. No Content-Security-Policy, or one so loose it does nothing (*, 'unsafe-inline').
  3. Switching on CSP without testing it in report-only mode first.
  4. Loading a CDN file with "latest" and no integrity hash.
  5. Using SRI on a file that is meant to change.
  6. Iframes with no sandbox, and no title.
  7. allow-scripts plus allow-same-origin on your own content.
  8. Granting camera, microphone, or location to frames that do not need them.
  9. Secrets in addresses, which then leak through the referrer.
  10. Loading trackers before consent.
  11. A page that breaks when a third party is slow or blocked.
  12. Never reviewing, so old widgets and forgotten scripts stay on the page for years.

Practice Time​

Try these in your editor or in your browser's developer tools:

  1. Open the network panel on a real site you made, and list every third-party host.
  2. Remove one you do not really need.
  3. Add a <meta> CSP to a test page, and see what it blocks in the console.
  4. Switch to Content-Security-Policy-Report-Only on a test server, and read a report.
  5. Add an inline script with and without 'unsafe-inline', and see the difference.
  6. Add integrity and crossorigin to a script from a CDN. Then change one character in the hash, and watch it be refused.
  7. Add sandbox to an iframe, and add back one permission at a time.
  8. Add referrerpolicy="no-referrer" to a frame, and compare the requests.
  9. Replace an auto-loading map or video with a click-to-load button.
  10. Bonus: use the checklist above on your own site, and fix the first three items you cannot tick.

Quick Recap​

Click a question to check your answer.

1. Why is a third-party script risky?

It runs with the same power as your own code. If the provider is hacked, or the file is changed, it can read what visitors type, change the page, and send data away, and your visitors will blame you.

2. What does a Content-Security-Policy do?

It tells the browser where each kind of content (scripts, frames, images, connections) may come from, and blocks everything else, even if an attacker managed to inject it.

3. Why start with report-only mode?

In Content-Security-Policy-Report-Only, the browser allows everything but reports what would have been blocked. You can fix the problems before enforcing the policy, so nothing breaks.

4. What does Subresource Integrity protect against?

A file being changed after you pinned it, for example a CDN being compromised. The page includes a hash of the expected file, and the browser refuses to run any file that does not match.

5. How do you stop other sites from framing your page?

Send Content-Security-Policy: frame-ancestors 'self' (or a list of allowed sites). The older X-Frame-Options header does a similar job.

6. How can you reduce what a third party learns about your visitors?

Use click-to-load, ask for consent first, set Referrer-Policy or referrerpolicy, limit permissions with sandbox, allow, and Permissions-Policy, and never put secrets in addresses.

Key Takeaways​

  • Every third-party script, frame, and font is a guest on your page, with real power, and your visitors will hold you responsible.
  • Defend in layers: choose well, limit what loads, check what loads, fence it in, say less, ask first, and plan for failure.
  • Use Content-Security-Policy (start in report-only mode), and avoid 'unsafe-inline' and wildcards.
  • Use Subresource Integrity with pinned versions for static libraries, and self-host when you can.
  • Use sandbox, allow, Permissions-Policy, frame-ancestors, and Referrer-Policy to restrict frames and what is shared.
  • Get consent where the law requires it, and use click-to-load for heavy or tracking embeds.
  • Make pages work without third parties, load scripts with async or defer, and keep an inventory you review regularly.
  • Treat visitors' content as untrusted too: use textContent, sanitise HTML, and show uploads safely.
  • Remember that you inherit the accessibility of everything you embed.

Congratulations, you have finished the Embedded Content section. You can now embed pages, files, graphics, and maps, draw with SVG and canvas, and keep it all safe and accessible. Return to the Embedded Content overview to review the lessons, or revisit Iframe for the full set of frame options.