Small list of security considerations and practices I usually aim to incorporate into the designs of web applications I am working on. Mostly user focused.
Applications must enforce TLS - secure client connections. Your application shouldn’t even accept insecure connections by design. The only exception to this rule is when the application is in development mode on a development machine - and even then, aim to have local TLS and design your application around having TLS everywhere, just to avoid ambiguity.
Applications must allow for optional two-factor authentication using an authenticator app or using a code sent to users’ email.
In a perfect scenario, applications must have an implicit two-factor authentication using an email code for all users who have not opted for different authentication methods. A code sent to the user must be required whenever the user authenticates using a new device.
Authentication links must only work when opened in the same browser within the same session as the browser where the authentication link was requested from.
Applications must use authentication codes for cross-device authentication. Never use authentication links for cross-device authentication. For example, clicking an authentication link from a mobile device in your mobile email client should not authenticate a desktop session for this user.
Applications must send a notification to the user whenever someone tries to unsuccessfully access their account.
Applications must send notifications to the user when someone signs in using valid credentials from a new browser/device. That includes users with explicit two-factor authentication enabled.
Applications should notify users when their accounts are accessed from a previously unknown geographic location, even when using an already known and authenticated device. Your user might be traveling - or someone might be using hijacked session and storage data.
Unusual account events notifications must always include browser name and geographic location of whoever caused the event.
Unusual account events notifications must include a link to lock the account once visited. The landing page in this link should always require an active confirmation from the user - “Are you sure you want to lock the account”?
A locked account should stay locked until the user resets the password.
Applications must require authenticated users to input a password or otherwise explicitly re-authorize themselves before allowing access to sensitive areas of your application or before authorizing potentially destructive actions. Expire such authorization right after the action is complete or after a short period, no more than 10 minutes.
Applications should use two-factor confirmation for very sensitive actions, such as account termination, funds transfer, purchases above a certain threshold, and similar.
Users should not be able to reuse old passwords.
Accounts inactive for long periods should always require email or explicit two-factor confirmation to access, even with valid credentials, before access to the account is granted.
Accessing accounts inactive for long periods must always trigger a notification to the user, regardless if the two-factor authentication is used.
Security-related confirmation notifications must always require explicit user confirmation. Following a hyperlink sent by email or clicking on a notification in a mobile device must never be considered an explicit user confirmation.
Applications must not assume the email of a user is private.
Important account actions should always generate email notifications.
Destructive account actions should always generate email notifications.
Applications must not use single-factor authentication and authorization links for applications requiring a somewhat increased level of security - financial applications, applications that have sensitive private data, medical information, etc. Consider the damage someone could cause by accessing the application on behalf of another user. If the answer is “potentially catastrophic”, single-factor email links must not be used.
Users must be able to easily de-authorize all their devices and active sessions related to their account.
The security and privacy section of the user’s account must be easily accessible and retrievable.
Accounts and important data should never be deleted immediately. Use soft-delete with a grace period for accounts and important associated data.
Applications should never send private data export to the email of the user, including a direct access link to an archive.
Getting an archive that contains private data of a user from a web UI or application should always require two-factor confirmation. Especially if the archive could contain sensitive user information.
Private data export archives should be password protected. The password to the archive should be random and sent to the user by a different medium than the export itself - usually, to the email of the user.
Applications must secure production service connections to the furthest extent permissible by the services themselves. That includes databases. PostgreSQL, MySQL, MongoDB, and most others have support for TLS.
Audit-log every remotely important user account operation, regardless if performed by the user or administrator of the application. Store audit logs in remote, preferably append-only storage system, separate from storage used for other application functions and technical logs. Audit-logs should be kept for as long as legally required, longer if resources permit.
The possibility to upgrade the password hashing/storage algorithm should be part of the design of the application. It should be possible to upgrade transparently whenever a user logs into the application. It should be possible to lock out select algorithms and force password reset.
It should be possible to force the reset of all user passwords.
You should follow OWASP Secure Coding Practices - Quick Reference Guide.