How to Stay Safe Online: Tips for Personal Security

How to Stay Safe Online: Tips for Personal Security

How I learned to "start worrying" and love the illusion of feeling safe.

Digital security and privacy have become more important than ever. With cyber threats constantly evolving, it is crucial to stay up-to-date with best practices and take proactive measures to protect your online presence. This comprehensive guide covers a wide range of security and privacy recommendations for various platforms and scenarios, aiming to help you fortify your digital life.

General

  • Use a password manager: 1Password or Bitwarden.
    • Never reuse passwords.
  • Don't store real passwords of critical accounts in password managers.
    • Instead, store the password altered by a secret rule that only you know.
    • Apply to accounts related to your real-world identity or money.
  • Avoid using your password manager to generate one-time password.
    • Instead, use a separate authenticator app.
    • Otherwise, it would become a single point of failure if compromised.
  • Use different emails and different strong passwords when registering services.
    • Use a secure email provider if possible: Proton Mail.
  • Always use HTTPS.
  • Always enable 2FA (MFA), but avoid SMS-based 2FA if possible.
    • Be aware of SIM swap attack.
    • Turn off "Cloud Syncing" feature if you're using Google Authenticator.
    • Write down backup codes on paper and store them in a safe.
  • Don't provide your phone number to any cloud service if possible.
    • Even you don't use SMS-based 2FA, your phone number might be used as a "Recovery Method"
  • Use Passkey or security keys.
    • You could use your iOS/Android devices or YubiKey.
  • Install any security updates as soon as possible.
  • Don't blindly click on any website you see in your emails or Google search results; they could be scams!
    • Instead, add your frequently visited websites to your browser bookmarks.
  • Carefully review requested permissions when you connect third-party apps to your Google, Twitter, Discord, or other critical accounts.
  • Do things that can ease your paranoia.
  • Read Personal Security Checklist.
  • Read An ultimate list of rules any on-chain survivor should follow to stay safe.

Privacy

macOS

  • Use an application firewall and network monitor: Little Snitch.
  • Create separate browser profiles for different usages.
    • One for daily activities.
    • One for financial activities, and don't install any extensions other than the password manager for this profile.
    • Use Incognito mode.
    • Even better: use separate computers.
  • The fewer browser extensions installed, the better.
    • Be aware of developers might sell their extension to someone else.
  • Don't use any apps that can read your clipboard or what you type.
  • Don't use third-party input tools.
  • Automatically lock your screen when idle.
    • System Settings > Lock Screen > Require password after screen saver begins or display is turned off
  • Set one of Hot Corners to "Lock Screen" and always trigger it when you're away from keyboard.
    • System Settings > Desktop & Dock > Hot Corners
  • Disable AirDrop and Handoff.
    • System Settings > General > Airdrop & Handoff > Off
  • Exclude sensitive folders from Spotlight.
    • System Settings > Siri & Spotlight > Spotlight Privacy
  • Turn on FileVault which provides full disk encryption.
  • Power off your computer when not in use, in order for the disk to be encrypted.
  • Use Dangerzone if you are working with PDFs.
  • Use an ad blocker: uBlock Origin.
  • Read macOS Security and Privacy Guide.

iOS

  • Change the default PIN of your SIM card.
    • Settings > Cellular > SIM PIN > Change PIN
  • Disable Predictive Text.
    • Settings > General > Keyboards > Predictive
    • Settings > General > Transfer or Reset iPhone > Reset > Reset Keyboard Dictionary
  • Don't use third-party keyboard apps.
  • Turn off AirDrop.
  • Read Telegram & Discord Security Best Practices

Crypto

  • For large amounts of assets, always store them in hardware wallets or multisig wallets.
  • Use multiple hardware wallets from different vendors; don't put all your eggs in one basket.
    • Some should only hold assets and never interact with DeFi apps.
    • Some are used for trading or DeFi stuff.
    • Or use an old phone to create wallets, and NEVER connect it to the Internet.
  • Use hardware wallet's hidden wallet with passphrase.
  • Write your seed phrases on paper or metal, and store them in a (physical) safe.
    • Modify the seed phrase with a secret rule that only you know.
    • Keep at least 2 copies in different locations.
    • Never store a hardware wallet's seed phrase digitally, NEVER.
  • Verify backups of your seed phrases every 3 months.
  • Use multisig wallets: Gnosis Safe.
  • Only store a small amount of assets in hot wallets.
    • If you follow this rule, it might be acceptable to store the seed phrase in a password manager.
    • Furthermore, encrypt the seed phrase before storing it.
  • Rotate your hot wallets regularly.
  • When transferring tokens to a new address, always send a small amount first, and make sure you can transfer out.
    • It may waste gas, but it's better than losing funds.
  • Add addresses to contacts or whitelists.
  • Always approve tokens with the exact amount, never use infinite (type(uint256).max) approval.
    • It may waste gas, but it's better than losing funds.
  • Always check the slippage setting before swapping.
  • Review your token approvals regularly: Revoke.cash.
    • Before revoking an approval, you should check the original approve() tx is initiated by you.
    • Attacker can create a fake ERC-20 token and set allowance for you.
  • Signing could be dangerous.
    • If it's a clear, human-readable message, it's probably safe to sign.
    • If it contains a large amount of data, read carefully before signing.
    • Especially, there are "permit" signatures.
  • Use browser extensions or wallets that can simulate/preview transactions.
  • Learn to decode a transaction.
  • Use Etherscan's Watch List to monitor your account activities.
    • Though the notification might be a bit delayed, it's not real-time.
  • Website (domain or frontend code) can be hacked, even if smart contracts are secure.
  • Read Blockchain Dark Forest Selfguard Handbook.
  • Read officercia.eth's articles.

Developer

  • Always create API keys with minimum permissions and set a short expiration time if possible.
  • Create distinct API keys for different purposes, services, or machines.
    • Deactivate the API key if you're not using it.
  • If you're unsure, run the program inside a non-root container.
  • The fewer IDE/editor plugins installed, the better.
  • Enable GitHub Copilot only for specific languages or files.
    • Especially, disable it for .env files or any files that may contain sensitive data.
  • Sign your Git commits.

Wi-Fi

  • Always change the default username/password of your router or IoT devices.
  • Keep your router firmware up-to-date.
  • Only use WPA3-Personal or higher.
  • Disable remote access on your router.
    • If you really want to visit your router's management console through Internet, set IP whitelist at least.
  • Disable WPS (Wi-Fi Protected Setup) which is vulnerable to brute-force attack.
  • Avoid using public Wi-Fi.

Physical

  • Cover your laptop's camera with a sticky note.
  • Be cautious when plugging USB devices into your computer.
  • Be vigilant for keyloggers.
    • Bring your own keyboard and USB hub when necessary.
  • Shred or redact sensitive documents.
    • Instead of simply disposing of them in the trash.
  • Don't reveal information during "inbound" calls.
    • Only share sensitive data during outbound calls or communications that you initiate.
  • Use a certified and well-protected extension cord.
  • Get fire and earthquake insurance for your house.
Solidity: CREATE vs CREATE2

Solidity: CREATE vs CREATE2

In Solidity, there are two opcodes for creating contracts: CREATE and CREATE2. Also, the deployed contract address can be precomputed via:

  • keccak256(deployerAddress, deployerNonce) if you're using CREATE opcode
  • keccak256(0xFF, deployerAddress, salt, bytecode) if you're using CREATE2 opcode

ref:
https://ethereum.stackexchange.com/questions/101336/what-is-the-benefit-of-using-create2-to-create-a-smart-contract

CREATE

Default opcode used when deploying smart contracts. If you're deploying a contract using new YourContract() without salt, then you're using CREATE.

The following code written in TypeScript and ethers.js, shows how to deploy a contract using CREATE under the hood:

import { ethers } from 'ethers';

const deployer = await ethers.getNamedSigner("deployer")
const nonce = await deployer.getTransactionCount()
const computedAddress = ethers.utils.getContractAddress({
    from: deployer.address,
    nonce: nonce,
})
console.log(`computed address: ${computedAddress}`)

const ktbArbitrageurFactory = await ethers.getContractFactory("KtbArbitrageur", deployer)
const ktbArbitrageur = await ktbArbitrageurFactory.deploy(oinchAggregationRouterV5)
console.log(`deployed address: ${ktbArbitrageur.address}`)

Though it's pretty inefficient, but you can specify the deployed address (to some extend) by keeping increasing nonce until it meets some conditions you set:

async increaseNonceToDeployUpgradeable(condition: string, targetAddr: string) {
    const { ethers } = this._hre
    const deployer = await ethers.getNamedSigner("deployer")

    // We use deployer's address and nonce to compute a contract's address which deployed with that nonce,
    // to find the nonce that matches the condition
    let nonce = await deployer.getTransactionCount()
    console.log(`Next nonce: ${nonce}`)

    let computedAddress = "0x0"
    let count = 0
    while (
        count < 2 ||
        (condition == "GREATER_THAN"
            ? computedAddress.toLowerCase() <= targetAddr.toLowerCase()
            : computedAddress.toLowerCase() >= targetAddr.toLowerCase())
    ) {
        // Increase the nonce until we find a contract address that matches the condition
        computedAddress = ethers.utils.getContractAddress({
            from: deployer.address,
            nonce: nonce,
        })
        console.log(`Computed address: ${nonce}, ${computedAddress}`)
        nonce += 1
        count += 1
    }

    // When deploying a upgradable contract,
    // it will deploy the implementation contract first, then deploy the proxy
    // so we need to increase the nonce to "the expected nonce - 1"
    let nextNonce = await deployer.getTransactionCount()
    for (let i = 0; i < count - 1 - 1; i++) {
        nextNonce += 1
        console.log(`Increasing nonce to ${nextNonce}`)
        const tx = await deployer.sendTransaction({
            to: deployer.address,
            value: ethers.utils.parseEther("0"),
        })
        await tx.wait()
    }

    console.log(`Finalized nonce`)
}

ref:
https://docs.ethers.org/v5/api/utils/address/#utils-getContractAddress

CREATE2

The Solidity code below demonstrates how to deploy a contract using CREATE2 which is introduced in EIP-1014 to provide more flexible and predictable address generation:

bytes32 salt = bytes32("perp");
address oinchAggregationRouterV5 = 0x1111111254EEB25477B68fb85Ed929f73A960582;

address computedAddress = address(
    uint256(
        keccak256(
            abi.encodePacked(
                bytes1(0xff), // avoid conflict with CREATE
                address(this), // deployer
                salt,
                keccak256(
                    abi.encodePacked(
                        type(KtbArbitrageur).creationCode, // bytecode
                        abi.encode(oinchAggregationRouterV5) // constructor parameter
                    )
                )
            )
        )
    )
);

KtbArbitrageur ktbArbitrageur = new KtbArbitrageur{ salt: salt }(oinchAggregationRouterV5);
console.logAddress(address(ktbArbitrageur));
console.logAddress(computedAddress);

You can change salt to an arbitrary value to produce different contract addresses.

ref:
https://docs.soliditylang.org/en/v0.7.6/control-structures.html#salted-contract-creations-create2

Solidity: calldata, memory, and storage

Solidity: calldata, memory, and storage

Variables

There are three types of variables in Solidity:

  • Global variables
    • Provide information about the blockchain
    • For example, block.number, block.timestamp, or msg.sender
  • State variables
    • Declared outside a function
    • Stored on the blockchain
    • Also called "storage"
  • Local variables
    • Declared inside a function
    • Not stored on the blockchain, stored in memory instead
    • Erased between function calls

References:
https://docs.soliditylang.org/en/v0.7.6/units-and-global-variables.html
https://solidity-by-example.org/variables

Data Types

There are two types of data:

  • Value types: YourContract, address, bool, uint256, int256, enum, and bytes32 (fixed-size byte arrays)
  • Reference types: array, mapping, and struct

It's worth noting that bytes and string are dynamically-sized byte arrays and are considered reference types. However, byte (bytes1), bytes2, ..., bytes32 are value types since they're fixed-size byte arrays.

References:
https://docs.soliditylang.org/en/v0.7.6/types.html#value-types
https://docs.soliditylang.org/en/v0.7.6/types.html#reference-types

Data Location

When using a reference type, you must explicitly provide the data location where the type is stored. There are three data locations:

  • storage is where the state variables are stored, and its lifetime is the lifetime of the contract
  • memory means variables are temporary and erased between external function calls
  • calldata behaves mostly like memory, but is immutable

For reference types in function arguments, you must declare them as memory or calldata.

If possible, use calldata as the data location because it avoids copying, reduces gas usage, and ensures that the data cannot be modified. Arrays and structs with calldata data location can also be returned from functions, but it is not possible to allocate such types.

References:
https://docs.soliditylang.org/en/v0.7.6/types.html#data-location
https://medium.com/coinmonks/solidity-storage-vs-memory-vs-calldata-8c7e8c38bce
https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc

It is also a best practice to use external if you expect that the function will only ever be called externally, and use public if you need to call the function internally. The difference between both is that public function arguments are copied to memory, while in external functions, arguments are read directly from calldata, which is cheaper than memory allocation. external functions are sometimes more efficient when they receive large arrays.

References:
https://medium.com/newcryptoblock/best-practices-in-solidity-b324b65d33b1

Assignment Behaviour

  • Assignments between storage and memory (or from calldata) always create an independent copy.
  • Assignments from memory to memory only create references.
  • Assignments from storage to a local storage variable also only assign a reference.
  • All other assignments to storage always copy.

References:
https://docs.soliditylang.org/en/v0.7.6/types.html#data-location-and-assignment-behaviour