ordinals / ord

👁‍🗨 Rare and exotic sats
https://ordinals.com
Creative Commons Zero v1.0 Universal
3.82k stars 1.35k forks source link

Recursive Ordinals are blocked and not functional for display on Safari and Safari & Chrome & Imbeded browsers on iOS #2229

Closed Xen0ph0n closed 10 months ago

Xen0ph0n commented 1 year ago

Due to incompatibility with Safari and apples CSP policy recursive ordinals do not function at all on roughly 40% of the mobile market. This is a hard block to which we've been unable to find a successful work around.

This issue was mentioned prior to recursion release with regards to Safari (OSX) however this is the case in all browsers on iOS which makes up the a good portion of internet traffic globally, albiet currently there are few ordinal wallets or websites geared towards mobile.

This means all the recursive ordinals sold thus far, or in the future utilizing the current protocol standards may/will fail to render as expected for the majority of owners or platforms.

Example:

https://ordinals.com/content/821006d94ecbe675dcb6bcb911a6e55c0fc3015f57b64ef366c84899ff8a7a20i0 Refused to load because it appears in neither the img-src directive nor the default-src directive of the Content Security Policy.

We have tested this on multiple recursive ordinals from both ordinals.com and the magiceden content mirror during testing of recursion for project and artist usage.

Examples of testing include basic recursion such as recursive frogs: (Simply layering) https://ord-mirror.magiceden.dev/content/3e0838c4e814f7ed88ef932e09ae56a5b6627dbfed89ee8ba160edcdb527b303i0

And more complex which pulls in libraries to display images: (rotating 3d gallery) https://ordinals.com/content/0829ab0e3a096e37604a52a08acbf343146fe4daa4469f54408a19e10e5a5917i0

Images below.. note this is the case on ordinals.com and magiceden and other sites which implement protocol correct security policies.

Given the issue with CSPs was presented prior to release, this is likely not news with regards to safari incompatibility, but iOS broadly renders recursion non operable for the majority of users, and projects selling or users buying recursive ordinals need to understand these current limitations.

Apples CSP policy is not likely to change, and is secure, if anything it is likely to propagate to other browsers over time.

![IMG_7AC4C94C7D78-1](https://github.com/ordinals/ord/assets/2313682/b3cbd193-e95b-41f0-a99d-13d44145412e) ![IMG_0044](https://github.com/ordinals/ord/assets/2313682/a9f10d50-a92d-4fe4-ae67-ff66eeb4e6c7) ![IMG_0045](https://github.com/ordinals/ord/assets/2313682/254bbbf1-f896-4fe9-ab0b-abc87c28686f) ![IMG_0046](https://github.com/ordinals/ord/assets/2313682/1a810d19-22c2-4296-9674-e61cd7e3d6e5) ![IMG_0048](https://github.com/ordinals/ord/assets/2313682/751b6280-5706-4ff3-a270-6f6bae0af36d)
bodily11 commented 1 year ago

We've done VERY extensive testing with our BitGen standard that uses recursion, and it renders correctly in all major browsers with our local testing so far (Chrome, Safari, Firefox). We haven't tested as actual inscriptions yet though--this is the next step, but it should work.

You can view how we are doing it here: https://bioniq-1.gitbook.io/bitgen-bitcoin-generative-ordinals-standard-alpha/

Does this solve the problem?

Xen0ph0n commented 1 year ago

Hi @bodily11 , looking through your docs... and also BTW that standard and toolset looks amazing! Great work :)

I believe it will break when inscribed in a protocol correct display, I could be wrong but the call to /content/ without the FQDN matching is what creates the CSP blockage.

All of what you have after that is really cool! I can't wait to read more, and maybe use! We're working on similar gen/art centric tools :)


As for noodling on some solution's with minimal changes, I think there's nothing wrong I with leaving the existing recursive endpoints in place /content/ /blockhash/ etc, but the CSP policy will disalow their inclusion as mentioned above.

So existing inscribed recursive ordinals will break on ios/safari and in the future possibly more DOMs. (apples CSP policy is suprior so unlikely there would be movement in the other direction from chrome or ffx etc)

--

The easiest way I can think to possibly fix… maybe.. would be to explicitly allow only hard coded https://ordinals.com/content/ in recursion as well as the existing endpoints.... I believe this would work universally but have no way to test given the existing standard disalows this (for good reasons of being able to cache/mirrior and point to local files on marketplaces etc)

If that was done (FQDN) capacity to cache or mirror anything would be limited severely, and we go back to the DDoS concern, this would be more workable of ords had $$$$$$ prob 🤔

Protocol should maybe find a way to get some small optional dust from each inscription, as markeplaces are profiting readily as are projects and wallet / inscription platforms ?

I am not sure if the iframe display of galleries/other sites etc would puke at that double external ref but it would unify the load and so would cost more money for the ord team to display.

More over it’s a bigger problem going forward than it is now, right now it’s more of a decision point regarding future visibility and compatibility with a protocol update. As a creator, I don't feel comfortable selling something which displays only on a portion of devices and browsers, so need to find a solution before we can deploy recursive tools or content.

The existing pointers don’t hurt to leave in, just need to figure a CSP compliant way to do it, while also excluding external malicious includes (arbitary files, libraries, cross site scripting scams etc) which would happen if random FQDNs are allowed.

Note this doesn't impact the inscriptions on any of the individual ordinals that might be used in recursion, (e.g. 5 jpgs being used to compose an image) just the HTML composition piece.

Pure (self contained / pre recursion) html ordinals display perfectly in all browsers/devices. But are very expensive etc.

bodily11 commented 1 year ago

I think we've finally figured out a fix here for ordinals.com and iOS/Safari.

Currently the CSP is configured as follows: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob:

Instead, it should be changed to the following: default-src 'unsafe-eval' 'unsafe-inline' 'self' data: blob:

Turns out that moving 'self' after the 'unsafe-eval' and 'unsafe-inline' allows recursive inscriptions to render on iOS and Safari.

I'll submit a PR now and I'll link back to this thread.

bodily11 commented 1 year ago

PR now submitted #2236.

Xen0ph0n commented 1 year ago

Great work! I think this is correct logically, as CSP's can only progressively reduce scope, so starting with unsafe-eval (which is exactly as it sounds) would enable broader scope, it does need to be assessed from a security perspective first. I believe this context would enable/allow the self referential includes, but I'm not sure if it would ALSO allow for unsafe arbitary external includes. (I'm not an adequate solution for DOM security analysis :D)

after you check the PR please check to validate that it will not enable arbitrary includes, which was the purpose of restricting ordinals to "self" first to begin with. (e.g. flipping the order to be permissive may be overly so)

This is a great step in the right direction!!

As I see it for ords to be secure we need to ensure no non inscribed content can be referenced or included, and for them to be performant and display securely we need to ensure inscribed content can be presented. Which presents difficulty, non trivial challenge given the extensive array of attack vectors for XSS and the vulnerability of browser extentions and users if we can pass through arbitrary data.

The other recursive endpoints outside of /content/ present caching and injection issues, but those are minimal compared to ensuring display of recursive ords is successful on the majority of 1st world mobile devices!

Awesome work!

bodily11 commented 1 year ago

Yeah, we've tested and so far haven't found any vulnerabilities to cross-site scripting attacks. So I think the CSP still works as intended here, meaning blocks any external scripts, while allowing for all self-referring images/scripts/content to be loaded.

So based on our testing, I think this is a working solution that maintains existing functionality and preserves security. But would love for others to do testing as well.

Xen0ph0n commented 1 year ago

That would be AMAZING!

devlz303 commented 1 year ago

I think the solution is to use one CSP header instead of two, because Safari only uses the first CSP header it encounters in the response. The fix for this is in this pull request https://github.com/ordinals/ord/pull/2251

casey commented 1 year ago

I've debugged this a little bit, and the issue is not that Safari doesn't support multiple CSP headers, it's that Safari does not support wildcards, i.e. *, in CSP headers. So the second CSP header:

Content-Security-Policy: default-src *:*/content/ *:*/blockheight *:*/blockhash *:*/blockhash/ *:*/blocktime 'unsafe-eval' 'unsafe-inline' data: blob:

Which is used to restrict requests to recursive endpoints only, fails.

Wildcards are part of the CSP spec, see this section of the CSP spec, so this is a problem with Safari.

Some possible fixes, none of them great:

  1. Omit the wildcard CSP header when the user agent is Safari. This would allow requests to any endpoint at the current origin, which isn't ideal.
  2. Use a single CSP header which avoids wildcards, by using the full domain, e.g.: Content-Security-Policy: default-src https://ordinals.com/content/ https://ordinals.com/blockheight https://ordinals.com/blockhash https://ordinals.com/blockhash/ https://ordinals.com/blocktime 'unsafe-eval' 'unsafe-inline' data: blob:. We'd have to use the domain passed to --acme-domain, or add another flag to provide the domain name to use in CSP headers.
devlz303 commented 1 year ago

Okay, so I made the wrong assumption about Safari ignoring the second CSP header. Of the two possible solutions I think the second is best.

matttturnbull commented 1 year ago

Is this still active? Seems like the second PR was closed but the issue still remains for many Ordinals currently.

casey commented 1 year ago

This is still an issue. The closed PR didn't actually fix the problem.

bodily11 commented 1 year ago

@casey I think the best way to solve this is to load the parent site at domain A, and then load the inscriptions in an iframe from a separate domain, let's call it domain B. Domain B could even be a subdomain of domain A.

When you load inscriptions from a separate domain you cannot access anything from the parent domain. Meaning domain A (top level site with wallets/login) is safe from any malicious scripts loaded from domain B.

This also means you don't have to put anything restrictive as CSP because you are loading inscriptions from a separate domain so you don't really care what the inscriptions can access. You don't need to add sandbox (or any other restrictions) in the iframe and you shouldn't have to whitelist specific endpoints.

The final beauty of this setup is it works on webkit browsers, so Safari and iPhone load recursive inscriptions just fine when using this setup.

What do you think?

casey commented 1 year ago

It would be unfortunate if users were required to host ord on multiple domains. The underlying issue, iirc, is that Safari does not support wildcards in content-security-policy headers. Currently, we use the following headers:

content-security-policy: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob:
content-security-policy: default-src *:*/content/ *:*/blockheight *:*/blockhash *:*/blockhash/ *:*/blocktime 'unsafe-eval' 'unsafe-inline' data: blob:

We can avoid using wildcards if we use the domain instead. For example, for ordinals.com:

content-security-policy: default-src https://ordinals.com/content/ https://ordinals.com/blockheight http://ordinals.com/blockhash http://ordinals.com/blockhash/ http://ordinals.com/blocktime 'unsafe-eval' 'unsafe-inline' data: blob:

So the ideal solution would be to add an additional flag to ord server, --content-security-policy-origin <ORIGIN>. If it isn't passed, we set the content security policy header as we've been doing, if it is passed we set the header as:

content-security-policy: default-src ORIGIN/content/ ORIGIN/blockheight ORIGIN/blockhash ORIGIN/blockhash/ ORIGIN/blocktime 'unsafe-eval' 'unsafe-inline' data: blob:

(Note that in the case where we know the CSP origin, we only need one CSP header, since using two headers, the first with 'self' is actually a workaround for not knowing the CSP origin.)