1
0
mirror of https://github.com/PrivSec-dev/privsec.dev synced 2024-11-18 04:21:34 -05:00
privsec.dev/public/os/securing-openssh-with-fido2/index.html

13 lines
23 KiB
HTML
Raw Normal View History

<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=x-ua-compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Securing OpenSSH with FIDO2 | PrivSec.dev</title><meta name=keywords content="operating systems,linux,security"><meta name=description content="Passwordless authentication with OpenSSH keys has been the de facto security standard for years. SSH keys are more robust since they&rsquo;re cryptographically sane by default, and are therefore resilient to most bruteforce atacks. They&rsquo;re also easier to manage while enabling a form of decentralized authentication (it&rsquo;s easy and painless to revoke them). So, what&rsquo;s the next step? And more exactly, why would one need something even better?
Why? The main problem with SSH keys is that they&rsquo;re not magic: they consist of a key pair, of which the private key is stored on your disk."><meta name=author content="Wonderfall"><link rel=canonical href=https://wonderfall.dev/openssh-fido2/><link crossorigin=anonymous href=/assets/css/stylesheet.8b523f1730c922e314350296d83fd666efa16519ca136320a93df674d00b6325.css integrity="sha256-i1I/FzDJIuMUNQKW2D/WZu+hZRnKE2MgqT32dNALYyU=" rel="preload stylesheet" as=style><script defer crossorigin=anonymous src=/assets/js/highlight.f413e19d0714851f6474e7ee9632408e58ac146fbdbe62747134bea2fa3415e0.js integrity="sha256-9BPhnQcUhR9kdOfuljJAjlisFG+9vmJ0cTS+ovo0FeA=" onload=hljs.initHighlightingOnLoad()></script>
<link rel=icon href=https://privsec.dev/%3Clink%20/%20abs%20url%3E><link rel=icon type=image/png sizes=16x16 href=https://privsec.dev/%3Clink%20/%20abs%20url%3E><link rel=icon type=image/png sizes=32x32 href=https://privsec.dev/%3Clink%20/%20abs%20url%3E><link rel=apple-touch-icon href=https://privsec.dev/%3Clink%20/%20abs%20url%3E><link rel=mask-icon href=https://privsec.dev/%3Clink%20/%20abs%20url%3E><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><noscript><style>#theme-toggle,.top-link{display:none}</style></noscript><meta property="og:title" content="Securing OpenSSH with FIDO2"><meta property="og:description" content="Passwordless authentication with OpenSSH keys has been the de facto security standard for years. SSH keys are more robust since they&rsquo;re cryptographically sane by default, and are therefore resilient to most bruteforce atacks. They&rsquo;re also easier to manage while enabling a form of decentralized authentication (it&rsquo;s easy and painless to revoke them). So, what&rsquo;s the next step? And more exactly, why would one need something even better?
Why? The main problem with SSH keys is that they&rsquo;re not magic: they consist of a key pair, of which the private key is stored on your disk."><meta property="og:type" content="article"><meta property="og:url" content="https://privsec.dev/os/securing-openssh-with-fido2/"><meta property="article:section" content="os"><meta name=twitter:card content="summary"><meta name=twitter:title content="Securing OpenSSH with FIDO2"><meta name=twitter:description content="Passwordless authentication with OpenSSH keys has been the de facto security standard for years. SSH keys are more robust since they&rsquo;re cryptographically sane by default, and are therefore resilient to most bruteforce atacks. They&rsquo;re also easier to manage while enabling a form of decentralized authentication (it&rsquo;s easy and painless to revoke them). So, what&rsquo;s the next step? And more exactly, why would one need something even better?
Why? The main problem with SSH keys is that they&rsquo;re not magic: they consist of a key pair, of which the private key is stored on your disk."><script type=application/ld+json>{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":2,"name":"Operating Systems","item":"https://privsec.dev/os/"},{"@type":"ListItem","position":3,"name":"Securing OpenSSH with FIDO2","item":"https://privsec.dev/os/securing-openssh-with-fido2/"}]}</script><script type=application/ld+json>{"@context":"https://schema.org","@type":"BlogPosting","headline":"Securing OpenSSH with FIDO2","name":"Securing OpenSSH with FIDO2","description":"Passwordless authentication with OpenSSH keys has been the de facto security standard for years. SSH keys are more robust since they\u0026rsquo;re cryptographically sane by default, and are therefore resilient to most bruteforce atacks. They\u0026rsquo;re also easier to manage while enabling a form of decentralized authentication (it\u0026rsquo;s easy and painless to revoke them). So, what\u0026rsquo;s the next step? And more exactly, why would one need something even better?\nWhy? The main problem with SSH keys is that they\u0026rsquo;re not magic: they consist of a key pair, of which the private key is stored on your disk.","keywords":["operating systems","linux","security"],"articleBody":"Passwordless authentication with OpenSSH keys has been the de facto security standard for years. SSH keys are more robust since theyre cryptographically sane by default, and are therefore resilient to most bruteforce atacks. Theyre also easier to manage while enabling a form of decentralized authentication (its easy and painless to revoke them). So, whats the next step? And more exactly, why would one need something even better?\nWhy? The main problem with SSH keys is that theyre not magic: they consist of a key pair, of which the private key is stored on your disk. You should be wary of various exfiltration attempts, depending on your theat model:\nIf your disk is not encrypted, any physical access could compromise your keys. If your private key isnt encrypted, malicious applications could compromise it. Even with both encrypted, malicious applications could register your keystrokes. All these attempts are particularly a thing on desktop platforms, because they dont have a proper sandboxing model. On Windows, non-UWP apps could likely have full access to your .ssh directory. On desktop Linux distributions, sandboxing is also lacking, and the situation is even worse if youre using X.org since it allows apps to spy on each other (and on your keyboard) by design. A first good step would be to only use SSH from a trusted \u0026 decently secure system.\nAnother layer of defense would obviously be multi-factor authentification, or the fact that youre relying on a shared secret instead. We can use FIDO2 security keys for that. That way, even if your private key is compromised, the attacker needs physical access to your security key. TOTP is another common 2FA technique, but its vulnerable to various attacks, and relies on the quality of the implementation on the server.\nHow? Fortunately for us, OpenSSH 8.2 (released in February 2020) introduced native support for FIDO2/U2F. Most OpenSSH distributions should have the middleware set to use the libfido2 library, including portable versions such as the one for Win32.\nBasically, ssh-keygen -t ${key_type}-sk will generate for us a token-backed key pair. The key types that are supported depend on your security key. Newer models should support both ECDSA-P256 (ecdsa-sk) and Ed25519 (ed25519-sk). If the latter is available, you should prefer it.\nClient configuration To get started:\nssh-keygen -t ed25519-sk This will generate a id_ed25519_sk private key and a id_ed25519_sk.pub public key in .ssh. These are defaults, but you can change them if you want. We will call this key pair a handle, because theyre not sufficient by themselves to derive the real secret (as you guessed it, the FIDO2 token is
&nbsp;|&nbsp;<span>Originally published at&nbsp;<a href=https://wonderfall.dev/openssh-fido2/ title=https://wonderfall.dev/openssh-fido2/ target=_blank rel="noopener noreferrer">wonderfall.dev</a></span></div></header><div class=toc><details><summary accesskey=c title="(Alt + C)"><span class=details>Table of Contents</span></summary><div class=inner><ul><li><a href=#why aria-label=Why?>Why?</a></li><li><a href=#how aria-label=How?>How?</a><ul><li><a href=#client-configuration aria-label="Client configuration">Client configuration</a></li><li><a href=#server-configuration aria-label="Server configuration">Server configuration</a></li></ul></li><li><a href=#thats-cool-right aria-label="That&amp;rsquo;s cool, right?">That&rsquo;s cool, right?</a></li></ul></div></details></div><div class=post-content><p>Passwordless authentication with OpenSSH keys has been the <em>de facto</em> security standard for years. SSH keys are more robust since they&rsquo;re cryptographically sane by default, and are therefore resilient to most bruteforce atacks. They&rsquo;re also easier to manage while enabling a form of decentralized authentication (it&rsquo;s easy and painless to revoke them). So, what&rsquo;s the next step? And more exactly, why would one need something even better?</p><h2 id=why>Why?<a hidden class=anchor aria-hidden=true href=#why>#</a></h2><p>The main problem with SSH keys is that they&rsquo;re not magic: they consist of a key pair, of which the private key is stored on your disk. You should be wary of various exfiltration attempts, depending on your theat model:</p><ul><li>If your disk is not encrypted, any physical access could compromise your keys.</li><li>If your private key isn&rsquo;t encrypted, malicious applications could compromise it.</li><li>Even with both encrypted, malicious applications could register your keystrokes.</li></ul><p>All these attempts are particularly a thing on desktop platforms, because they don&rsquo;t have a proper sandboxing model. On Windows, non-UWP apps could likely have full access to your <code>.ssh</code> directory. On desktop Linux distributions, sandboxing is also lacking, and the situation is even worse if you&rsquo;re using X.org since it allows apps to spy on each other (and on your keyboard) by design. A first good step would be to only use SSH from a trusted & decently secure system.</p><p>Another layer of defense would obviously be multi-factor authentification, or the fact that you&rsquo;re relying on a shared secret instead. We can use FIDO2 security keys for that. That way, even if your private key is compromised, the attacker needs physical access to your security key. TOTP is another common 2FA technique, but it&rsquo;s vulnerable to various attacks, and relies on the quality of the implementation on the server.</p><h2 id=how>How?<a hidden class=anchor aria-hidden=true href=#how>#</a></h2><p>Fortunately for us, <a href=https://www.openssh.com/txt/release-8.2>OpenSSH 8.2</a> (released in February 2020) introduced native support for FIDO2/U2F. Most OpenSSH distributions should have the middleware set to use the <code>libfido2</code> library, including portable versions such as the one <a href=https://github.com/PowerShell/Win32-OpenSSH>for Win32</a>.</p><p>Basically, <code>ssh-keygen -t ${key_type}-sk</code> will generate for us a token-backed key pair. The key types that are supported depend on your security key. Newer models should support both ECDSA-P256 (<code>ecdsa-sk</code>) and Ed25519 (<code>ed25519-sk</code>). If the latter is available, you should prefer it.</p><h3 id=client-configuration>Client configuration<a hidden class=anchor aria-hidden=true href=#client-configuration>#</a></h3><p>To get started:</p><pre tabindex=0><code>ssh-keygen -t ed25519-sk
</code></pre><p>This will generate a <code>id_ed25519_sk</code> private key and a <code>id_ed25519_sk.pub</code> public key in <code>.ssh</code>. These are defaults, but you can change them if you want. We will call this key pair a &ldquo;handle&rdquo;, because they&rsquo;re not sufficient by themselves to derive the real secret (as you guessed it, the FIDO2 token is needed). <code>ssh-keygen</code> should ask you to touch the key, and enter the PIN prior to that if you did set one (you probably should).</p><p>You can also generate a <strong>resident key</strong> (referred to as <em>discoverable credential</em> in the WebAuthn specification):</p><pre tabindex=0><code>ssh-keygen -t ed25519-sk -O resident -O application=ssh:user1
</code></pre><p>As you can see, a few options must be specified:</p><ul><li><code>-O resident</code> will tell <code>ssh-keygen</code> to generate a resident key, meaning that the private &ldquo;handle&rdquo; key will also be stored on the security key itself. This has security implications, but you may want that to move seamlessly between different computers. In that case, you should absolutely protect your key with a PIN beforehand.</li><li><code>-O application=ssh:</code> is necessary to instruct that the resident key will use a particular slot, because the security key will have to index the resident keys (by default, they use <code>ssh:</code> with an empty user ID). If this is not specificed, the next key generation might overwrite the previous one.</li><li><code>-O verify-required</code> is optional but instructs that a PIN is required to generate/access the key.</li></ul><p>Resident keys can be retrieved using <code>ssh-keygen -K</code> or <code>ssh-add -K</code> if you don&rsquo;t want to write them to the disk.</p><h3 id=server-configuration>Server configuration<a hidden class=anchor aria-hidden=true href=#server-configuration>#</a></h3><p>Next, transfer your public key over to the server (granted you have already access to it with a regular key pair):</p><pre tabindex=0><code>ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@server.domain.tld
</code></pre><p><em>Ta-da!</em> But one last thing: we need to make sure the server supports this public key format in <code>sshd_config</code>:</p><pre tabindex=0><code>PubkeyAcceptedKeyTypes ssh-ed25519,sk-ssh-ed25519@openssh.com
</code></pre><p>Adding <code>sk-ssh-ed25519@openssh.com</code> to <code>PubkeyAcceptedKeyTypes</code> should suffice. It&rsquo;s best practice to only use the cryptographic primitives that you need, and hopefully ones that are also modern. This isn&rsquo;t a full-on SSH hardening guide, but you should take a look at the <a href=https://github.com/GrapheneOS/infrastructure/blob/main/sshd_config>configuration file GrapheneOS uses</a> for their servers to give you an idea on a few good practices.</p><p>Restart the <code>sshd</code> service and try to connect to your server using your key handle (by passing <code>-i ~/.ssh/id_ed25519_sk</code> to <code>ssh</code> for instance). If that works for you (your FIDO2 security key should be needed to derive the real secret), feel free to remove your previous keys from <code>.ssh/authorized_keys</code> on your server.</p><h2 id=thats-cool-right>That&rsquo;s cool, right?<a hidden class=anchor aria-hidden=true href=#thats-cool-right>#</a></h2><p>If you don&rsquo;t have a security key, you can buy one from <a href=https://www.yubico.com/fr/store/>YubiKey</a> (I&rsquo;m very happy with my 5C NFC by the way), <a href=https://www.nitrokey.com/>Nitrokey</a>, <a href=https://solokeys.com/>SoloKeys</a> or <a href=https://onlykey.io/>OnlyKey</a> (to name a few). If you have an Android device with a hardware security module (HSM), such as the Google Pixels equipped with Titan M (Pixel 3+), you could even use them as bluetooth security keys.</p><p><em>No reason to miss out on the party if you can afford it!</em></p></div><footer class=post-footer><ul class=post-tags><li><a href=https://privsec.dev/tags/operating-systems/>operating systems</a></li><li><a href=https://privsec.dev/tags/linux/>linux</a></li><li><a href=https://privsec.dev/tags/security/>security</a></li></ul><nav class=paginav><a class=prev href=https://privsec.dev/os/linux-insecurities/><span class=title>« Prev</span><br><span>Linux Insecurities</span></a></nav></footer></article></main><footer class=footer><span>&copy; 2022 <a href=https://privsec.dev>PrivSec.dev</a></span>
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg></a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script><script>document.querySelectorAll("pre > code").forEach(e=>{const n=e.parentNode.parentNode,t=document.createElement("button");t.classList.add("copy-code"),t.innerHTML="copy";function s(){t.innerHTML="copied!",setTimeout(()=>{t.innerHTML="copy"},2e3)}t.addEventListener("click",t=>{if("clipboard"in navigator){navigator.clipboard.writeText(e.textContent),s();return}const n=document.createRange();n.selectNodeContents(e);const o=window.getSelection();o.removeAllRanges(),o.addRange(n);try{document.execCommand("copy"),s()}catch{}o.removeRange(n)}),n.classList.contains("highlight")?n.appendChild(t):n.parentNode.firstChild==n||(e.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName=="TABLE"?e.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(t):e.parentNode.appendChild(t))})</script></body></html>