Harden for the reader: three red flags an AppSec engineer hits on `git clone`
There's a gap between software that works and software that survives being read by a security engineer. A stack can pass every test, boot cleanly, and demo flawlessly while quietly carrying the exact patterns a reviewer is trained to grep for in the first five minutes. For a security product especially, the reader is the user — the person deciding whether to trust you is going to open the repo,…
Security engineers often find it difficult to distinguish between software that functions properly and software that can withstand scrutiny from a security perspective. A product may appear flawless when tested, smoothly booting and demonstrating its features without issue. However, security is ultimately the responsibility of the user.
For security products, this user is the person who will open the repository and decide whether to trust the software. With this in mind, I conducted a thorough review of a suite of applications, asking: If an AppSec engineer cloned this code, what would cause them to express concern? Three significant issues were identified, none of which impacted runtime functionality.
These three findings illustrate problems that could transform an otherwise promising project into one that is deemed unsuitable for adoption. Let's examine each red flag and the steps taken to address them.
Red Flag 1: The `synchronize` option in TypeORM
TypeORM automatically derives the database schema from entities every time the application starts. This feature may be convenient during the initial development phase, but it presents serious long-term liabilities. Without proper schema management, there is no record of schema changes, and renaming columns could result in data loss.
When this option is enabled, it suggests a lack of ownership over the database schema. To rectify this, the `DB_SYNC` environment variable was configured to default to `false`, ensuring that migrations become the single source of truth. The application now automatically applies migrations during bootstrapping, and the migration files were fully generated and tested against an empty, throwaway Postgres database.
This approach ensures a complete and accurate schema representation while eliminating the risk of data loss due to schema renaming.
Red Flag 2: Hardcoded secrets in the Docker Compose file
The codebase contained multiple instances of hardcoded security secrets, such as JWT access secrets, in the Docker Compose file. This practice poses two primary issues: first, these secrets are committed to the repository, preventing them from being securely overridden. Second, the same secret is duplicated across various services, potentially leading to drift and making it difficult to debug issues related to cross-service authentication.
To address these problems, each logical secret was externalized into a single interpolation variable defined in a `.env` file, which is ignored by Git. This allows for secure injection of values during local development and continuous integration (CI) processes while keeping the secrets separate and synchronized across services.
The fallback values for development remain clearly marked as insecure, while production environments require a separate overlay configuration that enforces the presence of the required secrets. This approach ensures that secrets are safely managed, cannot drift between services, and prevent accidental production deployments with insecure configurations.
Red Flag 3: In-memory nonce management for replay protection
The codebase implemented a mechanism to protect against replay attacks by storing nonces in an in-memory `Map` object. This approach proved to be insufficient for a Zero Trust product, as restarting the application would result in the loss of nonce records. Consequently, a replay attack could potentially succeed when the application was scaled across multiple instances.
To resolve this issue, a database table named `replay_nonces` was introduced, and an atomic check-and-record operation was implemented. This new approach ensures that nonce records are correctly maintained across all application instances while eliminating the race condition present in the in-memory `Map`. The signature verification step is performed before the nonce is recorded, preventing unverified requests from being stored in the database. Furthermore, a scheduled job is responsible for pruning expired rows to maintain data hygiene.
In conclusion, these three red flags demonstrate the importance of adopting a security-first approach when developing software. By addressing these issues, the codebase becomes more resilient to security threats and better equipped to withstand scrutiny from security engineers. By focusing on what the code says about itself to someone qualified to read it, we can ensure that security considerations are integrated seamlessly into the development process, ultimately resulting in a more robust and trustworthy product.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.