mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-04 07:09:41 +00:00
LibURL: Use a nonce to distinguish opaque origins
Opaque origins are meant to be unique in terms of equality from one another. Since this uniqueness needs to be across processes, use a nonce to implement the uniqueness check.
This commit is contained in:
parent
ee8e4d1eec
commit
38765fd617
Notes:
github-actions[bot]
2025-06-25 15:48:27 +00:00
Author: https://github.com/shannonbooth
Commit: 38765fd617
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5193
Reviewed-by: https://github.com/tcl3 ✅
7 changed files with 77 additions and 24 deletions
|
@ -105,8 +105,10 @@ template<>
|
||||||
ErrorOr<URL::Origin> decode(Decoder& decoder)
|
ErrorOr<URL::Origin> decode(Decoder& decoder)
|
||||||
{
|
{
|
||||||
auto is_opaque = TRY(decoder.decode<bool>());
|
auto is_opaque = TRY(decoder.decode<bool>());
|
||||||
if (is_opaque)
|
if (is_opaque) {
|
||||||
return URL::Origin::create_opaque();
|
auto nonce = TRY(decoder.decode<URL::Origin::Nonce>());
|
||||||
|
return URL::Origin { nonce };
|
||||||
|
}
|
||||||
|
|
||||||
auto scheme = TRY(decoder.decode<Optional<String>>());
|
auto scheme = TRY(decoder.decode<Optional<String>>());
|
||||||
auto host = TRY(decoder.decode<URL::Host>());
|
auto host = TRY(decoder.decode<URL::Host>());
|
||||||
|
|
|
@ -114,6 +114,7 @@ ErrorOr<void> encode(Encoder& encoder, URL::Origin const& origin)
|
||||||
{
|
{
|
||||||
if (origin.is_opaque()) {
|
if (origin.is_opaque()) {
|
||||||
TRY(encoder.encode(true));
|
TRY(encoder.encode(true));
|
||||||
|
TRY(encoder.encode(origin.nonce()));
|
||||||
} else {
|
} else {
|
||||||
TRY(encoder.encode(false));
|
TRY(encoder.encode(false));
|
||||||
TRY(encoder.encode(origin.scheme()));
|
TRY(encoder.encode(origin.scheme()));
|
||||||
|
|
|
@ -20,5 +20,5 @@ set(SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_lib(LibURL url)
|
serenity_lib(LibURL url)
|
||||||
target_link_libraries(LibURL PRIVATE LibUnicode LibTextCodec LibRegex)
|
target_link_libraries(LibURL PRIVATE LibUnicode LibTextCodec LibRegex LibCrypto)
|
||||||
target_compile_definitions(LibURL PRIVATE ENABLE_PUBLIC_SUFFIX=$<BOOL:${ENABLE_PUBLIC_SUFFIX_DOWNLOAD}>)
|
target_compile_definitions(LibURL PRIVATE ENABLE_PUBLIC_SUFFIX=$<BOOL:${ENABLE_PUBLIC_SUFFIX_DOWNLOAD}>)
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2024-2025, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <LibCrypto/SecureRandom.h>
|
||||||
#include <LibURL/Origin.h>
|
#include <LibURL/Origin.h>
|
||||||
#include <LibURL/Parser.h>
|
#include <LibURL/Parser.h>
|
||||||
#include <LibURL/Site.h>
|
#include <LibURL/Site.h>
|
||||||
|
|
||||||
namespace URL {
|
namespace URL {
|
||||||
|
|
||||||
// FIXME: This should be generating a unique origin identifer that can be used for equality checks.
|
|
||||||
Origin Origin::create_opaque()
|
Origin Origin::create_opaque()
|
||||||
{
|
{
|
||||||
return Origin {};
|
return Origin { Crypto::get_secure_random<Nonce>() };
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/browsers.html#same-site
|
// https://html.spec.whatwg.org/multipage/browsers.html#same-site
|
||||||
|
@ -66,8 +66,14 @@ namespace AK {
|
||||||
|
|
||||||
unsigned Traits<URL::Origin>::hash(URL::Origin const& origin)
|
unsigned Traits<URL::Origin>::hash(URL::Origin const& origin)
|
||||||
{
|
{
|
||||||
if (origin.is_opaque())
|
if (origin.is_opaque()) {
|
||||||
return 0;
|
auto const& nonce = origin.nonce();
|
||||||
|
// Random data, so the first u32 is as good as hashing the entire thing.
|
||||||
|
return (static_cast<u32>(nonce[0]) << 24)
|
||||||
|
| (static_cast<u32>(nonce[1]) << 16)
|
||||||
|
| (static_cast<u32>(nonce[2]) << 8)
|
||||||
|
| (static_cast<u32>(nonce[3]));
|
||||||
|
}
|
||||||
|
|
||||||
unsigned hash = origin.scheme().value_or(String {}).hash();
|
unsigned hash = origin.scheme().value_or(String {}).hash();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -15,8 +16,17 @@ namespace URL {
|
||||||
|
|
||||||
class Origin {
|
class Origin {
|
||||||
public:
|
public:
|
||||||
|
using Nonce = Array<u8, 16>;
|
||||||
|
|
||||||
|
explicit Origin(Nonce nonce)
|
||||||
|
: m_state(move(nonce))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static Origin create_opaque();
|
||||||
|
|
||||||
Origin(Optional<String> const& scheme, Host const& host, Optional<u16> port)
|
Origin(Optional<String> const& scheme, Host const& host, Optional<u16> port)
|
||||||
: m_state(State {
|
: m_state(Tuple {
|
||||||
.scheme = scheme,
|
.scheme = scheme,
|
||||||
.host = host,
|
.host = host,
|
||||||
.port = move(port),
|
.port = move(port),
|
||||||
|
@ -24,22 +34,21 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static Origin create_opaque();
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/origin.html#concept-origin-opaque
|
// https://html.spec.whatwg.org/multipage/origin.html#concept-origin-opaque
|
||||||
bool is_opaque() const { return !m_state.has_value(); }
|
bool is_opaque() const { return m_state.has<Nonce>(); }
|
||||||
|
|
||||||
Optional<String> const& scheme() const { return m_state->scheme; }
|
Optional<String> const& scheme() const { return m_state.get<Tuple>().scheme; }
|
||||||
Host const& host() const { return m_state->host; }
|
Host const& host() const { return m_state.get<Tuple>().host; }
|
||||||
Optional<u16> port() const { return m_state->port; }
|
Optional<u16> port() const { return m_state.get<Tuple>().port; }
|
||||||
|
|
||||||
|
Nonce const& nonce() const { return m_state.get<Nonce>(); }
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/origin.html#same-origin
|
// https://html.spec.whatwg.org/multipage/origin.html#same-origin
|
||||||
bool is_same_origin(Origin const& other) const
|
bool is_same_origin(Origin const& other) const
|
||||||
{
|
{
|
||||||
// 1. If A and B are the same opaque origin, then return true.
|
// 1. If A and B are the same opaque origin, then return true.
|
||||||
// FIXME: What about opaque origins that are not equal to one another?
|
|
||||||
if (is_opaque() && other.is_opaque())
|
if (is_opaque() && other.is_opaque())
|
||||||
return true;
|
return nonce() == other.nonce();
|
||||||
|
|
||||||
// 2. If A and B are both tuple origins and their schemes, hosts, and port are identical, then return true.
|
// 2. If A and B are both tuple origins and their schemes, hosts, and port are identical, then return true.
|
||||||
if (!is_opaque() && !other.is_opaque()
|
if (!is_opaque() && !other.is_opaque()
|
||||||
|
@ -57,9 +66,8 @@ public:
|
||||||
bool is_same_origin_domain(Origin const& other) const
|
bool is_same_origin_domain(Origin const& other) const
|
||||||
{
|
{
|
||||||
// 1. If A and B are the same opaque origin, then return true.
|
// 1. If A and B are the same opaque origin, then return true.
|
||||||
// FIXME: What about opaque origins that are not equal to one another?
|
|
||||||
if (is_opaque() && other.is_opaque())
|
if (is_opaque() && other.is_opaque())
|
||||||
return true;
|
return nonce() == other.nonce();
|
||||||
|
|
||||||
// 2. If A and B are both tuple origins, run these substeps:
|
// 2. If A and B are both tuple origins, run these substeps:
|
||||||
if (!is_opaque() && !other.is_opaque()) {
|
if (!is_opaque() && !other.is_opaque()) {
|
||||||
|
@ -94,20 +102,19 @@ public:
|
||||||
// FIXME: 2. If origin's domain is non-null, then return origin's domain.
|
// FIXME: 2. If origin's domain is non-null, then return origin's domain.
|
||||||
|
|
||||||
// 3. Return origin's host.
|
// 3. Return origin's host.
|
||||||
return m_state->host;
|
return m_state.get<Tuple>().host;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(Origin const& other) const { return is_same_origin(other); }
|
bool operator==(Origin const& other) const { return is_same_origin(other); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Origin() = default;
|
struct Tuple {
|
||||||
|
|
||||||
struct State {
|
|
||||||
Optional<String> scheme;
|
Optional<String> scheme;
|
||||||
Host host;
|
Host host;
|
||||||
Optional<u16> port;
|
Optional<u16> port;
|
||||||
};
|
};
|
||||||
Optional<State> m_state;
|
|
||||||
|
Variant<Tuple, Nonce> m_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Can't access property 'thing' on cross-origin object
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<body>
|
||||||
|
<iframe id="iframeOuter"></iframe>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
asyncTest(async done => {
|
||||||
|
const iframeOuter = document.getElementById('iframeOuter');
|
||||||
|
|
||||||
|
window.addEventListener('message', (event) => {
|
||||||
|
println(event.data);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Data URLs result in the iframes having an opaque origin, resulting in cross origin access.
|
||||||
|
const iframeOuterContent = `
|
||||||
|
<iframe id="iframeInner" src="data:text/html,<p>Iframe 1 content</p>" style="width: 300px; height: 100px;"></iframe>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const iframeInner = document.getElementById('iframeInner');
|
||||||
|
|
||||||
|
iframeInner.onload = () => {
|
||||||
|
try {
|
||||||
|
iframeInner.contentWindow.parent.frames[0].thing;
|
||||||
|
} catch (e) {
|
||||||
|
window.top.postMessage(e.message, '*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<\/script>
|
||||||
|
`;
|
||||||
|
|
||||||
|
iframeOuter.src = 'data:text/html,' + encodeURIComponent(iframeOuterContent);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
Loading…
Add table
Add a link
Reference in a new issue