[PHP-WEBMASTER] [web-news] master: Harden attachment download headers

[email protected] (NickSdot via Derick Rethans)
Newsgroups php.webmaster
Message-ID <[email protected]>
Author: NickSdot (NickSdot)
Committer: Derick Rethans (derickr)
Date: 2026-08-10T14:54:33+01:00

Commit: https://github.com/php/web-news/commit/5c140154ffd7a8c42f7d5069af8dde151c4d8cda
Raw diff: https://github.com/php/web-news/commit/5c140154ffd7a8c42f7d5069af8dde151c4d8cda.diff

Harden attachment download headers

Changed paths:
  M  getpart.php


Diff:

diff --git a/getpart.php b/getpart.php
index c14b616..5652849 100644
--- a/getpart.php
+++ b/getpart.php
@@ -2,6 +2,14 @@
 
 require 'common.php';
 
+function sanitise_header_value($value)
+{
+    // Values must not contain control bytes; stripping them
+    // prevents rejected or injected response headers.
+
+    return trim(preg_replace('/[\x00-\x1F\x7F]/', '', (string) $value));
+}
+
 if (isset($_GET['group'])) {
     $group = preg_replace('@[^A-Za-z0-9.-]@', '', $_GET['group']);
 } else {
@@ -43,14 +51,37 @@
     $contentdisposition = 'attachment';
 
     if (!empty($attachment['filename'])) {
-        $contentdisposition .= '; filename="' . $attachment['filename'] . '"';
+
+        // Use a simple download name; attachment filenames
+        // are not trusted message content.
+
+        $filename = basename(str_replace('\\', '/', sanitise_header_value($attachment['filename'])));
+    } else {
+        $filename = '';
+    }
+
+    if ($filename === '') {
+        $filename = 'attachment';
+    }
+
+    $contentdisposition .= '; filename="' . addcslashes($filename, '\\"') . '"';
+
+    $mimetype = sanitise_header_value($attachment['mimetype']);
+
+    // Only send a bare type/subtype MIME value; parameters
+    // and malformed values fall back safely.
+
+    if (!preg_match('#^[a-z0-9!#$&^_.+-]+/[a-z0-9!#$&^_.+-]+$#i', $mimetype)) {
+        $mimetype = 'application/octet-stream';
     }
 
-    header('Content-Type: ' . $attachment['mimetype']);
+    header('X-Content-Type-Options: nosniff');
+    header('Content-Security-Policy: sandbox');
+    header('Content-Type: ' . $mimetype);
     header('Content-Disposition: ' . $contentdisposition);
 
     if (isset($attachment['description'])) {
-        header('Content-Description: ' . $attachment['description']);
+        header('Content-Description: ' . sanitise_header_value($attachment['description']));
     }
 
     echo $attachment['data'];
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.