DEV Community

0xdbe
0xdbe

Posted on • Originally published at 0xdbe.github.io

Next.js: consequence of Next/Image on your CSP

The Next/Image component is a crucial part of the Next.js framework, offering image optimization functionalities.
However, utilizing this component can have significant implications for Content Security Policy (CSP).

This article is documented as an ADR (Architectural Decision Record) because, in my perspective, this represents a crucial security decision.
This includes the context of how the decision was made and the consequences of adopting it
Feel free to share this ADR with your team members if you find it beneficial.

Issue

To understand the implications of Next/Image, let's examine the following example extracted from the default Next.js template:

<Image
  className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
  src="/next.svg"
  alt="Next.js Logo"
/>
Enter fullscreen mode Exit fullscreen mode

This component generates the following HTML code:

<img
  alt="Next.js Logo"
  style="color:transparent"
  src="/next.svg"
>
Enter fullscreen mode Exit fullscreen mode

As observed, the outcome contains an unsafe inline style used to conceal the alt text.

There are another properties translated into inline style by Next/Image.
For example, fill property is translated by:

<img
  style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;object-fit:contain;color:transparent"
>
Enter fullscreen mode Exit fullscreen mode

Considered Options

Unsafe Inline

The simplest approach is to allow the unsafe-inline keyword for style-src directive.
However, this leaves you vulnerable to CSS injection attacks.

Unsafe Hashes

Since styles for images are predetermined, we can pre-compute hashes for styles:

echo -n "color:transparent" | openssl dgst -sha256 -binary | openssl base64 -A
Enter fullscreen mode Exit fullscreen mode

Subsequently, you can append the following content to your CSP:

style-src 'self' 'sha256-zlqnbDt84zf1iSefLU/ImC54isoprH/MRiVZGskwexk=' 'unsafe-hashes'
Enter fullscreen mode Exit fullscreen mode

Unfortunately, the unsafe-hashes keyword is necessary to permit hashes for the style attribute.

Remove Style

Even without utilizing any properties that generate inline style, there's only one persistent style attribute that you can't eliminate: color:transparent.

To remove the style attribute, it is possible to create a new component called ImageNoStyle, which omits the style attribute:

// ImageNoStyle.tsx
import NextImage, { getImageProps } from 'next/image';
import { ComponentProps } from 'react';

export default function ImageNoStyle(props: ComponentProps<typeof NextImage>) {
  const { props: nextProps } = getImageProps({
    ...props,
  });

  const { style: _omit, ...delegated } = nextProps;

  return <img {...delegated} />;
}
Enter fullscreen mode Exit fullscreen mode

This component uses getImageProps(), available from Next.js 14.1, to generate image properties and omit the style attribute.
Then, ImageNoStyle can be used like this with the same properties as NextImage:

<ImageNoStyle
  className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
  src="/next.svg"
  alt="Next.js Logo"
  width={180}
  height={37}
  priority
/>
Enter fullscreen mode Exit fullscreen mode

However, it's worth noting that this workaround removes inline style for images.
Consequently, this workaround can modify rendering of an existing website.

HTML Tag

The basic HTML <img> tag can be used to embed an image in react component.

Decision

Pending a resolution for issues 61388 and 45184, it's advisable to refrain from using Next/Image.
Employing the <img> HTML tag helps maintain a strict CSP (Content Security Policy).

This decision is relevant to a new application.
For existing applications, the choice may differ, particularly if image optimization is already in use.

Consequence

Opting for the basic <img> HTML tag means foregoing the image optimization features provided by Next.js, as outlined in the image optimization documentation.

Links

Top comments (0)