<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Marek Elmayan</title>
    <description>The latest articles on Forem by Marek Elmayan (@mareke).</description>
    <link>https://forem.com/mareke</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1209578%2F155d9b04-315e-45c6-b357-54102284dcc1.jpeg</url>
      <title>Forem: Marek Elmayan</title>
      <link>https://forem.com/mareke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mareke"/>
    <language>en</language>
    <item>
      <title>How to configure CORS for Next.js</title>
      <dc:creator>Marek Elmayan</dc:creator>
      <pubDate>Wed, 29 Jan 2025 13:17:18 +0000</pubDate>
      <link>https://forem.com/theodo/how-to-configure-cors-for-nextjs-5ek2</link>
      <guid>https://forem.com/theodo/how-to-configure-cors-for-nextjs-5ek2</guid>
      <description>&lt;p&gt;Hey 👋,&lt;/p&gt;

&lt;p&gt;I've been working on a new package called &lt;a href="https://github.com/marek-e/next-armored" rel="noopener noreferrer"&gt;next-armored&lt;/a&gt; that &lt;strong&gt;helps you secure your Next.js application&lt;/strong&gt;. My first release is out, and I'm excited to share it with you. The &lt;strong&gt;first tool&lt;/strong&gt; I added is &lt;strong&gt;a middleware to help you configure CORS&lt;/strong&gt; for your Next.js server.&lt;/p&gt;

&lt;p&gt;If you're only interested in how to use it, and how to set it up, you can directly go check the 👉 tutorial section 👈.&lt;/p&gt;

&lt;p&gt;Otherwise, let's start with a quick reminder of what &lt;strong&gt;Cross-Origin Resource Sharing (CORS)&lt;/strong&gt; is and when you need to set it up in your &lt;strong&gt;Next.js app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marek-e/next-armored" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;Drop a star on Github ⭐️&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/next-armored" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;Check on NPM 📦&lt;/a&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 I - What is the CORS policy ?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cross-Origin Resource Sharing a web browser security feature
&lt;/h3&gt;

&lt;p&gt;CORS is a security feature that allows you to control which origins are allowed to access your resources. It is &lt;strong&gt;enforced by web browsers&lt;/strong&gt; through &lt;code&gt;Access-control-allow-*&lt;/code&gt; headers.&lt;/p&gt;

&lt;p&gt;CORS protects users of your app from malicious sites trying to perform operations (like retrieving personal information or account deletion) on your server without their knowledge. While the request will still reach your server, if it is correctly configured, it will attach CORS security headers based on the request’s origin.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the request comes from your allowed origins, the browser will allow access to the server’s response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the request originates from a disallowed or malicious site, the browser will block access to the response.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, in the simplest case of a GET request, an attacker could try to retrieve information. For other methods, such as DELETE, the browser sends a preflight request first. Based on the server's CORS configuration, the browser decides whether to proceed with the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrxpvuvkhpiqfce95gcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrxpvuvkhpiqfce95gcq.png" alt="CORS explained with a schema" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more in-depth information, check this &lt;a href="https://blog.theodo.com/2024/07/mental-model-of-cors/" rel="noopener noreferrer"&gt;article by a friend of mine&lt;/a&gt; or the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;MDN Documentation&lt;/a&gt;.&lt;br&gt;
For insights into CORS vulnerabilities, explore &lt;a href="https://outpost24.com/blog/exploiting-permissive-cors-configurations/" rel="noopener noreferrer"&gt;Outpost24’s blog&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ❓ What is an origin, and how do you differentiate them?
&lt;/h3&gt;

&lt;p&gt;An origin is defined as a combination of the &lt;strong&gt;protocol, host, and port&lt;/strong&gt; of a resource. For example, the origin of &lt;code&gt;https://example.com/index.html&lt;/code&gt; is &lt;code&gt;https://example.com&lt;/code&gt;. A change in any of these three components results in a different origin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh75nuejuc1yn4jm4mf96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh75nuejuc1yn4jm4mf96.png" alt="Origin explained with a schema" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚡️ Subdomains change the host, so &lt;code&gt;https://example.com&lt;/code&gt; and &lt;code&gt;https://api.example.com&lt;/code&gt; are considered different origins.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🛠️ II - When should you configure CORS for your Next.js application?
&lt;/h2&gt;

&lt;p&gt;We saw that &lt;code&gt;https://example.com&lt;/code&gt; and &lt;code&gt;https://api.example.com&lt;/code&gt; are different origins. So if your server is at &lt;code&gt;https://api.example.com&lt;/code&gt; but your frontend app at &lt;code&gt;https://example.com&lt;/code&gt;, you need to configure CORS on your server to allow requests from your frontend.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why is that?&lt;/em&gt; Historically, browsers enforce the &lt;strong&gt;Same Origin Policy (SOP)&lt;/strong&gt;, allowing requests only between the same origin for security. This policy’s limitations necessitated the introduction of CORS to enable controlled cross-origin requests, such as inter-operability between different subdomains or public APIs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now, what about Next.js ?&lt;/em&gt; If you use Next.js 13 or later with the app router (e.g., a folder like ./src/app/api), your frontend (e.g., &lt;a href="https://your-domain.com" rel="noopener noreferrer"&gt;https://your-domain.com&lt;/a&gt;) and API (e.g., &lt;a href="https://your-domain.com/api" rel="noopener noreferrer"&gt;https://your-domain.com/api&lt;/a&gt;) share the same origin. Here, the SOP suffices, and enabling CORS without a valid reason could introduce vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So, when should you enable CORS?&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If your API is accessed by another frontend&lt;/strong&gt; (e.g., &lt;code&gt;https://admin.your-domain.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your API is public&lt;/strong&gt;, allowing access from any origin (ensure the API doesn’t handle sensitive data).&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  &lt;a&gt;&lt;/a&gt; 👨🏼‍💻 III - How to configure CORS for Next.js with next-armored
&lt;/h2&gt;
&lt;h3&gt;
  
  
  📦 Install the next-armored package
&lt;/h3&gt;

&lt;p&gt;Select your favorite package manager (e.g. pnpm) and install the &lt;code&gt;next-armored&lt;/code&gt; package by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add next-armored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🥇 Use the CORS middleware as your first middleware
&lt;/h3&gt;

&lt;p&gt;Create or update your &lt;code&gt;./src/middleware.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCorsMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-armored/cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCorsMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;corsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/:path*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Next.js, the middleware is executed before every request matching the &lt;code&gt;matcher&lt;/code&gt; pattern inside the config object.&lt;br&gt;
For this example, I will suppose that you just created the middleware file. So you just need to match all your api routes.&lt;br&gt;
If you are using the app router, you can match all your api routes by using the &lt;code&gt;'/api/:path*'&lt;/code&gt; pattern.&lt;br&gt;
Then import the &lt;code&gt;createCorsMiddleware&lt;/code&gt; function from &lt;code&gt;next-armored/cors&lt;/code&gt; and pass your configuration object to it. This will allow you to build the cors middleware with the origins you want to allow and pass it to the middleware function.&lt;/p&gt;
&lt;h3&gt;
  
  
  🎼 Compose your CORS middleware with other middleware
&lt;/h3&gt;

&lt;p&gt;If you already have middleware, compose them as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCorsMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-armored/cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCorsMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otherMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;otherMiddleware&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;otherMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isApi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;corsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/:path*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/home/:path*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your matcher didn't previously include the api routes, you will have to add it to the matcher.&lt;br&gt;
Then you can call the other middleware first and get the response.&lt;br&gt;
Since the cors middleware should be applied only to the api routes, you have to check if the request is an api request and then apply the cors middleware only in that case. This can be done easily by checking the pathname of the request with &lt;code&gt;request.nextUrl.pathname.startsWith('/api')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, pass the response to the cors middleware as the 2nd argument. In this case, the cors middleware will attach the headers to the existing response instead of returning a new response.&lt;/p&gt;



&lt;p&gt;Alternatively, use a utility to chain middleware. Here is a snippet of how you can do it but it shall be adapted to the need of each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CustomMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextFetchEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;NextMiddlewareResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MiddlewareFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomMiddleware&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;CustomMiddleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chainMiddlewares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MiddlewareFactory&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;CustomMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chainMiddlewares&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;functions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;_event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextFetchEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;middleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chainMiddleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;middleware1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;middleware2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;corsMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚙️ Full CORS middleware configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCorsMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;optionsSuccessStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;allowCredentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;exposedHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;preflightContinue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to at least specify the &lt;code&gt;origins&lt;/code&gt; option because keeping &lt;code&gt;*&lt;/code&gt; by default is not recommended as it can be a security risk. If you are building a public api, you can allow all origins by passing &lt;code&gt;origins: ['*']&lt;/code&gt; but you should be aware of the security implications.&lt;/p&gt;

&lt;p&gt;You can define other options as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;origins&lt;/code&gt; - array of origins that are allowed to access the resource. The only one to be required.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods&lt;/code&gt; - array of methods that are allowed to access the resource. Default value to &lt;code&gt;["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;headers&lt;/code&gt; - array of headers that are allowed to access the resource. Default value to &lt;code&gt;["Content-Type", "Authorization"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maxAge&lt;/code&gt; - number of seconds that the results of a preflight request can be cached. Default value to &lt;code&gt;60 * 60 * 24&lt;/code&gt; (1 day).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;optionsSuccessStatus&lt;/code&gt; - status code to send for successful OPTIONS requests. Default value to &lt;code&gt;204&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allowCredentials&lt;/code&gt; - boolean value to indicate if credentials are allowed to be sent with the request. Default value to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exposedHeaders&lt;/code&gt; - array of headers that are exposed to the client. Default value to &lt;code&gt;[]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;preflightContinue&lt;/code&gt; - boolean value to indicate if it should pass the CORS preflight response to the next handler. Default value to &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚫 Enable/disable CORS for specific paths
&lt;/h3&gt;

&lt;p&gt;Enable CORS for specific paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCorsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/v2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;additionalIncludes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This will enable CORS for all paths that start with &lt;code&gt;/api/v2&lt;/code&gt; and at the same time include &lt;code&gt;example&lt;/code&gt; in the pathname.&lt;br&gt;
&lt;code&gt;api/v2/product/example&lt;/code&gt; will have CORS enabled, but &lt;code&gt;api/v1/product/example/test&lt;/code&gt; will not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Disable CORS for specific paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;corsMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCorsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;excludes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/restricted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In this case, all the routes starting with &lt;code&gt;/api/restricted&lt;/code&gt; will not enforce CORS but the SOP (Same-Origin Policy) as the default behavior suggests.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔮 IV - Why use &lt;code&gt;next-armored&lt;/code&gt;?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🚀 Easy to use&lt;/strong&gt;: Create the middleware with few lines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🔧 Flexible&lt;/strong&gt;: Customize middleware configurations to fit your needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🛡️ Secure&lt;/strong&gt;: Default configurations are based on best practices and security standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;✅ Type-safe&lt;/strong&gt;: Middleware is fully typed and compatible with Next.js and TypeScript. Also it's tree-shakable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🌐 Open source&lt;/strong&gt;: Audit, report issues, and contribute.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔎 V - Next steps for &lt;code&gt;next-armored&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I started with a cors middleware for Next.js but my goal is to create a bunch of utils to help you secure your Next.js application. So I definitely plan to add more utils and more middleware to this package.&lt;br&gt;
The next one will probably be a middleware to handle Content Security Policy (CSP) headers. But if you have any suggestion, or specific needs, please let me know. 😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marek-e/next-armored" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;Drop a star on Github ⭐️&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/next-armored" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;Check on NPM 📦&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>nextjs</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Mastering File Upload Security: DoS and Antivirus</title>
      <dc:creator>Marek Elmayan</dc:creator>
      <pubDate>Wed, 20 Mar 2024 13:45:00 +0000</pubDate>
      <link>https://forem.com/theodo/mastering-file-upload-security-dos-and-antivirus-1me9</link>
      <guid>https://forem.com/theodo/mastering-file-upload-security-dos-and-antivirus-1me9</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mitigate Denial of Service (DoS) threats by imposing restrictions on both the size of files and the quantity of uploads permitted on your server.&lt;/li&gt;
&lt;li&gt;Implement antivirus scanning for files that users download, ensuring protection against malware infections.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  File Upload vulnerabilities and security challenges
&lt;/h2&gt;

&lt;p&gt;Incorporating a file upload feature into your website introduces a spectrum of vulnerabilities that can compromise not only your servers but also the integrity and safety of your users' data.&lt;/p&gt;

&lt;p&gt;This article aims to shed light on the critical security challenges associated with file uploads and offers a roadmap to safeguard your digital infrastructure against malicious attacks. We'll explore strategies to prevent attackers from exploiting file upload functionalities to overload and destabilize your servers. Additionally, considering the risks posed when users download files uploaded by others, we emphasize the importance of integrating robust antivirus scanning processes.&lt;/p&gt;

&lt;h2 id="DoS-attacks"&gt;⚡️ Denial of Service (DoS) attacks&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Denial of Service (DoS)&lt;/strong&gt; attack is a malicious attempt to &lt;strong&gt;disrupt&lt;/strong&gt; the normal &lt;strong&gt;functioning&lt;/strong&gt; of a network, service, website, or computer system by overwhelming it with a &lt;strong&gt;flood&lt;/strong&gt; of traffic or requests. The primary objective of a DoS attack is to make a target system or network unavailable to its users, thereby denying access to &lt;strong&gt;legitimate&lt;/strong&gt; users. In our case, uploading specific files can result in service disruptions for &lt;strong&gt;unsafe&lt;/strong&gt; server &lt;strong&gt;configurations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Considering a file upload stream what are the possibilities to block the traffic?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Voluminous File Upload:&lt;/strong&gt; This vulnerability occurs when an attacker uploads an extremely large file, often much larger than what the system is designed to handle. Such a file can consume all available server resources, such as disk space, memory, and processing power, causing the system to slow down or even crash.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxin6nnxo45tr9usdl4zu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxin6nnxo45tr9usdl4zu.png" alt="An illustration of a cybersecurity attack: a hooded hacker uses a laptop, sending a large file marked 'DANGER' with an upload arrow to a server, where a red lightning bolt signifies a breach." width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Small File Upload:&lt;/strong&gt; In this scenario, an attacker uploads a large number of relatively small files. While individual files may not be large, the cumulative impact can lead to resource depletion or storage space exhaustion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrrqj9weydeo025vias2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrrqj9weydeo025vias2.png" alt="An illustration of a cybersecurity attack: a hooded hacker on the left uses a laptop, with arrows leading from him to twelve small documents marked with green upload arrows, then to a server hit by a red lightning bolt, signifying a system breach." width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="prevent-DoS"&gt;🛡️ How to prevent file upload DoS attacks from happening?&lt;/h3&gt;

&lt;p&gt;To deal with these vulnerabilities, it's essential to implement proper security measures in the backend part of your file upload feature. It’s relatively easy and straightforward to do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set&lt;/strong&gt; appropriate file &lt;strong&gt;size limits&lt;/strong&gt; to prevent the upload of excessively large files that could overload your system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set&lt;/strong&gt; a &lt;strong&gt;limit&lt;/strong&gt; to the &lt;strong&gt;number&lt;/strong&gt; of simultaneous files that can be uploaded at the same time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an elementary Typescript implementation example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxFileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 50 MB limit&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Assuming 'files' is an array of File objects in the request body&lt;/span&gt;

&lt;span class="c1"&gt;// Check file size and number of files&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Too many files uploaded.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxFileSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;File size exceeds the limit.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Handle the file, e.g., save it to the server&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Files uploaded successfully!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More general tips to prevent any type of DoS attacks are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing &lt;strong&gt;rate limiting&lt;/strong&gt; for file uploads to prevent the rapid uploading of numerous small files. This helps control the flow of incoming data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP blocking&lt;/strong&gt; → blocking traffic from known or suspected malicious sources to prevent DoS traffic from reaching its target. You can find lists of abusive IPs, for instance on &lt;a href="https://www.abuseipdb.com/" rel="noopener noreferrer"&gt;abuseipdb.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Using a &lt;strong&gt;Content Delivery Networks&lt;/strong&gt; (CDNs) such as &lt;a href="https://www.cloudflare.com" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt;, can help absorb large amounts of traffic and mitigate the impact of DoS attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Additional benefits of DoS protection measure: 💰 &amp;amp; 🌱
&lt;/h3&gt;

&lt;p&gt;Using &lt;strong&gt;smaller and fewer files&lt;/strong&gt; can bring also other &lt;strong&gt;benefits&lt;/strong&gt; to you and the environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;strong&gt;less quantity&lt;/strong&gt; to store on your server, you will have &lt;strong&gt;better performances&lt;/strong&gt; and &lt;strong&gt;less to pay&lt;/strong&gt; at the end of the month.&lt;/li&gt;
&lt;li&gt;Moreover, less storage usage will &lt;strong&gt;reduce the environmental impact&lt;/strong&gt; of manufacturing, operating, and cooling data centers.&lt;/li&gt;
&lt;li&gt;Sending voluminous files is also costly so even though front-end file size validation isn’t enough for security measure it can help to reduce the environmental impact of the uses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="zip-bomb"&gt;💣 What is a Zip Bomb? A type of DoS attack&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Zip Bomb&lt;/strong&gt;, also known as a Zip of Death or decompression bomb, is a &lt;strong&gt;malicious archive file&lt;/strong&gt; designed to crash or render useless the program or system reading it. It's a &lt;strong&gt;type of DoS attack&lt;/strong&gt;. The concept behind a Zip Bomb is to exploit the file compression algorithm's ability to highly compress a large amount of data. A Zip Bomb file appears small and innocuous but contains an &lt;strong&gt;enormous amount of data when decompressed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a targeted system tries to decompress such a file, it can exhaust system resources due to the disproportionally large size of the decompressed data compared to the compressed file. For example, a Zip Bomb could be a file that is only a few kilobytes in size but expands to several gigabytes or even terabytes when decompressed. This can lead to &lt;strong&gt;system crashes&lt;/strong&gt;, slowdowns, or can render the system inoperable, thus achieving the attacker's goal of &lt;strong&gt;denying service&lt;/strong&gt; to legitimate users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckjodf12kq0xy0chwzty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckjodf12kq0xy0chwzty.png" alt="An illustration of a cyber attack sequence: a figure in a purple hood uses a laptop with a skull emblem, symbolizing a hacker. At the center, a yellow folder labeled 'ZIP' with a bomb indicates a malicious 'zip bomb.' To the right, an explosion in front of a cloud and servers shows the zip bomb's impact in a cloud environment." width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zip Bombs are not only limited to Zip files; they can be created in any file format that supports compression, such as RAR or 7z. Accepting an archive file is always a delicate matter, and decompressing it on your server even more so. The defense against Zip Bombs involves careful scanning and validation of input files, limiting the maximum size of decompressed data, and using updated antivirus and anti-malware programs capable of detecting such threats.&lt;/p&gt;

&lt;h2 id="antivirus-scanning"&gt;🕵️‍♂️ Antivirus scanning for user protection&lt;/h2&gt;

&lt;p&gt;The purpose of uploading files is very often to share them with other people thus another user is supposed to download the file afterwards. These users can inadvertently become targets of malicious activity when using file upload features on web applications. In many cases, attackers may attempt to exploit the trust of users by uploading files that appear harmless but, in reality, contain malware or other malicious content. This can include infected documents, executables, or scripts. When these files are downloaded and executed on a user's system, it can lead to compromised security and potential data breaches.&lt;/p&gt;

&lt;p&gt;If the uploaded files are &lt;strong&gt;retrievable&lt;/strong&gt;, to safeguard both your platform and mainly your users, it is &lt;strong&gt;crucial&lt;/strong&gt; to run an up-to-date &lt;strong&gt;virus scanner&lt;/strong&gt;. Virus scanners are very adept at spotting malicious files masquerading as a different file type, invisible to most unaware users. This proactive measure can &lt;strong&gt;automatically check&lt;/strong&gt; each uploaded file for potential threats, helping ensure that users are not unwittingly exposed to malicious content.&lt;/p&gt;

&lt;p&gt;Running an antivirus can also help you secure your server, because it can detect potentially malicious files that succeeded in bypassing all your previous security checks.&lt;/p&gt;

&lt;p&gt;Before really uploading a file you can &lt;strong&gt;isolate&lt;/strong&gt; it and run an antivirus to identify potential threats, then only finally uploading it. This way all potential threats are being &lt;strong&gt;neutralised&lt;/strong&gt; before having access to sensitive data.&lt;/p&gt;

&lt;h3 id="antivirus-recommendations"&gt;⭐️ Recommendations to go beyond simple antivirus scanning&lt;/h3&gt;

&lt;p&gt;For maximal safety, you can implement &lt;strong&gt;multi-scanning&lt;/strong&gt;. For each file run multiple anti-malware engines, which can be &lt;strong&gt;parallelised&lt;/strong&gt;, in order to get the highest detection rate and the shortest window of exposure to malware outbreaks. Here are some reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Malware can &lt;strong&gt;bypass&lt;/strong&gt; a single antivirus engine.&lt;/li&gt;
&lt;li&gt;Different anti-virus vendors have different &lt;strong&gt;response times&lt;/strong&gt; to outbreaks due to their location and focused markets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False positives&lt;/strong&gt; in virus detection are a common side-effect of any malware scanning solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lastly, if you have to choose between an &lt;strong&gt;Online&lt;/strong&gt; and an &lt;strong&gt;Offline&lt;/strong&gt; antivirus, you should probably prefer an online solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you use offline antivirus software, you have to download and install it in your system. While &lt;strong&gt;online&lt;/strong&gt; virus scanner does &lt;strong&gt;not require&lt;/strong&gt; any &lt;strong&gt;storage space&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Protection &amp;amp; Regular Updates&lt;/strong&gt;: Online antivirus software constantly updates its virus definitions and scans your system in real-time, providing immediate protection against the latest threats. Online antivirus solutions automatically receive updates and patches to keep up with the evolving threat landscape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud-Based&lt;/strong&gt;: Online antivirus relies on cloud servers to analyse files and detect malware. This reduces the strain on your computer's resources and can be more efficient since the processing part is usually done on high-speed cloud-based servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Detection&lt;/strong&gt;: Online antivirus solutions often use behavioural analysis and machine learning algorithms to identify previously unseen malware, improving their ability to detect new threats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less Control &amp;amp; Privacy&lt;/strong&gt;: Data sent to external servers and no customisation according to personal preferences are the main drawbacks that would make you prefer an offline solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My recommendation would be to use &lt;a href="https://www.clamav.net/" rel="noopener noreferrer"&gt;ClamAV&lt;/a&gt; an open source antivirus engine. It is a versatile tool designed to detect multiple types of threats from numerous file formats and other use cases (cross-platform, integration such as mail server). Finally, it is updated on a daily basis, ensuring protection against the latest known threats. This rapid update cycle is crucial for an antivirus tool to be effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up AWS antivirus scanning for files in S3 Buckets
&lt;/h3&gt;

&lt;p&gt;You can find a guide on the AWS blog, detailing &lt;a href="https://aws.amazon.com/fr/blogs/apn/integrating-amazon-s3-malware-scanning-into-your-application-workflow-with-cloud-storage-security/" rel="noopener noreferrer"&gt;how to integrate Amazon S3 Malware Scanning into your web App&lt;/a&gt;. &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon Simple Storage Service (Amazon S3)&lt;/a&gt; is a highly scalable object storage service that allows file storage. Currently, when a file is uploaded to an S3 Bucket, two scanning engines are available: &lt;strong&gt;Sophos&lt;/strong&gt; and &lt;strong&gt;ClamAV&lt;/strong&gt;, to detect malicious ones. Additionally, both engines can be used together for an even higher safety.&lt;/p&gt;

&lt;h3 id="asynchronous-scanning"&gt;Asynchronous antivirus scanning to enhance performance and UX&lt;/h3&gt;

&lt;p&gt;Running an extensive antivirus engine can be quite &lt;strong&gt;time-consuming&lt;/strong&gt; thus implementing &lt;strong&gt;asynchronous&lt;/strong&gt; antivirus scanning can significantly &lt;strong&gt;enhance&lt;/strong&gt; both &lt;strong&gt;performance&lt;/strong&gt; and &lt;strong&gt;user experience&lt;/strong&gt; in systems that require real-time or near-real-time processing. This approach involves decoupling the scanning process from the main application flow, allowing the application to continue executing without waiting for the scan to complete.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a user uploads a file, the &lt;strong&gt;server immediately acknowledges receipt&lt;/strong&gt; and begins the upload process, providing a responsive experience.&lt;/li&gt;
&lt;li&gt;The file is being &lt;strong&gt;processed in the background&lt;/strong&gt;, scanned and uploaded if successful or either deleted or quarantined if not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send a status update&lt;/strong&gt; on the upload process to the user, such as through email notifications, dashboard updates, or real-time alerts using web sockets.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="race-conditions"&gt;🏎️ Race conditions applied to file upload&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://portswigger.net/web-security/race-conditions" rel="noopener noreferrer"&gt;Race conditions in cybersecurity&lt;/a&gt; refer to a scenario where the outcome of a process is unexpectedly altered due to the &lt;strong&gt;timing&lt;/strong&gt; of events not happening as planned. This occurs in a concurrent environment where multiple processes or threads execute in &lt;strong&gt;overlapping timeframes&lt;/strong&gt;, leading to &lt;strong&gt;conflicts&lt;/strong&gt; in accessing or modifying shared resources. The security risk arises when an attacker can &lt;strong&gt;manipulate&lt;/strong&gt; the timing of actions to gain unauthorized access or privileges, modify data, or exploit system functionality in unintended ways.&lt;/p&gt;

&lt;p&gt;In the context of file upload vulnerabilities, race conditions can be particularly concerning. There are multiple scenarios where the file security checks could be bypassed. Here is one example:&lt;/p&gt;

&lt;p&gt;Imagine a web application that allows users to upload files. The application checks the file for security purposes (e.g., ensuring it's not a malicious executable) before moving it to a permanent storage location. A race condition vulnerability could occur if an attacker manages to manipulate the timing of the upload process.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initial Upload&lt;/strong&gt;: The attacker begins by uploading a benign file that passes the application's security checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing Manipulation&lt;/strong&gt;: Right after the initial check but before the file is moved to its permanent location, the attacker quickly replaces the benign file with a malicious one, exploiting the time gap in the process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution of Malicious File&lt;/strong&gt;: The application, believing the file to be safe, moves it to permanent storage, from where the attacker can trigger the execution or processing of the malicious file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This exploitation leverages the race condition to bypass security checks, allowing the upload and execution of potentially harmful files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ue236e43gm51941ntxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ue236e43gm51941ntxg.png" alt="The image depicts a 'Race condition vulnerability' in file uploads. A cloud symbolizes a server with a refresh icon, linked to 'Antivirus Scan' and 'Bucket Upload' boxes. A hooded hacker uses a laptop with a skull, connected by lines to two 'file.pdf' versions: one 'safe' with a green check, the other 'evil' with a purple emoji. A dashed 'frame to override file.pdf' line shows the 'evil' file replacing the 'safe' during upload, highlighting the exploitation of a race condition to insert a malicious file." width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try yourself exploiting a file upload race condition vulnerability, of a different kind, on this &lt;a href="https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-race-condition" rel="noopener noreferrer"&gt;Portswigger’s Lab&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;I hope this article has provided you with a comprehensive understanding of the security challenges associated with file uploads and the strategies to mitigate these risks. By implementing the recommended security measures, you can ensure that your file upload feature is robust, resilient, and secure, protecting your servers and users from potential threats.&lt;/p&gt;

&lt;p&gt;If you'd like to find out more about file type validation, take a look at the first article in this series: &lt;a href="https://dev.to/theodo/mastering-file-upload-security-understanding-file-types-5fd6"&gt;Mastering File Upload Security: Understanding File Types&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>fileupload</category>
    </item>
    <item>
      <title>Mastering File Upload Security: Understanding File Types</title>
      <dc:creator>Marek Elmayan</dc:creator>
      <pubDate>Wed, 06 Dec 2023 14:30:52 +0000</pubDate>
      <link>https://forem.com/theodo/mastering-file-upload-security-understanding-file-types-5fd6</link>
      <guid>https://forem.com/theodo/mastering-file-upload-security-understanding-file-types-5fd6</guid>
      <description>&lt;p&gt;In today's digital landscape, file exchange has become an integral part of our online activities, from sharing work documents to uploading pictures on social media. However, this convenience comes with inherent risks. File upload functions, seemingly innocuous, can serve as potent vectors for high-severity attacks, potentially jeopardizing your data and user security. Whether you're a seasoned developer or just embarking on your coding journey, ensuring the security of your file upload features is paramount.&lt;/p&gt;

&lt;p&gt;To address this concern, it's crucial to understand the concept of file types, including file extensions and MIME types. These are fundamental to ensure the proper handling and validation of uploaded files and thus prevent some malicious files from being uploaded to your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to determine the type of a file? Introducing extension &amp;amp; MIME type
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2zrwojphowzfrz0gzt7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2zrwojphowzfrz0gzt7.png" alt="Visual breakdown of a PDF file's anatomy emphasizing security elements. Key metadata like MIME type and magic number are highlighted, suggesting their role in content security and integrity checks." width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  File Extensions
&lt;/h3&gt;

&lt;p&gt;File extensions are suffixes added to a file's name, typically separated from the base filename by a dot (e.g., &lt;code&gt;.jpg&lt;/code&gt;, &lt;code&gt;.pdf&lt;/code&gt;, &lt;code&gt;.txt&lt;/code&gt;). They serve as a way to identify the file format, which can be important for both the operating system and the user.&lt;/p&gt;

&lt;p&gt;File extensions are a common way for users to recognize the type of a file. For example, &lt;code&gt;.jpg&lt;/code&gt; or &lt;code&gt;.jpeg&lt;/code&gt; extensions are associated with image files, while &lt;code&gt;.pdf&lt;/code&gt; extensions denote documents. However, it's important to note that file extensions can be manipulated or spoofed by malicious users, making them an unreliable method for file type verification.&lt;/p&gt;

&lt;p&gt;Developers should not solely rely on file extensions when validating the types of uploaded files in a web application. An attacker could easily rename a malicious file with a benign extension, exploiting this vulnerability.&lt;/p&gt;

&lt;h3&gt;
  
  
  MIME Types
&lt;/h3&gt;

&lt;p&gt;MIME (Multipurpose Internet Mail Extensions) types, on the other hand, provide a more reliable way to identify the content type of a file. MIME types are associated with the actual content of the file rather than its name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvncqvdbclxz4twshcss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvncqvdbclxz4twshcss.png" alt="Illustration depicting examples of MIME types. At the top, the general structure of MIME type is described as  raw `type/subtype` endraw . Below, 3 examples are listed:  raw `text/plain` endraw ,  raw `image/png` endraw , and  raw `application/pdf` endraw ." width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A MIME type is typically expressed as a two-part identifier, separated by a slash (/). The first part specifies the general category of the file, such as &lt;code&gt;text&lt;/code&gt; for plain text documents or &lt;code&gt;image&lt;/code&gt; for image files. The second part provides more specific information about the file's format, such as &lt;code&gt;html&lt;/code&gt; for HTML web pages or &lt;code&gt;jpeg&lt;/code&gt; for JPEG image files. Together, these two parts form a complete MIME type, like &lt;code&gt;text/html&lt;/code&gt; for HTML documents or &lt;code&gt;image/jpeg&lt;/code&gt; for JPEG images.&lt;/p&gt;

&lt;p&gt;These MIME types are standardized, and web servers use them to indicate the content type of a file in HTTP headers when serving files over the web. You can check the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types" rel="noopener noreferrer"&gt;Common MIME types&lt;/a&gt; 👀&lt;/p&gt;

&lt;p&gt;When dealing with file uploads, you can extract the MIME type from the file's metadata, allowing you to verify its content regardless of the file's name or extension. This helps prevent attackers from disguising malicious content by simply changing the file extension.&lt;/p&gt;

&lt;p&gt;Here is a quick command line example showing that modifying the extension of a file doesn’t change its mime type and doesn’t prevent it from being executed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;hello.php
&lt;span class="c"&gt;# output: &amp;lt;?php echo "Hello World!"; ?&amp;gt;&lt;/span&gt;

file &lt;span class="nt"&gt;--mime-type&lt;/span&gt; hello.php
&lt;span class="c"&gt;# output: hello.php: text/x-php&lt;/span&gt;

php hello.php
&lt;span class="c"&gt;# output: Hello world!&lt;/span&gt;

&lt;span class="nb"&gt;cp &lt;/span&gt;hello.php hello.png

file &lt;span class="nt"&gt;--mime-type&lt;/span&gt; hello.png
&lt;span class="c"&gt;# output: main.png: text/x-php&lt;/span&gt;

php hello.png
&lt;span class="c"&gt;# output: Hello world!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🪄 Magic Numbers hidden in the File Metadata
&lt;/h3&gt;

&lt;p&gt;File metadata refers to the hidden information or attributes associated with a file. Basically, metadata are data about the data, it provides essential context about the file, such as its creation date, author, size, and information about its content. From our security point of view, it’s the last information that interests us. By examining metadata, we can gain insights into the nature of uploaded files and deduce their MIME type ✨.&lt;/p&gt;

&lt;p&gt;Magic numbers, also known as magic bytes or file signatures, are special values or sequences of bytes located at the beginning of a file, in the metadata, that help identify the file's format or type. They serve as a way for software to quickly determine the nature of a file without having to rely solely on its file extension. Magic numbers are commonly used to ensure that the correct software or application is used to open and interpret the file.&lt;/p&gt;

&lt;p&gt;Here are some common examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File type&lt;/th&gt;
&lt;th&gt;Extension&lt;/th&gt;
&lt;th&gt;Hex digits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF format&lt;/td&gt;
&lt;td&gt;.pdf&lt;/td&gt;
&lt;td&gt;25 50 44 46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PNG format&lt;/td&gt;
&lt;td&gt;.png&lt;/td&gt;
&lt;td&gt;89 50 4e 47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GIF format&lt;/td&gt;
&lt;td&gt;.gif&lt;/td&gt;
&lt;td&gt;47 49 46 38&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can check a more extensive list of &lt;a href="https://en.wikipedia.org/wiki/List_of_file_signatures" rel="noopener noreferrer"&gt;file signatures&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The command &lt;code&gt;hexdump -C&lt;/code&gt; is used in Unix-like operating systems to display the contents of a file in hexadecimal format along with printable ASCII characters. Here is a way to check that your PDF is legitimate by verifying its magic number. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69i2b0uvvhxdqvix0tmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69i2b0uvvhxdqvix0tmm.png" alt="Output of the hexdump -C command on a pdf in a terminal. With a highlight on the first hex digits 25 50 44 46 corresponding to the PDF format" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Unreliable tools to help with quick type validation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" rel="noopener noreferrer"&gt;Content-Type header&lt;/a&gt; ?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Content-Type&lt;/code&gt; header is an HTTP header used to indicate the media type or MIME type of the data that is being sent in the HTTP response. This header is crucial for web servers and clients to understand how to process the content they receive. For example, when a web server sends an HTML web page to a browser, it includes the &lt;code&gt;Content-Type&lt;/code&gt; header with a value of "text/html" to inform the browser that it should interpret and display the content as an HTML document.&lt;/p&gt;

&lt;p&gt;However, the &lt;code&gt;Content-Type&lt;/code&gt; header should not be relied upon for robust file type validation, especially for security-critical applications.&lt;/p&gt;

&lt;p&gt;The Content-Type for uploaded files is provided by the client in the HTTP request, thus it can be manipulated by malicious users. Hence it cannot be trusted, as it is trivial to spoof. Although it should not be relied upon for security, it can provide a quick check to prevent users from unintentionally uploading files with the incorrect type.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://html.spec.whatwg.org/multipage/input.html#file-upload-state-(type=file)" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;input type='file'/&amp;gt;&lt;/code&gt;&lt;/a&gt; HTML component
&lt;/h3&gt;

&lt;p&gt;To let users upload files to your web application, you can use the HTML &lt;code&gt;&amp;lt;input type="file"&amp;gt;&lt;/code&gt; element. This element creates a file picker dialog that allows users to select one or more files from their local device. The selected files can then be uploaded to the server using a form submission or an AJAX request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Upload"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even specify the types of files that users can select by using the &lt;code&gt;accept&lt;/code&gt; attribute. This attribute accepts a comma-separated list of MIME types or file extensions. For example, to only allow users to select image files, you can use the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/*"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nevertheless, it's important to note that the &lt;code&gt;accept&lt;/code&gt; attribute is only a hint to the browser and does not provide any security. It's trivial for malicious users to bypass this restriction by simply modifying the file picker dialog or sending a request directly to the server. According to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob/type" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, only the extension is used to determine the type of the file. Therefore, it is not a reliable way to validate the type of uploaded files but it can be used to prevent users from unintentionally uploading files with the incorrect type.&lt;/p&gt;




&lt;h2 id="secure-your-file-validation"&gt;🔒 Secure your app and your file upload feature&lt;/h2&gt;

&lt;p&gt;Ensure the usage of &lt;em&gt;business-critical&lt;/em&gt; extensions only, without allowing any type of &lt;em&gt;non-required&lt;/em&gt; extensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧬 Using Both Extensions and MIME Types:
&lt;/h3&gt;

&lt;p&gt;To achieve robust security, it's advisable to combine both file extensions and MIME types when validating uploaded files. By cross-referencing the file extension with the extracted MIME type, you can add an extra layer of protection to your application. This ensures that the file's content matches its declared extension.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Whitelist relevant types
&lt;/h3&gt;

&lt;p&gt;Based on your web application's needs, ensure the &lt;strong&gt;least harmful&lt;/strong&gt; and the &lt;strong&gt;lowest risk&lt;/strong&gt; file types to be used. By limiting the list of allowed file types, you can already avoid executables, scripts, and other potentially malicious content from being uploaded to your application.&lt;/p&gt;

&lt;p&gt;For instance, allow only common image types for a picture sharing feature, such as &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, &lt;code&gt;svg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ Having protection against file type doesn't mean that it is robust. Some flaws likely exist in the mechanisms implemented to validate the file type. One example among many, is the file extension can be spoofed by using a double extension such as &lt;code&gt;image.png.php&lt;/code&gt; or &lt;code&gt;image.png%00.php&lt;/code&gt; (null byte injection).&lt;/p&gt;

&lt;p&gt;→ The use of a deny list is inherently flawed as it's difficult to explicitly block every possible file extension that could be used to execute code. (It can be bypassed by using lesser known, alternative file extensions that may still be executable, such as &lt;code&gt;.php5&lt;/code&gt;, &lt;code&gt;.shtml&lt;/code&gt;, &lt;code&gt;.docm&lt;/code&gt;, ... )&lt;/p&gt;

&lt;p&gt;→ Ensure that the validation occurs after decoding the file name and that a proper filter is set in place in order to avoid certain known bypasses.&lt;/p&gt;

&lt;p&gt;For more information and practical knowledge, check the following &lt;a href="https://portswigger.net/web-security/file-upload#insufficient-blacklisting-of-dangerous-file-types" rel="noopener noreferrer"&gt;Portswigger topic&lt;/a&gt;.&lt;/p&gt;




&lt;h2 id="exploit-file-type-vulnerabilities-with-polyglot-files"&gt;🥷 Exploiting file type vulnerabilities with Polyglot Files 🎭 &lt;/h2&gt;

&lt;p&gt;Tempering directly the magic number of a file isn’t a big risk since it will make it unreadable to programs thus preventing malicious code execution. Nevertheless, there is a special category of files, called &lt;strong&gt;Polyglot files,&lt;/strong&gt; that are used to exploit vulnerabilities in file validation.&lt;/p&gt;

&lt;p&gt;Inspired by the analogy to multilingualism, they are files that are deliberately crafted to be valid and functional in multiple file formats simultaneously.&lt;/p&gt;

&lt;p&gt;For instance, a PDF-ZIP polyglot can be opened as a valid PDF document and also at the same time decompressed as a valid ZIP archive. Hence if you allow the upload of PDF files then you can bypass the type restriction and upload a ZIP archive.&lt;/p&gt;

&lt;p&gt;Here is a polyglot program that can be interpreted as a C++ or as a Python program depending on the compiler used. The common technique used is to make use of languages that use different characters for comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c"&gt;#if 0
print('Hello world')
#endif
#if 0
""" "
#endif
&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Hello world"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;#if 0
" """
#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a PDF-ZIP file, it is more complicated since you need to understand the structure of the file and how to craft it to be valid in both formats by manipulating bytes. Many different methods can be used to create polyglot files and it is not limited to 2 types for a single file. I will not go into details about the process of creating polyglot files but here are some resources to get started on this fascinating topic.&lt;br&gt;
Here is a &lt;a href="https://github.com/Polydet/polyglot-database/tree/master" rel="noopener noreferrer"&gt;list of polyglot files&lt;/a&gt; you can manipulate and test. Otherwise, you can use &lt;a href="https://github.com/corkami/mitra" rel="noopener noreferrer"&gt;Mitra&lt;/a&gt; to create your own polyglot files.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to prevent polyglot files from being uploaded to your application?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up a very strict validation process that will reject any file that shows any sign of anomaly.&lt;/li&gt;
&lt;li&gt;Sanitize the file content.&lt;/li&gt;
&lt;li&gt;Verify the file through an antivirus software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the above methods are complex, not light to implement, and not always necessary depending on the context of your application. However, it is important to be aware of the existence of polyglot files and the risks they represent.&lt;br&gt;
The next step is to configure your server safely, thus preventing the execution of malicious code.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Stay tuned for the next steps
&lt;/h2&gt;

&lt;p&gt;I hope you now understand more about how file types work. File type validation is a complex process, and no single method is infallible or sufficient. Using a combination of methods to protect against file upload attacks is recommended.&lt;/p&gt;

&lt;p&gt;Stay tuned if you are interested in file upload vulnerabilities such as remote code execution (RCE) or Denial of Service (DoS) attacks. I’ll help you get a gist of it and guide you to safely configure your server and protect your user from downloading malicious files.&lt;/p&gt;

</description>
      <category>security</category>
      <category>fileupload</category>
      <category>filetype</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
