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

·

3 min read


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"
/>

This component generates the following HTML code:

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

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"
>

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

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

style-src 'self' 'sha256-zlqnbDt84zf1iSefLU/ImC54isoprH/MRiVZGskwexk=' 'unsafe-hashes'

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} />;
}

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
/>

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.