<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>tmp files &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/tmp-files/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Fri, 03 May 2024 18:53:51 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wadetregaskis.com/wp-content/uploads/2016/03/Stitch-512x512-1-256x256.png</url>
	<title>tmp files &#8211; Wade Tregaskis</title>
	<link>https://wadetregaskis.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">226351702</site>	<item>
		<title>Creating files safely in Mac apps</title>
		<link>https://wadetregaskis.com/creating-temporary-files-safely-in-mac-apps/</link>
					<comments>https://wadetregaskis.com/creating-temporary-files-safely-in-mac-apps/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Wed, 31 Jan 2024 02:48:27 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Howto]]></category>
		<category><![CDATA[App sandboxing]]></category>
		<category><![CDATA[Broken by design]]></category>
		<category><![CDATA[File system]]></category>
		<category><![CDATA[Files]]></category>
		<category><![CDATA[Insecure]]></category>
		<category><![CDATA[mkstemp]]></category>
		<category><![CDATA[mktemp]]></category>
		<category><![CDATA[NSString]]></category>
		<category><![CDATA[O_CREAT]]></category>
		<category><![CDATA[O_EXCL]]></category>
		<category><![CDATA[O_TRUNC]]></category>
		<category><![CDATA[open]]></category>
		<category><![CDATA[OutputStream]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[Tested]]></category>
		<category><![CDATA[tmp files]]></category>
		<category><![CDATA[umask]]></category>
		<category><![CDATA[Undocumented]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=7603</guid>

					<description><![CDATA[Creating a file is a pretty basic and conceptually simple task, that many applications do (whether they realise it or not &#8211; library code often does this too, at least for temporary files such as caches or for communicating between programs). So you&#8217;d think it&#8217;d be trivial to do correctly. Alas, it is not. ☝️&#8230; <a class="read-more-link" href="https://wadetregaskis.com/creating-temporary-files-safely-in-mac-apps/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>Creating a file is a pretty basic and conceptually simple task, that many applications do (whether they realise it or not &#8211; library code often does this too, at least for temporary files such as caches or for communicating between programs).</p>



<p>So you&#8217;d think it&#8217;d be trivial to do correctly.  Alas, it is not.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ This is a challenging topic, and I&#8217;ve done my best to research thoroughly and check everything experimentally.  Still, it&#8217;s certainly possible I&#8217;ve made a mistake or overlooked something.  Please let me know of any errors, in the comments at the bottom.<br><br>Also, it&#8217;s a dense topic, so I&#8217;ve tried to highlight (in bold) the most important points.  In case of TL;DR. 🙂</p>
</div></div>



<h1 class="wp-block-heading">What is the danger?</h1>



<p>There are two key things to watch out for when creating files:</p>



<ol class="wp-block-list">
<li><strong>Security flaws due to incorrect use of, or badly designed, file system APIs</strong>.  This is especially a concern for privileged applications (e.g. setuid) or those that ever run with elevated privileges (e.g. via sudo or by admin users).  Unintentional reuse of existing files can be (and <a href="https://nvd.nist.gov/vuln/detail/CVE-2011-4119" data-wpel-link="external" target="_blank" rel="external noopener">has</a> <a href="https://nvd.nist.gov/vuln/detail/CVE-2020-28407" data-wpel-link="external" target="_blank" rel="external noopener">been</a>) the cause of major security vulnerabilities.  <a href="#file-system-races-in-more-detail">See the appendix for more details</a>.<br><br>Another security concern is leaking sensitive information to other programs (or users, on a shared computer).  This can easily happen if files are created in places other programs or users can access, such as shared folders.  The files may be inadvertently created with inappropriately broad permissions (e.g. world-readable) or the parent folder&#8217;s permissions might permit others to change the permissions of the file after the fact even if they don&#8217;t own it (e.g. a world-writable folder <em>without</em> the sticky bit set).</li>



<li><strong>Data loss risks due to inadvertent overwrites or modifications of existing files</strong>.  If you&#8217;re not <em>certain</em> you know what file is already at a given path on disk, <em>and</em> that you should be allowed to overwrite it, then you should not.  Usually, the user has to give <em>explicit</em> permission (e.g. Save dialogs that explicitly ask the user if they intend to overwrite an existing file).<br><br>This risk is greatest for persistent files, e.g. files in your Documents folder.  Those are usually where the user stores their most important data.<br><br>For temporary files the level of danger is generally lower but not zero.  If you&#8217;re using a system-designated temporaries folder, then in principle anything in there is unimportant anyway.  However, randomly mucking with temporary files can still cause data corruption or loss, depending on how those files are used by applications (including your own).  e.g. they might store autosaves of the current document in temporary files, and directly copy / move those files when the user formally saves.  Thus, modifying the temporary file might end up modifying the user&#8217;s actual save file.</li>
</ol>



<h1 class="wp-block-heading">How are these dangers mitigated?</h1>



<h2 class="wp-block-heading">App Sandboxing helps</h2>



<p>As annoying &amp; limiting as <a href="https://developer.apple.com/documentation/security/app_sandbox/" data-wpel-link="external" target="_blank" rel="external noopener">App Sandboxing</a> can be in other regards, it is one of the best single steps an application author can take to improve file security.  For the most part, your application is the only (non-system &amp; non-privileged<sup data-fn="77cbc356-ca3e-4dff-892d-ddf0ca3c31c9" class="fn"><a href="#77cbc356-ca3e-4dff-892d-ddf0ca3c31c9" id="77cbc356-ca3e-4dff-892d-ddf0ca3c31c9-link">1</a></sup>) application that can write within its sandbox, and you&#8217;ll usually be creating files only within your own sandbox.</p>



<h2 class="wp-block-heading">Avoid /private/tmp (a.k.a. /tmp)</h2>



<p><code>/tmp</code> is merely a symlink to <code>/private/tmp</code>.</p>



<p><code>/private/tmp</code> is <em>world-readable</em>: every program on the computer can access its contents.  Thankfully, it&#8217;s not <em>as</em> bad it first appears &#8211; <code>/private/tmp</code> is special in that it has the &#8220;<a href="https://en.wikipedia.org/wiki/Sticky_bit" data-wpel-link="external" target="_blank" rel="external noopener">sticky bit</a>&#8221; set, which imposes some key restrictions on what users may do to each other&#8217;s files (most importantly, that they can&#8217;t delete them even though <code>/tmp</code> is world-writable).</p>



<p>Still, it&#8217;s better to use the tmp folder designated via <code><a href="https://developer.apple.com/documentation/foundation/filemanager" data-wpel-link="external" target="_blank" rel="external noopener">FileManager</a>.<a href="https://developer.apple.com/documentation/foundation/filemanager/1409234-default" data-wpel-link="external" target="_blank" rel="external noopener">default</a>.<a href="https://developer.apple.com/documentation/foundation/filemanager/1642996-temporarydirectory" data-wpel-link="external" target="_blank" rel="external noopener">temporaryDirectory</a></code> or <code><a href="https://developer.apple.com/documentation/foundation/url" data-wpel-link="external" target="_blank" rel="external noopener">URL</a>.<a href="https://developer.apple.com/documentation/foundation/url/3988477-temporarydirectory" data-wpel-link="external" target="_blank" rel="external noopener">temporaryDirectory</a></code>.  Though the location and security properties of that folder varies:</p>



<ul class="wp-block-list">
<li>If App Sandboxing is in use, it&#8217;s an application-specific folder like <code>/Users/SadPanda/Library/Containers/com.sadpanda.MyApp/Data/tmp/</code>.  No other (unprivileged) application can access that folder.</li>



<li>If App Sandboxing is not in use, it&#8217;s a user-specific folder like <code>/var/folders/v3/8anbad56df64adf3_35gj346jg13v19a/T/</code> (the exact path varies between user accounts and computers, by design for security).  That folder is still insecure &#8211; it is accessible to all programs of the same user &#8211; but at least you don&#8217;t have to worry about other [unprivileged] users.</li>
</ul>



<p>If used correctly, <code>/private/tmp</code> has essentially the same properties as the user-specific temporary folders.  Using it correctly starts with ensuring files are created with no group or &#8216;other&#8217; privileges, but the full complexities are beyond the scope of this post.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ <code>/private/tmp</code> is not accessible when App Sandboxing is enabled.</p>
</div></div>



<h2 class="wp-block-heading" id="consider-setting-a-restrictive-umask">Consider setting a restrictive umask</h2>



<p>When you create a file on any Linux or Unix system, such as macOS, its permissions are set based on a combination of the specific API used and the process-wide <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/umask.2.html" data-wpel-link="external" target="_blank" rel="external noopener">umask</a></code>.  Good APIs will require you to specify the initial privileges of the file, but some do not (e.g. <code>fopen</code>) and instead use some arbitrary default, such as creating files as readable and writable <em>by anyone</em> (<a href="https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation" data-wpel-link="external" target="_blank" rel="external noopener">0666</a>).  That is bad &#8211; usually you <em>don&#8217;t</em> want your files readable by any other users.</p>



<p>While you should generally avoid APIs that don&#8217;t provide proper control over file permissions, you realistically may not even <em>know</em> if you&#8217;re using such APIs because they might be employed by library code you don&#8217;t control (including Apple&#8217;s).</p>



<p>While it&#8217;s possible that the parent folder(s) will protect a given file (by preventing access to their contents by other users), it&#8217;s safest to not assume that.</p>



<p>The umask can help mitigate the dangers of bad APIs by specifying which privileges are <em>not</em> to be granted by default<sup data-fn="afb3f9ba-f914-4dd9-803c-2aad08a6800a" class="fn"><a href="#afb3f9ba-f914-4dd9-803c-2aad08a6800a" id="afb3f9ba-f914-4dd9-803c-2aad08a6800a-link">2</a></sup>.</p>



<p>The umask defaults to denying write access to groups and other users (0022), which is a start but still not good &#8211; read access to private user data is still a concern.</p>



<p>However &#8211; beyond the overly permissive default setting &#8211; there at two problems with umask:</p>



<ol class="wp-block-list">
<li>It is a process-wide global.  Modifications to it apply to all threads in your process, which makes it dangerous to modify.  e.g. you might be creating a particularly sensitive file and need the umask to be 0177, so you set it to that, but before you actually get to execute the file creation another thread sets the umask to 0000 because <em>it</em> wants to create an otherwise unrelated file that&#8217;s world-writable.<br><br>So unfortunately the only safe way to use umask is to set it very early in process launch before any additional threads are created<sup data-fn="9dedd292-995e-479b-ba90-8d2d787e436e" class="fn"><a href="#9dedd292-995e-479b-ba90-8d2d787e436e" id="9dedd292-995e-479b-ba90-8d2d787e436e-link">3</a></sup>.<br><br>You can <em>try</em> to enforce your own mutual exclusion around umask, e.g. with a global lock, but beware of 3rd party code (including Apple&#8217;s) that might modify umask without following your mutual exclusion protocol.  <em>Generally</em> umask isn&#8217;t modified often, so this arguably <em>is</em> possible to achieve in practice, but <em>proving</em> that there are no missed calls to <code>umask</code> can be practically impossible.</li>



<li>Lots of existing code, that you might be unwittingly using via libraries or frameworks, assumes the umask remains at its default.  Thus, making it more restrictive might break things in ways &amp; places that are difficult to foresee.<br><br>In general the more complicated your program, that more of a concern this is.  e.g. most command-line programs can modify umask without causing issues, but GUI programs pull in a <em>lot</em> of framework code and functionality, some of which might implicitly rely on certain umask bits.  Unfortunately the only way to find out is experimentally.</li>
</ol>



<p>So umask is not a panacea.  Still, setting a restrictive umask improves security if you can get away with it.</p>



<h2 class="wp-block-heading">Correctly use the right file creation APIs</h2>



<p>In short, creation of files needs to (generally) not extend nor replace existing files, and ensure correct initial file permissions.</p>



<p>There are quite a lot of APIs for creating a file in macOS.  I&#8217;m going to enumerate only the most common ones provided by the system libraries &amp; frameworks.</p>



<h3 class="wp-block-heading"><code>⚠️</code> <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/open.2.html" data-wpel-link="external" target="_blank" rel="external noopener">open</a></code></h3>



<p>Nominally this is <em>the</em> low-level API for opening (existing or new) files, although as you&#8217;ll see later it&#8217;s not actually the only one.</p>



<p>It has a flags parameter, which is how you tell it what to <em>actually</em> do, between opening existing files, creating new ones, etc.  Two of the most important flags are <code>O_CREAT</code> and <code>O_EXCL</code>.  <code>O_CREAT</code> tells <code>open</code> to create the file.  If <code>O_EXCL</code> is specified, <code>open</code> will fail if a file already exists at the target location.  If <code>O_EXCL</code> is <em>not</em> specified, <code>O_CREAT</code> is interperted as &#8220;create the file <em>if necessary</em>&#8221; &#8211; meaning, it will actually open the existing file if it exists, and <em>not</em> create a new file.</p>



<p><strong>You should almost never use <code>O_CREAT</code> without <code>O_EXCL</code>.</strong>  If you really do intend to overwrite the existing file, then it&#8217;s safer to remove the existing file first (e.g. with <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/unlink.2.html" data-wpel-link="external" target="_blank" rel="external noopener">unlink</a></code>), and <em>then</em> create your new file (with <code>O_EXCL</code> to ensure no other file appears at the target location in the interim).  That way you ensure the new file has your expected location (not a symlink) and attributes (e.g. file permissions).</p>



<p>Note that <code>O_EXCL</code> will fail if the target is a symlink, so you don&#8217;t need to specify <code>O_NOFOLLOW</code> (although it doesn&#8217;t hurt).</p>



<p><code>open</code> requires you to specify the new file&#8217;s permissions if you use the <code>O_CREAT</code> option (and respects the umask), which is good as it makes you think about what the permissions should be, and lets you set them to something suitable for each use case.  These permissions are <em>only</em> applied to <em>new</em> files, so in a nutshell <strong>you cannot rely on them if you don&#8217;t use <code>O_EXCL</code>.</strong></p>



<p>For that reason also, <strong>you typically should not use <code>O_TRUNC</code>.</strong>  If you don&#8217;t need the contents of the existing file, <em>delete the file first</em>.  &#8220;Reusing it&#8221; via <code>O_TRUNC</code> <em>also</em> reuses its permissions and other attributes, which might not be set correctly for your intentions (e.g. a malicious program might have pre-created the file as world-readable, even though you intend it to be readable only by the current user and have otherwise done the right things such as set the umask to ensure that).</p>



<p>One reason you <em>might</em> validly use <code>O_TRUNC</code> is if you anticipate there being multiple <em>hard</em> links to the file, and you want to modify the file as seen by the other links too.  This is very rare.  Be <em>sure</em> that&#8217;s necessary before you use <code>O_TRUNC</code>, and consider putting validations in place to ensure the file you&#8217;ve just opened for reuse has the expected permissions and attributes (e.g. via <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fstat.2.html" data-wpel-link="external" target="_blank" rel="external noopener">fstat64</a></code> and similar APIs that operate on the file descriptor &#8211; do <em>not</em> use <code>lstat</code> or any other path-based APIs).</p>



<h3 class="wp-block-heading">⚠️ <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/fopen.3.html" data-wpel-link="external" target="_blank" rel="external noopener">fopen</a></code></h3>



<p>This is essentially just a wrapper atop <code>open</code>, with the &#8220;w&#8221; and &#8220;a&#8221; flags mapping to <code>O_CREAT</code> (essentially) and the &#8220;x&#8221; flag mapping to <code>O_EXCL</code>.  The same rules apply, so <strong>you should generally never use &#8220;w&#8221; without the &#8220;x&#8221; flag as well, nor &#8220;a&#8221; without &#8220;+&#8221;.</strong></p>



<p><code>fopen</code> does <em>not</em> let you specify the permissions of the created file, instead defaulting to 0666 (readable &amp; writable by <em>everyone</em>) which is a terrible default.  It does respect umask, so by default it will create files as 0644 which is marginally better.  But, <a href="#consider-setting-a-restrictive-umask">as discussed previously</a>, it is difficult to guarantee what the umask actually is at any particular point in time.  So <strong>in general you should prefer <code>open</code> instead of <code>fopen</code></strong>.</p>



<h3 class="wp-block-heading">❌ <code><a href="https://developer.apple.com/documentation/foundation/outputstream/1416367-init" data-wpel-link="external" target="_blank" rel="external noopener">OutputStream(toFileAtPath:append:)</a></code> &amp; friends</h3>



<p>This ultimately (when you call the <code>open</code> method) calls <code>open</code> with the flags <code>O_WRONLY | O_CREAT</code> (plus <code>O_TRUNC</code> if the append argument is false).  As such it will <em>always</em> modify an existing file if present.</p>



<p>It also does not let you specify the permissions of the new file, instead defaulting arbitrarily to 0666 (but respecting umask, at least).</p>



<p><strong>It is an unsafe API and should not be used.</strong></p>



<h3 class="wp-block-heading">⚠️ <code><a href="https://developer.apple.com/documentation/foundation/nsdata" data-wpel-link="external" target="_blank" rel="external noopener">NSData</a>.<a href="https://developer.apple.com/documentation/foundation/nsdata/1414800-write" data-wpel-link="external" target="_blank" rel="external noopener">write(toFile:options:)</a></code> &amp; friends</h3>



<p>This family of methods all ultimately call <code><a href="https://opensource.apple.com/source/xnu/xnu-2050.9.2/libsyscall/wrappers/open_dprotected_np.c.auto.html" data-wpel-link="external" target="_blank" rel="external noopener">open_dprotected_np</a></code> (<a href="https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/vfs/vfs_syscalls.c#L4517" data-wpel-link="external" target="_blank" rel="external noopener">implementation</a>), a variant of <code>open</code> specific to Apple platforms which adds Apple-specific functionality regarding file encryption and isolation (see e.g. the protection-related flags within <code><a href="https://developer.apple.com/documentation/foundation/nsdata/writingoptions" data-wpel-link="external" target="_blank" rel="external noopener">NSData.WritingOptions</a></code>).  It takes the same flags as the regular <code>open</code>, and <code>write(toFile:options:)</code> by default uses <code>O_CREAT | O_TRUNC</code>.  If you use the <code>withoutOverwriting</code> option, it adds <code>O_EXCL</code>.  <strong>So you should usually use <code>withoutOverwriting</code>.</strong></p>



<p>If you use the <code>atomic</code> flag, it creates the file using a private function <code>_NSCreateTemporaryFile_Protected</code> which obtains a file path using <a href="#mktemp"><code>mktemp</code> ⚠️</a> and calls <code>open_dprotected_np</code> with the flags <code>O_CREAT | O_EXCL | O_RDWR</code><sup data-fn="1d64125f-81d0-4bb5-8090-ac0b5d942d7d" class="fn"><a href="#1d64125f-81d0-4bb5-8090-ac0b5d942d7d" id="1d64125f-81d0-4bb5-8090-ac0b5d942d7d-link">4</a></sup>.  Once it has created &amp; written to that temporary file, it uses <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/rename.2.html" data-wpel-link="external" target="_blank" rel="external noopener">rename</a></code> to move it into place.  <code>rename</code> just silently deletes any existing file at the destination path.  So it&#8217;s safe against race attacks, but susceptible to data loss bugs from unintentionally overwriting existing files.  As such, <strong>use <code>atomic</code> only with caution</strong>.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>⚠️ If you add <code>withoutOverwriting</code> on top of <code>atomic</code>, the call crashes your program!  It throws an Objective-C exception &#8211; <code>NSInvalidArgumentException</code>.  Swift does not support Objective-C exceptions (<a href="https://forums.swift.org/t/pitch-a-swift-representation-for-thrown-and-caught-exceptions/54583/3" data-wpel-link="external" target="_blank" rel="external noopener">it&#8217;s fundamentally unsafe to pass Objective-C exceptions up to Swift functions</a>) so you have to <a href="https://stackoverflow.com/questions/32758811/catching-nsexception-in-swift/36454808#36454808" data-wpel-link="external" target="_blank" rel="external noopener">use an Objective-C helper function as an intermediary</a>.</p>



<p>This is a particularly unfortunate limitation &#8211; even aside from the crashiness &#8211; because using both options together is highly desirable and it <em>should</em> in principle work &#8211; the implementation can simply use <code><a href="https://www.manpagez.com/man/2/renamex_np/osx-10.12.3.php" data-wpel-link="external" target="_blank" rel="external noopener">renamex_np</a></code> instead with the flag <code>RENAME_EXCL</code>.</p>



<p>FB13568491.</p>
</div></div>



<p>It also does not let you specify the permissions of the new file, instead defaulting arbitrarily to 0666 (but respecting umask, at least).  <strong>For files containing sensitive data (such as private user data), this API should generally not be used.</strong></p>



<h3 class="wp-block-heading">❌ <code><a href="https://developer.apple.com/documentation/foundation/nsstring" data-wpel-link="external" target="_blank" rel="external noopener">NSString</a>.<a href="https://developer.apple.com/documentation/foundation/nsstring/1407654-write" data-wpel-link="external" target="_blank" rel="external noopener">write(toFile:atomically:encoding:)</a></code> &amp; friends</h3>



<p>These are essentially just wrappers over <code>NSData.write(toFile:options:)</code> &amp; friends, where the <code>atomically</code> argument maps to the <code>atomic</code> option.  They provide no way to use the <code>withoutOverwriting</code> option, so they <strong>should not be used in most cases.</strong></p>



<h2 class="wp-block-heading">Use randomised names for transient files</h2>



<p>Whenever the file name doesn&#8217;t actually matter &#8211; i.e. it&#8217;s not chosen by the user and isn&#8217;t pre-defined by some system requirement &#8211; it&#8217;s best to use a randomised name.  This serves two purposes:</p>



<ol class="wp-block-list">
<li>If your code has any bugs that allow it to erroneously overwrite existing files, using random names at least makes it a lot less likely that you&#8217;ll trigger those bugs, by greatly reducing the odds of using the same name twice.</li>



<li>It makes it harder (if not impossible) for attackers to predict the file names, and therefore to attack them.</li>
</ol>



<p>There are many ways to obtain a randomised name, but it&#8217;s wise to use one of the canonical methods detailed below.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ If you do care about the file name, but not its location, you can use these APIs to create a randomly-named temporary <em>folder</em>, and then create your file within there.</p>



<p>This can be handy for e.g. preparing a file URL to be dragged from your app, where you want the file to have a proper name but don&#8217;t care (per se) where it lives.</p>
</div></div>



<h3 class="wp-block-heading" id="mktemp">⚠️ <code><code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/mktemp.3.html" data-wpel-link="external" target="_blank" rel="external noopener">mktemp</a></code></code></h3>



<p><code>mktemp</code> is infamously a source of security vulnerabilities, because it doesn&#8217;t actually create the file (or folder) but merely returns a path.  The caller is responsible for securely creating the file (or folder).  This <em>can</em> be done safely &#8211; by following the guidance earlier in this post, in particular around the <code>O_EXCL</code> <code>open</code> flag &#8211; but it&#8217;s easy to screw up.</p>



<p><strong>Generally it&#8217;s preferable to use <code>mkstemp</code> / <code>mkdtemp</code> &amp; friends</strong>.  Unfortunately, if you want to use higher-level file APIs on Apple&#8217;s platforms that&#8217;s a problem because most of those APIs don&#8217;t support initialisation from a file descriptor, only a path (or equivalently, URL).  So you may find that you still need to use <code>mktemp</code>.  If so, be very careful about how you actually create the files &amp; folders.</p>



<h3 class="wp-block-heading">✅ <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/mkstemp.3.html" data-wpel-link="external" target="_blank" rel="external noopener">mkstemp</a></code> / <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/mkstemp.3.html" data-wpel-link="external" target="_blank" rel="external noopener">mkdtemp</a></code> &amp; friends</h3>



<p>These replacements for <code>mktemp</code> actually create the file / folder (respectively), in a safe way.</p>



<p><code>mkstemp</code> creates the file and returns the corresponding open file descriptor, instead of merely returning a path and leaving it to the caller to get the creation step right.  It will never overwrite (nor open) an existing file.  It also sets the file&#8217;s initial permissions to 0600 (i.e. read-writable but only by the current user), which is a pretty safe default.</p>



<p><code>mkdtemp</code> still returns a path (not a file descriptor), like <code>mktemp</code>, but it ensures the folder was actually created (and not previously existent) with the permissions set to 0700 (i.e. usable only by the current user).</p>



<p>Neither make any guarantees regarding security &#8211; or lack thereof &#8211; due to parent folder permissions.  The caller still needs to ensure necessary security protections for those (whether by choosing a suitable system-provided folder, or manually checking permissions and symlinks in the path).  <code><code><code><a href="https://developer.apple.com/documentation/foundation/url" data-wpel-link="external" target="_blank" rel="external noopener">URL</a>.<a href="https://developer.apple.com/documentation/foundation/url/3988477-temporarydirectory" data-wpel-link="external" target="_blank" rel="external noopener">temporaryDirectory</a></code></code></code> is a good starting point.</p>



<p>Using these from Swift is a little awkward because they mutate their primary argument (the path template), but <a href="https://github.com/apple/swift-corelibs-foundation/blob/dbca8c7ddcfd19f7f6f6e1b60fd3ee3f748e263c/Sources/Foundation/NSPathUtilities.swift#L774" data-wpel-link="external" target="_blank" rel="external noopener">here&#8217;s an example</a>.  Once you have the file descriptor (in the <code>mkstemp</code> case) you can wrap it in e.g. a <code><a href="https://developer.apple.com/documentation/foundation/filehandle" data-wpel-link="external" target="_blank" rel="external noopener">FileHandle</a></code> and work with it at a slightly higher level.</p>



<h3 class="wp-block-heading"><code><a href="https://developer.apple.com/documentation/foundation/filemanager" data-wpel-link="external" target="_blank" rel="external noopener">⚠️</a></code> <code><a href="https://developer.apple.com/documentation/foundation/filemanager" data-wpel-link="external" target="_blank" rel="external noopener">FileManager</a>.<a href="https://developer.apple.com/documentation/foundation/filemanager/1409234-default" data-wpel-link="external" target="_blank" rel="external noopener">default</a>.<a href="https://developer.apple.com/documentation/foundation/filemanager/1407693-url" data-wpel-link="external" target="_blank" rel="external noopener">url(for:in:appropriateFor:create:)</a></code></h3>



<p>In its special mode where &#8216;for&#8217; is <code><a href="https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/itemreplacementdirectory" data-wpel-link="external" target="_blank" rel="external noopener">.itemReplacementDirectory</a></code> and &#8216;in&#8217; is <a href="https://developer.apple.com/documentation/foundation/filemanager/searchpathdomainmask/1408037-userdomainmask" data-wpel-link="external" target="_blank" rel="external noopener">.<code>userDomainMask</code></a>, this behaves like <code>mkdtemp</code>; it creates a randomly-named folder and returns the URL to it (note that it completely ignores the &#8216;create&#8217; argument in this case &#8211; there is no way to have it <em>not</em> create the temporary folder).</p>



<p>This API seems to presume the use of App Sandboxing to mitigate its problems &#8211; and when App Sandboxing is enabled, it&#8217;s the best way to obtain a temporary file or folder path, though only if you use a suitable value for the &#8216;appropriateFor&#8217; parameter, such as <code><code><a href="https://developer.apple.com/documentation/foundation/url" data-wpel-link="external" target="_blank" rel="external noopener">URL</a>.<a href="https://developer.apple.com/documentation/foundation/url/3988477-temporarydirectory" data-wpel-link="external" target="_blank" rel="external noopener">temporaryDirectory</a></code></code><sup data-fn="a6f54a24-4596-4700-8d50-786942903c54" class="fn"><a href="#a6f54a24-4596-4700-8d50-786942903c54" id="a6f54a24-4596-4700-8d50-786942903c54-link">5</a></sup>.  When App Sandboxing is enabled, that will result in a path <em>inside</em> the sandbox, which is the most secure place an unprivileged application can use.</p>



<p>However, if instead a URL is provided which points to a different volume, it returns a path to a user-specific temporary folder on that volume, e.g. <code>/Volumes/Example/.TemporaryItems/folders.501/TemporaryItems/</code>.  While that does exclude (unprivileged) other users, it&#8217;s still a big step down from a location inside the app&#8217;s sandbox.</p>



<hr class="wp-block-separator has-alpha-channel-opacity is-style-dots"/>



<h1 class="wp-block-heading" id="file-system-races-in-more-detail">Appendix: File system races in more detail</h1>



<p>Generally-speaking, the file system is a shared resource.  Multiple programs can access it simultaneously with no coordination required<sup data-fn="41b5a8b5-6e90-4a7b-b4c2-8f687acd6914" class="fn"><a href="#41b5a8b5-6e90-4a7b-b4c2-8f687acd6914" id="41b5a8b5-6e90-4a7b-b4c2-8f687acd6914-link">6</a></sup> between them.  That opens the door for races &#8211; where the state of the world changes in-between file system operations that a program might mistakenly assume are atomic.</p>



<p>In general, any <em>single</em> call to a low-level file system API &#8211; e.g. <code>open</code> &#8211; is atomic.  Most such APIs correspond to a single syscall into the kernel, and the operation inside the kernel is wrapped inside a lock (conceptually if not also literally).</p>



<p>Conversely, any operation that takes multiple calls to a file system API is <em>never</em> atomic.</p>



<p>A textbook security vulnerability arises when you do something like:</p>



<ol class="wp-block-list">
<li>Make up some random file name (e.g. with <code>mktemp</code>).</li>



<li>Check that it doesn&#8217;t exist (<em>one</em> syscall), and see that it doesn&#8217;t.</li>



<li>Write to that file (<em>a separate syscall</em>).</li>
</ol>



<p>A malicious program could inject its own file in-between steps two and three &#8211; or even more dangerously, a symlink &#8211; and cause your program to overwrite something (many file APIs will automatically follow symlinks and open existing files, if not used correctly as detailed in this post).</p>



<p>The classic concern in this regard is with privileged programs that have the ability to overwrite sensitive files, e.g. <code>/etc/passwd</code>.  Tricking them into doing so can cause major damage to the system (e.g. nobody can login anymore!) in the <em>best</em> case, and in the worst case &#8211; where the attacker can also influence the contents of the file, or those contents are conveniently just what the attacker wants &#8211; they might be able to implement a more subtle attack that doesn&#8217;t merely break the system but instead e.g. changes the root password, giving them superuser access to the computer.</p>



<p>Even for unprivileged applications, it can still be a concern.  e.g. they might be tricked into writing a bunch of private user data into a shared location from where the attacker can exfiltrate it.</p>



<h1 class="wp-block-heading">Appendix: File writing APIs that cannot create new files</h1>



<p>These APIs are nominally irrelevant since they can&#8217;t be used to create new files, but it can be useful to <em>know</em> that fact, for use in converse scenarios where you do <em>not</em> want to create a file.</p>



<h3 class="wp-block-heading"><code><a href="https://developer.apple.com/documentation/foundation/filehandle/1414405-init" data-wpel-link="external" target="_blank" rel="external noopener">FileHandle(forWritingAtPath:)</a></code></h3>



<p>…ultimately calls <code>[[NSConcreteFileHandle alloc] initWithPath:… flags:0x1 createMode:0 error:nil]</code>, which turns the path string into a URL using <code>-[NSURL fileURLWithPath:]</code> and calls <code>-[NSConcreteFileHandle initWithURL:flags:createMode:error:]</code>, which calls <code>_NSOpenFileDescriptor</code> to do the actual file system calls.  That calls <code>open</code> with <em>only</em> the flag <code>O_WRONLY</code>; it does not pass <code>O_CREAT</code> nor <code>O_EXCL</code>.  So <code>FileHandle</code> cannot create new files (which I find a bit unintuitive, as nothing in the name really suggests that limitation).</p>



<h3 class="wp-block-heading"><code><a href="https://developer.apple.com/documentation/foundation/nsdata/1411145-init" data-wpel-link="external" target="_blank" rel="external noopener">NSData(contentsOfFile:options:)</a></code> &amp; friends</h3>



<p>…ultimately call <code>open</code> with no flags (meaning they can only <em>read</em> existing files, not even modify them).  So again, cannot create new files.  Which is perhaps implied and obvious from the name, but it&#8217;s good to be certain.</p>


<ol class="wp-block-footnotes"><li id="77cbc356-ca3e-4dff-892d-ddf0ca3c31c9">In principle system programs &amp; privileged (e.g. root &amp; admin) programs should be defended against too, but it&#8217;s often impractically difficult to do so, and beyond the scope of this post to try to explain how.<br><br>Root is of course the most impractical to defend against &#8211; even though the root user is <em>not</em> a traditional &#8220;God&#8221; user on macOS, Apple&#8217;s nerfing of root is designed to protect <em>Apple&#8217;s</em> programs, not yours.  Root (and admin users) can ultimately still access any files your application(s) create and there&#8217;s nothing you can do about it (other than potentially through orthogonal protections, such as encryption). <a href="#77cbc356-ca3e-4dff-892d-ddf0ca3c31c9-link" aria-label="Jump to footnote reference 1">↩︎</a></li><li id="afb3f9ba-f914-4dd9-803c-2aad08a6800a">It is of course possible for libraries to override the umask, but that would be <em>particularly</em> foul of them and none that I&#8217;ve surveyed do that, thankfully. <a href="#afb3f9ba-f914-4dd9-803c-2aad08a6800a-link" aria-label="Jump to footnote reference 2">↩︎</a></li><li id="9dedd292-995e-479b-ba90-8d2d787e436e">This can be hard to guarantee, in a non-trivial program.  You can use an assertion or precondition on the return value of <code><a href="https://github.com/apple-oss-distributions/libpthread/blob/d8c4e3c212553d3e0f5d76bb7d45a8acd61302dc/src/pthread.c#L943" data-wpel-link="external" target="_blank" rel="external noopener">pthread_is_threaded_np</a></code> to help ensure you&#8217;re modifying umask before additional threads are created. <a href="#9dedd292-995e-479b-ba90-8d2d787e436e-link" aria-label="Jump to footnote reference 3">↩︎</a></li><li id="1d64125f-81d0-4bb5-8090-ac0b5d942d7d">It&#8217;s not apparent to me why it needs <em>read</em> access to the file as well &#8211; that appears to be a bug. <a href="#1d64125f-81d0-4bb5-8090-ac0b5d942d7d-link" aria-label="Jump to footnote reference 4">↩︎</a></li><li id="a6f54a24-4596-4700-8d50-786942903c54">I&#8217;m not sure what happens if the App Sandbox container is not on the boot volume. <a href="#a6f54a24-4596-4700-8d50-786942903c54-link" aria-label="Jump to footnote reference 5">↩︎</a></li><li id="41b5a8b5-6e90-4a7b-b4c2-8f687acd6914">There are various mechanism for <em>voluntary</em> coordination, such as <code><a href="https://developer.apple.com/documentation/foundation/nsfilecoordinator" data-wpel-link="external" target="_blank" rel="external noopener">NSFileCoordinator</a></code> and <code><a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/flock.2.html" data-wpel-link="external" target="_blank" rel="external noopener">flock</a></code>, but programs are not required to use them (and malicious programs happily won&#8217;t). <a href="#41b5a8b5-6e90-4a7b-b4c2-8f687acd6914-link" aria-label="Jump to footnote reference 6">↩︎</a></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/creating-temporary-files-safely-in-mac-apps/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">7603</post-id>	</item>
	</channel>
</rss>
