codelessgenie blog

Sanitizing User Input in URL with amp-bind-macro: A Complete Guide

In today's web applications, user input handling is a critical security concern, especially when dealing with URL generation and redirection. AMP (Accelerated Mobile Pages) introduces additional constraints through its security model, making proper input sanitization even more crucial. When using amp-bind-macro to create reusable URL generation logic, understanding how to properly sanitize user input becomes essential for both security and AMP compliance.

This guide will walk you through comprehensive techniques for sanitizing user input when constructing URLs with amp-bind-macro, ensuring your AMP pages remain secure, functional, and compliant with AMP's strict security policies.

2026-07

Table of Contents#

  1. Introduction
  2. Understanding the Problem
  3. AMP Security Model Overview
  4. Input Sanitization Techniques
  5. Implementing amp-bind-macro with Sanitization
  6. Best Practices and Common Patterns
  7. Real-World Example
  8. Testing and Validation
  9. Conclusion
  10. References

Understanding the Problem#

The Risks of Unsanitized Input#

When user input is directly incorporated into URLs without proper sanitization, several security vulnerabilities can arise:

  • Open Redirect Attacks: Malicious actors can redirect users to phishing sites
  • JavaScript Injection: Despite AMP's restrictions, improper handling can lead to script injection
  • Parameter Pollution: Conflicting or malicious parameters can break application logic
  • XSS Vulnerabilities: Cross-site scripting attacks through URL parameters

AMP-Specific Challenges#

AMP's security model imposes restrictions that affect URL handling:

  • Limited JavaScript execution
  • Strict CSP (Content Security Policy) requirements
  • Validation rules for amp-bind expressions
  • Restrictions on dynamic URL protocols

AMP Security Model Overview#

AMP Bind Limitations#

AMP's amp-bind component has specific constraints for URL generation:

<!-- Allowed -->
<a [href]="'/search?q=' + encodeURIComponent(searchTerm)">Search</a>
 
<!-- Potentially problematic without sanitization -->
<a [href]="'javascript:alert(1)' + userInput">Dangerous Link</a>

URL Protocol Restrictions#

AMP only allows certain URL protocols in bindings:

  • http://, https://
  • mailto:, tel:
  • # fragment identifiers
  • Relative URLs

Input Sanitization Techniques#

1. URL Encoding#

The most fundamental sanitization technique is proper URL encoding:

<amp-state id="app">
  <script type="application/json">
    {
      "userInput": "",
      "baseUrl": "https://api.example.com/search"
    }
  </script>
</amp-state>
 
<amp-bind-macro id="buildSafeUrl" 
                arguments="input" 
                expression="baseUrl + '?query=' + encodeURIComponent(input)">
</amp-bind-macro>
 
<input type="text" on="input-throttled:AMP.setState({userInput: event.value})">
<a [href]="buildSafeUrl(userInput)" class="button">Search</a>

2. Whitelist-Based Validation#

Create macros that validate input against allowed patterns:

<amp-bind-macro id="validateAndSanitizeInput" 
                arguments="input" 
                expression="
                  input.replace(/[^a-zA-Z0-9\s\-_]/g, '').trim().substring(0, 100)
                ">
</amp-bind-macro>
 
<amp-bind-macro id="buildSearchUrl"
                arguments="searchTerm"
                expression="
                  '/search?q=' + encodeURIComponent(validateAndSanitizeInput(searchTerm))
                ">
</amp-bind-macro>

3. Parameter-Specific Sanitization#

Different URL parameters require different sanitization approaches:

<amp-bind-macro id="sanitizeEmailParam"
                arguments="email"
                expression="
                  email.replace(/[^a-zA-Z0-9@._-]/g, '').toLowerCase()
                ">
</amp-bind-macro>
 
<amp-bind-macro id="sanitizeIdParam"
                arguments="id"
                expression="
                  id.replace(/[^0-9]/g, '')
                ">
</amp-bind-macro>

Implementing amp-bind-macro with Sanitization#

Basic Safe URL Construction Macro#

<!-- State initialization -->
<amp-state id="appState">
  <script type="application/json">
    {
      "searchQuery": "",
      "category": "all",
      "sortBy": "relevance"
    }
  </script>
</amp-state>
 
<!-- Sanitization and URL building macros -->
<amp-bind-macro id="sanitizeInput"
                arguments="input,maxLength"
                expression="
                  encodeURIComponent(
                    input.replace(/[<>\"']/g, '')
                         .trim()
                         .substring(0, maxLength || 255)
                  )
                ">
</amp-bind-macro>
 
<amp-bind-macro id="buildSearchUrl"
                arguments="query,category,sort"
                expression="
                  '/results?q=' + sanitizeInput(query, 100) + 
                  '&category=' + sanitizeInput(category, 50) +
                  '&sort=' + sanitizeInput(sort, 20) +
                  '&source=amp'
                ">
</amp-bind-macro>
 
<!-- Usage -->
<input type="text" 
       placeholder="Search..." 
       on="input-throttled:AMP.setState({searchQuery: event.value})">
       
<select on="change:AMP.setState({category: event.value})">
  <option value="all">All Categories</option>
  <option value="tech">Technology</option>
  <option value="news">News</option>
</select>
 
<a [href]="buildSearchUrl(searchQuery, category, sortBy)" 
   class="search-button">
  Search Now
</a>

Advanced Macro with Multiple Validation Layers#

<amp-bind-macro id="validateUrlParams"
                arguments="params"
                expression="
                  {
                    query: params.query ? params.query.replace(/[^\w\s-]/g, '') : '',
                    page: Math.max(1, Math.min(100, parseInt(params.page) || 1)),
                    limit: [10, 25, 50].includes(parseInt(params.limit)) ? 
                           parseInt(params.limit) : 25
                  }
                ">
</amp-bind-macro>
 
<amp-bind-macro id="constructApiUrl"
                arguments="params"
                expression="
                  '/api/v1/search?q=' + 
                  encodeURIComponent(params.query ? params.query.replace(/[^\w\s-]/g, '') : '') +
                  '&page=' + Math.max(1, Math.min(100, parseInt(params.page) || 1)) +
                  '&limit=' + ([10, 25, 50].includes(parseInt(params.limit)) ? parseInt(params.limit) : 25) +
                  '&format=json'
                ">
</amp-bind-macro>

Best Practices and Common Patterns#

1. Defense in Depth Approach#

Implement multiple layers of sanitization:

<amp-bind-macro id="multiLayerSanitization"
                arguments="userInput"
                expression="encodeURIComponent(userInput.replace(/[<>\"'&]/g, '').trim().substring(0, 100))">
</amp-bind-macro>

2. Use AMP-State for Configuration#

Store sanitization rules in AMP state for maintainability:

<amp-state id="validationRules">
  <script type="application/json">
    {
      "maxLengths": {
        "searchQuery": 100,
        "username": 50,
        "email": 254
      },
      "allowedPatterns": {
        "username": "^[a-zA-Z0-9_-]{3,50}$",
        "id": "^[0-9]{1,10}$"
      }
    }
  </script>
</amp-state>

3. Centralized Sanitization Macros#

Create reusable sanitization macros for consistent application-wide use:

<amp-bind-macro id="sanitizeForUrl"
                arguments="input,paramType"
                expression="encodeURIComponent((paramType == 'email' ? input.toString().replace(/[^a-zA-Z0-9@._-]/g, '') : paramType == 'numeric' ? input.toString().replace(/[^0-9]/g, '') : input.toString().replace(/[^a-zA-Z0-9\s_-]/g, '')).substring(0, 255))">
</amp-bind-macro>

Real-World Example#

E-commerce Product URL Generation#

<!-- AMP State -->
<amp-state id="productState">
  <script type="application/json">
    {
      "selectedCategory": "electronics",
      "searchTerm": "",
      "minPrice": "",
      "maxPrice": "",
      "sortOption": "popularity"
    }
  </script>
</amp-state>
 
<!-- Sanitization Macros -->
<amp-bind-macro id="sanitizePrice"
                arguments="price"
                expression="
                  Math.max(0, Math.min(10000, 
                    parseInt(price.replace(/[^0-9]/g, '') || 0)
                  ))
                ">
</amp-bind-macro>
 
<amp-bind-macro id="sanitizeCategory"
                arguments="category"
                expression="
                  ['electronics', 'clothing', 'books', 'home'].includes(category) ? 
                  category : 'all'
                ">
</amp-bind-macro>
 
<amp-bind-macro id="buildProductSearchUrl"
                arguments="term,category,minPrice,maxPrice,sort"
                expression="'/products?search=' + encodeURIComponent(term.replace(/[^\w\s-]/g, '').substring(0, 50)) + '&category=' + sanitizeCategory(category) + '&min_price=' + sanitizePrice(minPrice) + '&max_price=' + sanitizePrice(maxPrice) + '&sort=' + (['popularity', 'price_asc', 'price_desc', 'newest'].includes(sort) ? sort : 'popularity') + '&amp=true'">
</amp-bind-macro>
 
<!-- UI Implementation -->
<div class="filters">
  <input type="text" 
         placeholder="Search products..."
         on="input-throttled:AMP.setState({searchTerm: event.value})">
  
  <input type="number" 
         placeholder="Min price"
         on="input-throttled:AMP.setState({minPrice: event.value})">
  
  <input type="number" 
         placeholder="Max price"
         on="input-throttled:AMP.setState({maxPrice: event.value})">
  
  <select on="change:AMP.setState({sortOption: event.value})">
    <option value="popularity">Popularity</option>
    <option value="price_asc">Price: Low to High</option>
    <option value="price_desc">Price: High to Low</option>
    <option value="newest">Newest First</option>
  </select>
</div>
 
<a [href]="buildProductSearchUrl(searchTerm, selectedCategory, minPrice, maxPrice, sortOption)" 
   class="cta-button">
  Apply Filters
</a>

Testing and Validation#

AMP Validator Compliance#

Ensure your macros pass AMP validation:

<!-- This will pass validation -->
<amp-bind-macro id="safeUrlBuilder"
                arguments="param"
                expression="'/path?value=' + encodeURIComponent(param)">
</amp-bind-macro>
 
<!-- This may cause validation warnings -->
<amp-bind-macro id="unsafeUrlBuilder"
                arguments="param"
                expression="'javascript:void(' + param + ')'">
</amp-bind-macro>

Testing Strategies#

  1. Boundary Testing: Test with maximum length inputs
  2. Special Character Testing: Test with HTML, SQL, and script tags
  3. Encoding Verification: Ensure proper URL encoding occurs
  4. AMP Validator: Always test with AMP's official validator
<!-- Test your implementation with various inputs -->
<amp-state id="testCases">
  <script type="application/json">
    {
      "tests": [
        "normal input",
        "input with <script>",
        "very-long-input-".repeat(10),
        "input with & special?chars"
      ]
    }
  </script>
</amp-state>
 
<div [text]="buildSearchUrl(testCases.tests[0])">Test Result 1</div>
<div [text]="buildSearchUrl(testCases.tests[1])">Test Result 2</div>

Conclusion#

Proper input sanitization in AMP pages, especially when using amp-bind-macro for URL generation, is crucial for security and compliance. By implementing the techniques outlined in this guide:

  • You protect users from redirect attacks and XSS vulnerabilities
  • Ensure AMP validator compliance
  • Create maintainable, reusable sanitization logic
  • Build robust applications that handle edge cases gracefully

Remember that security is an ongoing process. Regularly review and update your sanitization logic to address new threats and always test your implementation with the AMP validator.

References#

Official Documentation#

Security Resources#

Testing Tools#

Further Reading#