Skip to content
New issue

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

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

Already on GitHub? # to your account

Optimised font isn't listed in CSP #68

Closed
Shamilik opened this issue Aug 3, 2022 · 5 comments
Closed

Optimised font isn't listed in CSP #68

Shamilik opened this issue Aug 3, 2022 · 5 comments

Comments

@Shamilik
Copy link

Shamilik commented Aug 3, 2022

Optimised font isn't listed in CSP in Safari & Chrome

https://nextjs.org/docs/basic-features/font-optimization
Screenshot 2022-08-03 at 11 12 20
Also there is some problem with scripts, but I still didn't realise what exactly.

@Shamilik
Copy link
Author

Shamilik commented Aug 3, 2022

Screenshot 2022-08-03 at 11 21 52

@nibtime
Copy link
Owner

nibtime commented Aug 4, 2022

Hi @Shamilik

On the failed scripts

Is the respective route where the load fails with getServerSideProps or getInitialProps?

If so, you need to wrap the exported data fetching function with gsspWithNonce or gipWithNonce respectively (https://next-safe-middleware.vercel.app/guides/strict-csp-configuration). That is new since version 0.9.0 as there has been some internal change that makes it impossible to reliably tell apart getServerSideProps from getStaticProps + revalidate (ISR) by DocumentContext. It is an extra step to remember, but it's unavoidable to not break the actual security feature + it will be robust towards future changes. and the past.

If not, this is super strange. With the new proxy loading mode, the actual scripts wouldn't even be in the DOM in case of a missing hash in CSP. So if the proxy loader can execute, but the inserted scripts get blocked, that would violate the transitive trust semantics of strict-dynamic and a browser bug.

An inline proxy loader looks like this:
<script id="Yedj62rQfq60uPH64PMXT3QyQOhTx9o6R8Xh7QgjDw8=" integrity="sha256-b0G+uhdQVb4QIV+2wcWxkz/zpRluWegXaRWsuzRpIao=">(function () { 
  var s0 = document.createElement('script');
  s0.async=false;s0.crossOrigin='anonymous';s0.defer=true;s0.src='/_next/static/chunks/webpack-fdfc059437936d2e.js';
  var s1 = document.createElement('script');
  s1.async=false;s1.crossOrigin='anonymous';s1.defer=true;s1.src='/_next/static/chunks/framework-8e84920140c7484e.js';
  var s2 = document.createElement('script');
  s2.async=false;s2.crossOrigin='anonymous';s2.defer=true;s2.src='/_next/static/chunks/main-493ad903cf8fb0b9.js';
  var s3 = document.createElement('script');
  s3.async=false;s3.crossOrigin='anonymous';s3.defer=true;s3.src='/_next/static/chunks/pages/_app-7fb09efe43f5ca1a.js';
  var s4 = document.createElement('script');
  s4.async=false;s4.crossOrigin='anonymous';s4.defer=true;s4.src='/_next/static/chunks/618-96dee7e359cd4890.js';
  var s5 = document.createElement('script');
  s5.async=false;s5.crossOrigin='anonymous';s5.defer=true;s5.src='/_next/static/chunks/571-4efa2b5b3651989b.js';
  var s6 = document.createElement('script');
  s6.async=false;s6.crossOrigin='anonymous';s6.defer=true;s6.src='/_next/static/chunks/pages/index-1879bc8b71df0035.js';
  var s7 = document.createElement('script');
  s7.async=false;s7.crossOrigin='anonymous';s7.defer=true;s7.src='/_next/static/Z4vsrxjeHVcl6pZc5u9PF/_buildManifest.js';
  var s8 = document.createElement('script');
  s8.async=false;s8.crossOrigin='anonymous';s8.defer=true;s8.src='/_next/static/Z4vsrxjeHVcl6pZc5u9PF/_ssgManifest.js';
  var s = [s0,s1,s2,s3,s4,s5,s6,s7,s8];
  var self = document.getElementById('Yedj62rQfq60uPH64PMXT3QyQOhTx9o6R8Xh7QgjDw8=');
  var p = self.parentNode;
  p.removeChild(self);
  s.forEach(function(si) {
    p.appendChild(si);
  });
})()
</script>

Do the script problems on your site also occur with other browsers (Chrome, Firefox ...)?

Workaround for the font optimization problem

The output of Next font optimization can't be picked up during SSR of _document, it happens sometimes after.

Fortunately, there is a simple workaround:

// Output of Next.js font optimization for Rubik from Google Fonts
// the generated inline style can't captured by this lib.
// But can easily be copied from output and added as inline style
// URLs seem to be stable: https://stackoverflow.com/questions/47638772/do-google-gstatic-font-urls-change
const InlinedGoogleFontRubik = `@font-face{font-family:'Rubik';font-style:italic;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWbBXyIfDnIV7nEt3KSJbVDV49rz8sDE0Uz.woff) format('woff')}@font-face{font-family:'Rubik';font-style:italic;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWbBXyIfDnIV7nEt3KSJbVDV49rz8tdE0Uz.woff) format('woff')}@font-face{font-family:'Rubik';font-style:italic;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWbBXyIfDnIV7nEt3KSJbVDV49rz8tvE0Uz.woff) format('woff')}@font-face{font-family:'Rubik';font-style:italic;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWbBXyIfDnIV7nEt3KSJbVDV49rz8uDFEUz.woff) format('woff')}@font-face{font-family:'Rubik';font-style:italic;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWbBXyIfDnIV7nEt3KSJbVDV49rz8u6FEUz.woff) format('woff')}@font-face{font-family:'Rubik';font-style:italic;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWbBXyIfDnIV7nEt3KSJbVDV49rz8vdFEUz.woff) format('woff')}@font-face{font-family:'Rubik';font-style:normal;font-weight:300;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-WYi1Uw.woff) format('woff')}@font-face{font-family:'Rubik';font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-B4i1Uw.woff) format('woff')}@font-face{font-family:'Rubik';font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-NYi1Uw.woff) format('woff')}@font-face{font-family:'Rubik';font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-2Y-1Uw.woff) format('woff')}@font-face{font-family:'Rubik';font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-4I-1Uw.woff) format('woff')}@font-face{font-family:'Rubik';font-style:normal;font-weight:800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-h4-1Uw.woff) format('woff')}@font-face{font-family:'Rubik';font-style:italic;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWEBXyIfDnIV7nEnXO61E_c5IhGzg.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Rubik';font-style:italic;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWEBXyIfDnIV7nEnXq61E_c5IhGzg.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Rubik';font-style:italic;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWEBXyIfDnIV7nEnXy61E_c5IhGzg.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Rubik';font-style:italic;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWEBXyIfDnIV7nEnXC61E_c5IhGzg.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Rubik';font-style:italic;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWEBXyIfDnIV7nEnX661E_c5Ig.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Rubik';font-style:normal;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWKBXyIfDnIV7nMrXyw023e1Ik.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Rubik';font-style:normal;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWKBXyIfDnIV7nFrXyw023e1Ik.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Rubik';font-style:normal;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWKBXyIfDnIV7nDrXyw023e1Ik.woff2) format('woff2');unicode-range:U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F}@font-face{font-family:'Rubik';font-style:normal;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWKBXyIfDnIV7nPrXyw023e1Ik.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Rubik';font-style:normal;font-weight:300 800;font-display:swap;src:url(https://fonts.gstatic.com/s/rubik/v20/iJWKBXyIfDnIV7nBrXyw023e.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}`;

<Head>
<link rel="preconnect" href="https://fonts.gstatic.com" />
{/* this will work strict CSP */}
<script>{`console.log('Hello from _document/Head, I get nonced/hashed there')`}</script>
<style
id="inter-font"
dangerouslySetInnerHTML={{ __html: SelfHostedInlineFontInterVar }}
/>
<style
id="rubik-font"
dangerouslySetInnerHTML={{ __html: InlinedGoogleFontRubik }}
/>
{/* Stitches CSS-in-JS SSR */}
<style
id="stitches"
dangerouslySetInnerHTML={{
__html: lazyGetCssText(this.props.__NEXT_DATA__.page),
}}
/>
</Head>

I will also draft a bug template with a checklist tailored to Next+CSP-related bugs.

@nibtime
Copy link
Owner

nibtime commented Aug 5, 2022

I am closing this, as the font optimization thing can't be handled automatically and needs the workaround

For the script issues: I am going to release 0.10.1 now (#70). I fixed some bugs with middleware that could break CSP, but those happened on Vercel only.

I also recorded a basic click-through test of the e2e app with Safari+strict-dynamic on a iPad Pro Device (#69 (comment)) and couldn't spot any issue there, also not in the docs project.

@nibtime nibtime closed this as completed Aug 5, 2022
@bartoszhernas
Copy link

Can this be added in big red letters in documentation? I spent hours searching why I am getting inline style errors, only to discover the fonts are auto-inlined.

Do you pass nonce to (as nextjs recommends)? If yes, then the issue is on nextjs team, as they should be passing this down to inlined style

@bartoszhernas
Copy link

You can disable font-optimization by adding optimizeFonts: false, to nextjs.config
https://nextjs.org/docs/basic-features/font-optimization

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants