Wiping and overwriting file blocks - Linux by Example
Tags: linux, tutorial, bash, programming
Published: 2022-08-24 21:02:00

<p>If you delete a file (i.e. <code>rm /path/to/file</code>) on a filesystem like ext4, its inode and blocks will be released to the pool (marked as free), and there&#39;s a good chance that the contents of the file can be recovered.</p>
<p>There are tools like <code>shred</code>, <code>srm</code> and <code>wipe</code> that can be used to overwrite those blocks, but let&#39;s see what we can do without any extra tools.</p>
<p>Note: Don&#39;t rely on any of these tools or techniques to securely delete files on modern disks and filesystems (especially SSDs with wear leveling, and journaling/COW filesystems like ext4 and btrfs). Always encrypt your data, and make sure that you understand the encryption scheme you&#39;re using, and where and how you can leak unencrypted data.</p>
<hr>
<p>We can&#39;t just do something like this:</p>
<pre><code>cat /dev/zero &gt; /path/to/file
</code></pre>
<p>First, because <code>/dev/zero</code> provides us with a continuous stream of bytes, so the writing process wouldn&#39;t stop.</p>
<p>Instead of <code>/dev/zero</code>, we could use <code>/dev/null</code> to replace the contents of the file:</p>
<pre><code>cat /dev/null &gt; /path/to/file
</code></pre>
<p>But there is a second, more import issue - those commands will truncate the file (and release its blocks), and we need to replace the existing blocks in place.</p>
<p><code>dd</code> (a part of <code>coreutils</code> package) is a handy tool that we can use here. But again, if we&#39;d just try something like this:</p>
<pre><code>dd if=/dev/zero of=/path/to/file
</code></pre>
<p>We&#39;d have the same problem.</p>
<p>To solve the first issue, we need to tell <code>dd</code> how many blocks we want to write, and the block size.</p>
<p>To find those numbers, we can use <code>stat</code> command:</p>
<pre><code>stat /path/to/file
</code></pre>
<p>For a simple, 2 byte file on ext4 with the default settings, you should see something like this:</p>
<pre><code class="language-plain">  File: test
  Size: 2             Blocks: 8          IO Block: 4096   regular file
...
</code></pre>
<p>This means that our file has 8 512-byte blocks (<code>stat</code> always returns 512-byte blocks, my ext4 filesystem has 4KB blocksize, and this file is using a single allocation block - 8*512 = 4096 bytes).</p>
<p>And we can tell that to <code>dd</code> with:</p>
<pre><code>dd if=/dev/zero of=/path/to/file bs=512 count=8
</code></pre>
<p>This will overwrite our file, but we still have the second issue - the file is still getting truncated, and blocks are released to the pool. To solve that, we can use <code>conv=notrunc</code> option to tell <code>dd</code> that we want to preserve the existing blocks:</p>
<pre><code>dd if=/dev/zero of=/path/to/file bs=512 count=8 conv=notrunc
</code></pre>
<p>This should work as expected.</p>
<p>Instead of /dev/zero, we can also use /dev/urandom (slower, but it makes recovery even harder):</p>
<pre><code>dd if=/dev/urandom of=/path/to/file bs=512 count=8 conv=notrunc
</code></pre>
<p>A more scripting-friendly approach would look like this:</p>
<pre><code>read bs count &lt; &lt;(stat -c &quot;%B %b&quot; $file)
dd if=/dev/zero of=$file bs=$bs count=$count conv=notrunc
</code></pre>
<p>It wouldn&#39;t be hard to add support for multiple passes, but at that point, it probably makes more sense to use one of the tools made for this purpose.</p>
<h2 id="shred">shred</h2>
<p><code>shred</code> is also a part of <code>coreutils</code>, and you almost certainly have it already on your system.</p>
<pre><code>shred -n 3 -z /path/to/file
</code></pre>
<p>This command will make three passes overwriting the blocks with random data, and then a final pass overwriting with zeros.</p>
<h2 id="srm">srm</h2>
<p><code>srm</code> (from Secure Remove) is another popular tool for this purpose.</p>
<p>By default, it will do the following:</p>
<ul>
<li>1 pass with 0xff</li>
<li>5 random passes. /dev/urandom is used for a secure RNG if available.</li>
<li>27 passes with special values defined by Peter Gutmann.</li>
<li>5 random passes. /dev/urandom is used for a secure RNG if available.</li>
<li>Rename the file to a random value</li>
<li>Truncate the file</li>
</ul>
<p>On Debian-based distributions, <code>srm</code> is a part of the <code>secure-delete</code> package.</p>
<pre><code>apt install secure-delete
</code></pre>
<p>On Fedora and rpm-based distros, you can install it with:</p>
<pre><code>dnf install srm
</code></pre>
<p>Example use:</p>
<pre><code>srm -vz /path/to/file
</code></pre>
<p>(<code>-v</code> means &#39;verbose&#39;, and <code>-z</code> tells srm to zero blocks after overwriting them with random data)</p>
<h2 id="wipe">wipe</h2>
<p>Another popular tool for this purpose is called <code>wipe</code>.</p>
<p>Installation:</p>
<pre><code>apt install wipe
dnf install wipe
pacman -S wipe
</code></pre>
<p>Example use:</p>
<pre><code>wipe -i /path/to/file
</code></pre>
<p>(<code>-i</code> means informational, verbose mode)</p>
<hr><small>
Note: This is a snapshot of the page from the <a href="https://betterways.dev">BetterWays.dev</a> wiki, you can find the latest (better formatted) version here: <a href="https://betterways.dev/linux-wiping-and-overwriting-file-blocks">betterways.dev/linux-wiping-and-overwriting-file-blocks</a>.
</small>