ai

Generate More Secure Code With AI-Powered Tools

Essential practices for securing AI-generated code, key security considerations for your full-stack application, deployment, and infrastructure precautions.

Apr 17, 202518 min read

AI-powered development platforms can dramatically speed up building full-stack apps. However, as Uncle Ben almost said, with great speed comes great responsibility. Developers, or anyone using these powerful tools, must ensure the generated code is secure and reliable. Otherwise, you not only face the risk of unhappy or angry users, but you may also face legal liability if your application becomes compromised and sensitive data are exposed or stolen.

This article outlines essential practices for securing AI-generated code, key security considerations for your full-stack application, deployment and infrastructure precautions, and a handy security checklist. While we'll focus on the JavaScript ecosystem using technologies like React, Next.js, TypeScript, and Express.js, you can overlay this guidance over other technology stacks.

AI-Generated Code Security Essentials

AI coding assistants can generate impressive code quickly but might also introduce vulnerabilities if used carelessly or unknowingly. No one has the answers to every question, and we don't know what we don't know. As such, this section seeks to help you become aware of some key security practices to remember as you prompt engineer, vibe code, or whatever is floating in your AI boat.

Understand the limitations if AI tools and verify their outputs

Tools like Bolt and v0 make their best effort to generate the best possible code. However, as the adage goes, "Never trust, always verify". Do not assume that code from these tools is 100% secure. They are powerful but not omniscient. Keep in mind that their knowledge is based on the collective human knowledge. And just like you and I can make human errors, if you passed the CAPTCHA to read this article, they can also make mistakes. It's recommended to always review the generated code yourself (or with a teammate) to catch mistakes or unsafe patterns. Treat the AI tool as a peer that requires a Pull Request (PR) review on every contribution.

Incorporate secure coding guidelines in prompts

When prompting the AI, explicitly mention security requirements, such as input validation, parameterized queries, OWASP best practices, etc. This nudges the AI to produce safer code, but always double-check the results. When in doubt, validate your desired security posture with another human and seek assistance verifying that the generated code incorporated the security-related prompts.

Audit dependencies and packages

AI tools may add libraries automatically, just as humans can add them mindlessly. Scrutinize these dependencies to ensure they are well-maintained and free of known vulnerabilities.

You can also run tools like

npm audit
or similar before deployment to take a pulse on the health of your dependencies. Address any reported security issues in how your application uses the package. Oftentimes, a security vulnerability may not directly impact your application because the path to the attack vector does not exist in your application. Suppose there's no immediate patch that fixes the issue. In that case, it may be okay to continue using the package and deploy your application, keeping a watchful eye on the package's activity and patching it as soon as a fix is available.

Use automated code scanning

With a similar goal to the above, integrate Static Application Security Testing (SAST) tools (such as SonarQube, ESLint plugins, or Snyk) into your workflow to flag common vulnerabilities.

Write tests, including security tests

When builders move fast, they see testing as a burden or de-accelerator. However, having a strong testing suite is critical for AI-generated code as the tests can give you confidence that changes the AI makes to your application are not breaking its functionality. If all goes well, you may be hacking the app together today, but you will be maintaining it a month or a year from now. Invest time in testing so that you don't lose velocity later when you have to add new features to the app with the assistance of AI.

Create unit and integration tests, including security tests. For example, write tests that ensure that unauthorized access is denied or that inputs are sanitized. These tests can help you continuously validate that the effects of the security prompts you used earlier in your journey remain present in your application.

Consider dynamic security testing for critical endpoints for your APIs, such as fuzz testing or pentesting. There may be AI tools that can handle this type of testing for you. If not, wouldn't that be a great use case for building with AI? Just throwing ideas out there.

Educate yourself and adapt

Stay informed about common pitfalls in AI-generated code and changes in the security landscape. As mentioned earlier, treat the AI as another developer who needs a review of their output and iterate on it as needed. Consequently, treat your code as a living organism. Ok, consider your AI-generated code as a Tamagotchi. You simply don't interact with it once and then forget about it. You must constantly pay attention to and maintain it; otherwise, it can get sick or die.

AI code does not magically update itself yet. Set for yourself a calendar or a roadmap of when you should use AI to run an audit on your code. Don't prompt the AI for changes but rather ask it to generate a report on the state of your code. Research and validate any suggested changes and prompt the AI to integrate the change if applicable or valuable to your use cases. Ensure that any new features get new tests generated or written.

Security Considerations for Full-Stack Applications

When building a full-stack application, both the front and back end need to be secured. Now, let us review essential security practices related to generating application codes in Node.js.

Protect API endpoints and backend logic

Proactive prompt:

Generate a Next.js API endpoint that uses Zod for input validation and sanitization. The endpoint should accept a JSON payload (e.g., with fields like 'username', 'email', and 'password'), validate the data types, enforce length and format constraints, and sanitize inputs to prevent injection attacks. Please include inline comments explaining each step and best practices for secure handling.

Defensive prompt:

Please perform a comprehensive audit of our Next.js code base to verify that every endpoint handling user input uses Zod for input validation and sanitization. Confirm that all incoming data (e.g., request bodies, query parameters) are validated for data types, lengths, and formats and that appropriate sanitization is applied to prevent injection attacks. Provide a detailed report highlighting any endpoints or code sections that fall short of these requirements, along with recommendations for remediation.
  • Parameterized database queries:
    • Use parameterized queries or an ORM to avoid SQL injection. Avoid constructing queries by concatenating user input.

Proactive prompt:

Generate production-ready Node.js code for a function that retrieves a user record from a PostgreSQL (or MySQL) database using parameterized queries (or an ORM) to ensure secure query construction—do not use string concatenation for user input. Include inline comments that explain how binding parameters prevent SQL injection.

Defensive prompt:

Audit our entire Node.js code base to verify that every database query is implemented using parameterized queries or an ORM, thereby preventing SQL injection. Identify instances where queries are built by concatenating user input and provide a detailed report with recommendations for remediating those vulnerabilities.
  • Rate limiting and throttling:
    • Implement rate limiting on API endpoints to prevent brute-force attacks or Distributed Deniel of Service (DDoS) attacks. You can use
      express-rate-limit
      to facilitate the implementation of this feature or a service like Cloudflare if you are working on a serverless environment.

Proactive prompt:

Generate a production-ready Node.js code example for an API endpoint that implements rate limiting using `express-rate-limit` to prevent brute-force and DDoS attacks. The code should configure limits per IP, provide custom error responses when limits are exceeded, and include inline comments explaining how these settings mitigate such attacks.
  • Error handling and data leaks:
    • Catch errors and return generic error messages to the client.
    • Log detailed errors securely on the server without exposing them.
    • Follow the Exception and Error Handling guide from OWASP to implement best practices around this critical task. If you are using a database or log service to store logs, be mindful of the intent and impact of your logging strategy. Ensure you only store what is needed and have a sustainable retention policy. Logs can quickly add up to gigabytes or even terabytes of data as your user base or service activity grows.

Proactive prompt:

Generate a production-ready code example that implements robust error handling and logging following OWASP best practices for exception and error handling. The code should catch errors and return generic error messages to the client while logging detailed error information securely on the server. Additionally, measures should be included to ensure that only necessary log data is stored, and a sustainable log retention policy should be outlined to prevent excessive storage growth. Include inline comments to explain how your implementation mitigates data leaks and protects sensitive information.

Defensive prompt:

Audit our code base to verify that error handling and logging adhere to OWASP best practices: ensure all errors are caught, and only generic error messages are returned to the client. In contrast, detailed error information is securely logged without exposing sensitive data. Identify any endpoints or sections where error details might be leaked to users or where log data is stored excessively without a clear retention policy, and provide specific recommendations for remediation.
  • Use secure headers:
    • Set HTTP secure headers that protect against common attacks, such as XSS and clickjacking.
    • Content-Security-Policy
      (CSP): Helps prevent XSS attacks by restricting the sources from which content (like scripts) can be loaded. For example, setting `default-src 'self" ensures that only resources from the same origin are allowed.
    • X-Frame-Options
      : Protects against clickjacking by controlling whether your page can be embedded within an iframe. Common settings include
      DENY
      (disallow framing entirely) or
      SAMEORIGIN
      (allow framing only from the same domain).
    • X-Content-Type-Options
      : With the value
      nosniff
      , this header stops browsers from MIME-sniffing a response away from the declared content type, reducing the risk of certain attacks.

Proactive prompt:

Integrate secure HTTP headers into our codebase to protect against common attacks such as XSS, clickjacking, and MIME sniffing. Update our configuration or middleware so that every HTTP response includes a Content-Security-Policy header (e.g., set to restrict resources to the same origin like `default-src 'self"), an X-Frame-Options header (set to `DENY` or `SAMEORIGIN`), and an X-Content-Type-Options header (set to `nosniff`). Ensure these settings are consistently applied across all endpoints in the application.

Defensive prompt:

Audit our codebase to verify that secure HTTP headers are implemented on all endpoints. Specifically, check that every HTTP response includes a Content-Security-Policy header (with restrictions such as `default-src 'self"), an X-Frame-Options header (set to `DENY` or `SAMEORIGIN`), and an X-Content-Type-Options header (set to `nosniff`). Identify any instances where these headers are missing or misconfigured and provide detailed recommendations for remediation.

Secure Authentication and Authorization

  • Use established authentication libraries or services:

    • Leverage well-known libraries, such as
      next-auth
      , for Next.js instead of rolling your authentication.
    • Delegate authentication to an identity platform like Auth0 to avoid writing authentication from scratch, so you can focus on your core features instead of dealing with user management and security boilerplate.

  • Protect session tokens:

    • Store tokens in HTTP-only, Secure cookies rather than in localStorage. Use proper cookie attributes, such as
      SameSite=Lax
      or
      Strict
      .
    • Only rely on localStorage if you can support token rotation, which a platform like Auth0 supports out of the box.

  • Implement Role-Based Access Control (RBAC):

    • Ensure your backend enforces proper authorization.
    • Check user roles and permissions on each protected route.

  • Secure OAuth callbacks:

  • Password storage:

    • Hash passwords with strong algorithms like
      bcrypt
      or
      Argon2
      , and never store passwords in plaintext.

Securing Next.js Applications (SSR/SSG Concerns)

  • Avoid Accidental Data Exposure:
    • When using server-side components or similar methods, send only non-sensitive data to the client.

Proactive prompt:

Integrate safeguards into our server-side components and data-handling logic to ensure that only non-sensitive data is sent to the client. Review and update our data serialization processes so sensitive information—such as user credentials, private identifiers, or security tokens—is stripped or masked before rendering or sending responses.

Defensive prompt:

Audit our codebase to verify that all server-side components and API responses only transmit non-sensitive data to the client. Identify any areas where sensitive information might be unintentionally exposed and provide specific remediation recommendations to minimize data exposure.
  • Escape Output to Prevent XSS:
    • Use React's default escaping and avoid
      dangerouslySetInnerHTML
      with unsanitized content.
    • If you must use raw HTML, sanitize it with a library like
      DOMPurify
      .

Proactive prompt:

Update our React application codebase to ensure all output is securely escaped to prevent XSS. Confirm that we rely on React's default escaping mechanism and avoid using dangerouslySetInnerHTML with unsanitized content. For cases where raw HTML is necessary, integrate DOMPurify to sanitize the content before rendering and document these changes with inline comments explaining how they mitigate XSS vulnerabilities.

Defensive prompt:

Audit our codebase to verify that output escaping is consistently implemented to prevent XSS. Ensure that all dynamic content is rendered using React's default escaping and that any instance of `dangerouslySetInnerHTML` is accompanied by proper sanitization via `DOMPurify` or an equivalent library. Identify any occurrences where unsanitized HTML might be used and provide detailed recommendations for remediation.
  • Set up a Content Security Policy (CSP):
    • Configure strict CSP headers to limit the sources from which content can be loaded.

Proactive prompt:

Integrate a strict Content Security Policy (CSP) into our project by configuring our server or platform to send CSP headers that restrict content sources to trusted origins (e.g., using `default-src 'self" and additional directives as needed). Ensure all external resources are explicitly allowed and document exceptions with justifications.

Defensive prompt:

Audit our codebase to verify that every HTTP response includes a strict CSP header limiting content sources to trusted domains. Identify any endpoints or configurations where the CSP is missing or overly permissive and provide detailed recommendations for tightening the policy.

Handling Sensitive Data and Environment Variables

  • Keep secrets out of code:
    • Use environment variables for API keys, database credentials, and other secrets.
    • Ensure
      .env
      files are in
      .gitignore
      and not committed to version control.
    • If you are building in public, exercise extreme caution not to share any API secrets on screenshots you are sharing or while live-streaming.

Proactive prompt:

Update our project to manage secrets securely: ensure all API keys, database credentials, and sensitive information are loaded from environment variables; confirm that `.env` files are properly added to `.gitignore` to prevent accidental commits; and implement file reminders to avoid exposing secrets in screenshots or during live streams. Include documentation that explains these practices and their importance.

Defensive prompt:

Audit our codebase to verify that no sensitive secrets (API keys, database credentials, etc.) are hard-coded. Check that environment variables are used appropriately and that `.env` files are excluded from version control via `.gitignore`. Identify any potential leaks in the code or documentation that might reveal secrets during public demos or live streams and provide remediation recommendations.

Attention! If you suspect that a hard-coded secret was processed by a third-party AI tool, you should treat it as potentially compromised and immediately revoke or rotate that secret, review the service's data handling policies, and implement safeguards (like secret scanning) to prevent such exposures in the future.

  • Secure storage of secrets:

    • Use your deployment platform's secret management features to store secrets, such as Vercel's Environment Variables.

  • Configuration checks:

    • Verify that debug configurations are disabled in production. For example, you can set the
      NODE_ENV
      environment variable to
      production
      .

  • Encryption:

    • Serve your application over HTTPS and consider encrypting sensitive data at rest.

Mitigating Common Web Security Risks

  • Cross-Site Scripting (XSS):

    • Sanitize user inputs, use React's escaping features and set a strong CSP.

  • Cross-Site Request Forgery (CSRF):

    • Use CSRF tokens on state-changing endpoints or use SameSite cookie attributes.

  • SQL/NoSQL injection:

    • Validate and sanitize all inputs, and always use parameterized queries.

  • Cross-Origin Resource Sharing (CORS):

Proactive prompt:

Integrate secure CORS configuration into our project by ensuring that our server only accepts requests from a predefined list of trusted origins. Update the configuration (e.g., via middleware or server settings) to replace any permissive settings (like "*" for allowed origins) with explicit, secure origins, and document the rationale for each allowed domain.

Defensive prompt:

Audit our codebase to verify that CORS is configured securely across all endpoints: ensure that only trusted origins are allowed and that no endpoint is using overly permissive settings such as "*" for the allowed origins. Identify any insecure CORS configurations and provide detailed remediation recommendations.
  • Secure communication:
    • Enforce HTTPS and configure HSTS to prevent downgrade attacks.

Deployment and Infrastructure Security

Even a securely coded application can be compromised if deployed poorly. When using Bolt.new or v0 to deploy your app (or exporting the code to deploy on your own), consider the following:

Safe Deployment Practices

  • Review before deploying:

    • Always review project files for secrets, debug flags, or test routes before hitting "Deploy". ALWAYS.

  • Environment settings:

    • As outlined earlier, set environment variables securely for production using your platform's secret store. Do not hardcode secrets in code.
    • Ensure
      NODE_ENV
      is set to
      production
      to disable verbose error messages.

  • Deployment targets:

    • Use the secure deployment mechanisms provided by your deployment platforms.
    • Secure custom domains with HTTPS and proper TLS certificates.

  • Monitor deployed applications:

    • Use logging and monitoring tools to detect unusual activity.
    • Set up alerts for error spikes and suspicious requests or control your usage-related costs.
    • If using Auth0, leverage "Suspicious IP Throttling" to block traffic from any IP address that rapidly attempts too many logins or signups, protecting your applications from high-velocity attacks that target multiple accounts.

Dependency Management and Supply Chain Protection

  • Lock versions with lockfiles:

    • Use
      package-lock.json
      or
      pnpm-lock.yaml
      to ensure consistent dependency versions.

  • Audit dependencies:

    • Regularly run security audits with
      npm audit
      and update dependencies promptly.

  • Beware of typosquatting:

    • Verify package names and sources. Only install well-known, widely used packages.

  • Disable Unnecessary Post-Install Scripts:

    • Consider installing with the
      --ignore-scripts
      flag in production if feasible.

Securing CI/CD Pipelines

  • Principle of least privilege:

    • Use minimal-scoped tokens for CI/CD and restrict who can trigger deployments.

  • Automated security gates:

    • Integrate security checks (tests, linting, SAST) into your CI/CD pipelines.

  • Manage CI/CD secrets securely:

    • Use encrypted secret stores your CI platform provides and avoid printing secrets in logs.

  • Container and infrastructure security:

    • If using Docker, use minimal base images, avoid running as root, and configure firewall rules for cloud deployments.

  • Backup and rollback:

    • Maintain regular backups and have a rollback plan in case a deployment introduces issues.

Security Checklist for AI-Generated Applications

Use this step-by-step checklist to ensure your AI-generated full-stack app is secure and reliable:

  1. Review AI-Generated Code:

    • Manually inspect frontend and backend code for security issues.
    • Remove hardcoded secrets and debug/test configurations.

  2. Run Static Analysis/Linters:

    • Integrate tools like SonarQube, ESLint, or Snyk.
    • Address any flagged vulnerabilities before deployment.

  3. Verify Dependencies:

    • Review your
      package.json
      for unnecessary or insecure dependencies.
    • Run
      npm audit
      and lock dependency versions.

  4. Implement Secure Authentication and Authorization:

    • Use established libraries or services like Auth0.
    • Protect sessions with HTTP-only, Secure cookies.
    • Enforce proper role-based access control.

  5. Enforce Input Validation:

    • Validate and sanitize every API input.
    • Test APIs with malicious payloads to ensure protection.

  6. Test for Common Vulnerabilities:

    • Perform penetration testing for XSS, SQL injection, CSRF, etc.
    • Use automated tools (OWASP ZAP, Burp Suite) where possible.

  7. Secure Secrets & Configuration:

    • Ensure all sensitive data is stored in environment variables.
    • Confirm that
      .env
      files and similar configurations are not committed.

  8. Set Secure HTTP Headers:

    • Implement CSP, XSS Protection, HSTS, and other secure headers.
    • Use Helmet or configure headers via Next.js/Vercel settings.

  9. Configure Deployment Security:

    • Set your app's environment to production.
    • Enforce HTTPS and restrict access to sensitive endpoints.
    • Monitor logs and set up alerts for unusual activity.

  10. Continuous Monitoring and Patching:

    • Regularly audit dependencies and code changes.
    • Stay updated on new vulnerabilities and apply patches quickly.

  11. Prepare an Emergency Response Plan:

    • Have procedures for immediate rollback or patch deployment.
    • Regularly backup data and review incident response protocols.

Conclusion

By combining the efficiency of AI-powered platforms like Bolt.new and v0 with diligent security practices, you can build full-stack applications in TypeScript, React (Next.js), and Node.js that are reliable and secure. Always review AI-generated code, employ robust testing and monitoring, and adhere to best deployment and dependency management practices. This layered approach will help protect you and your users from potential security risks.

Happy coding, and stay secure!