SSH #
🌶 I have a hot take:
The
~/.ssh
folder should NOT contain private keys.
A private key is generated on the first day of computer setup and remains
there permanently. It will have mode 600
if not misconfigured and may also
have a passphrase for protection (you do... do you?).
So, what's the catch?
During its entire lifespan, which can be months or even years, those private keys can be compromised in just seconds.
This can occur if someone types curl -d
in the command line on your behalf
during a coffee break, or if an NPM package with numerous
intermediate dependencies' postinstall scripts sends it elsewhere.
Even if a passphrase is used for protection, it’s crucial to consider the strength of that phrase against offline brute-force attacks.
ssh-agent to the rescue
If you've enabled AddKeysToAgent
and UseKeychain
in your
~/.ssh/config
file, you can safely remove your private key from the disk
after it's automatically added to the ssh-agent (verify by ssh-add -L
).
This protects against various attacks; however,
if you reboot your system, you'll need to set everything up again.
Thus, the reproducible key generation comes into play.
Instead of relying on entropy from /dev/random
and expecting
the end user to manage it securely indefinitely (how?),
let's use a well-configured CSPRNG with better algorithms to generate the same
private key on demand. Once the private key is added to the ssh-agent,
just delete it from the disk. This greatly reduces the attack surface.
No private key left means nothing to leak in the first place.
YOU CAN NOT LOSE YOUR KEY
IF YOU DROP IT FIRST
Moreover, if you do not regularly need to establish an SSH connection, you might consider skipping the entire ssh-agent setup.
Additionally, backing up your private key using this method is much more reliable, and it remains effective even without specific security concerns.
The final piece of the puzzle is creating a manageable salt/passphrase for CSPRNG, this can vary based on your threat model. I will provide a few examples for inspiration, but you should choose what works best for you:
UUID #
generated from system entropy, put into ~/.ssh/config
as a vague comment
yet you can retrieve it later on
3c5ae80b-99a6-4449-85a0-3610a0700082
Password #
strong password from password managers and safely stored across multiple devices
Yi#uHvMQ1/.nH7pKp'TvA-dw-=97%4fWFd$3
Git Commit #
any git commit hash that is unrelated whatsoever, this can come from one of your side projects or even some opensource project, as long as you don't lose the trace from your mental memory
e8f897f4afef0031fe618a8e94127a0934896aba
--- Linux Kernel tag
v6.8
Blockchain #
Merkle Tree root hash from any given height of blockchains
d249b4edbc844fca9681cfce708a28afe6621ed402e9ca693a36ff563d6d15ec
--- Bitcoin block at height
835,485
Checksum #
specific version of any pkg (npm or crate, etc...) tarball's checksum
sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==
--- npm
esbuild@0.20.1
or
8666f87015685834a42aa61a391303d3bee0b1442dd9cf93e3adf4cbaf8de75a
--- crate
tokio@1.25.3
Pi digits #
chunk of pi digits
641260024379684543777339026472512819
---
36
digits of Pi at100,000
th
Calculation #
two of your favorite numbers multiplied and squared
232371981027000771278400
--- (
7355608
✕65535
) 2
etc...
All the above options are for demonstration purposes only. One should carefully evaluate and choose what suits them best.
Deno #
The program is released on JSR1 and designed to be executed by Deno which is secure by default, it reads from command args and emits to stdout, without any file, network, or environment access.
Deno's default security guarantees are important for programs like this, using as APIs from Node.js or Bun is possible, but not recommended or tested, use at your own risk.
Credit to Paul Miller by his package2 for the heavy lifting, it also has additional functionalities like PGP, OTP, and IPNS, among others; head over to find out more.