Treat all data as untrusted by default
All data should be considered untrusted unless explicitly validated and safely handled. This applies to client-side input, API responses, third-party integrations, internal services and microservices, cached responses, browser storage (localStorage, sessionStorage), and hidden form fields.
@Pattern constraint for regex validation
@Pattern validates that a CharSequence matches a regular expression. The annotation syntax is @Pattern(regex=, flag=). It checks if the annotated string matches the specified regex considering the given flag match.
@Digits constraint for numeric validation
@Digits validates numeric values. The annotation syntax is @Digits(integer=, fraction=). It checks whether the annotated value is a number having up to the specified integer digits and fraction fractional digits. Supported data types are: BigDecimal, BigInteger, CharSequence, byte, short, int, long and their respective wrappers, plus any sub-type of Number supported by Hibernate Validator.
@Size constraint for length validation
@Size validates that an element's size is between min and max (inclusive). The annotation syntax is @Size(min=, max=). Supported data types are: CharSequence, Collection, Map, and Arrays.
@Past and @Future constraints for date validation
@Past checks whether the annotated date is in the past. @Future checks whether the annotated date is in the future. Supported data types are: java.util.Date, java.util.Calendar, java.time.chrono.ChronoZonedDateTime, java.time.Instant, java.time.OffsetDateTime.
@Min and @Max constraints for numeric range validation
@Min(value=) checks whether the annotated value is higher than or equal to the specified minimum. @Max(value=) checks whether the annotated value is lower than or equal to the specified maximum. Supported data types are: BigDecimal, BigInteger, byte, short, int, long and their respective wrappers, any sub-type of CharSequence (numeric value represented by character sequence is evaluated), any sub-type of Number.
@Valid annotation for cascading validation
@Valid enables cascading validation to validate nested or graph-structured beans. When applied to a field, it triggers validation of the entire bean graph in one operation.
Hibernate Validator additional constraints
Hibernate Validator provides additional constraints beyond JSR303: @CreditCardNumber, @EAN, @Email, @Length, @Range, @ScriptAssert, @URL. The @SafeHtml constraint has been deprecated as of Hibernate Validator 6.1.0.Final and 6.0.18.Final and should not be used.
BindingResult object contains validation errors
Hibernate Validator returns a BindingResult object which contains a List<ObjectError>. The BindingResult can be checked with hasErrors() method and all errors retrieved with getAllErrors() method.
Custom error messages with message parameter
Custom error messages can be specified using the message parameter in validation annotations. For example: @Pattern(regexp = "[a-zA-Z0-9 ]", message="article.title.error"). Spring MVC will look up the message ID in a defined MessageSource.
Bean Validation definition and purpose
Bean Validation, also known as Jakarta Validation, is an application layer agnostic validation specification for Java that allows developers to define validation constraints on domain models once and reuse them across application tiers. Constraints and validators are written once, reducing duplication and ensuring uniformity.
Hibernate Validator Maven dependency
To use Hibernate Validator, add the dependency to pom.xml with groupId org.hibernate, artifactId hibernate-validator, and version USE_LATEST_VERSION.
Enable bean validation in Spring context.xml
To enable bean validation support in Spring, add <mvc:annotation-driven /> to the beans:beans configuration in context.xml.
Constraint application locations in Bean Validation
Validation constraints can be applied to fields and properties. For Bean Validation 1.1 and later, constraints can also be applied to parameters, return values, and constructors.
Combining multiple constraints
Validation annotations can be combined on the same field in any suitable way. For example, @Min(1) and @Max(5) can both be applied to the same field to enforce a range constraint.
Validate input ranges, not just format
Validate inputs against meaningful business ranges, not just format validation. A quantity can be a positive integer but still be wrong if it exceeds available stock or minimum order quantities. A discount can be numeric but still absurd. Validate that quantity is at least 1 and no more than available stock, dates are in the future and not more than a specified period out, amounts are positive and do not exceed limits.
Validate combinations of fields, not just individual fields
Fields that are individually valid can be collectively invalid. A booking for a valid room with a valid check-in date and check-out date is still invalid if check-out precedes check-in. A transfer between two valid accounts is invalid if they belong to different customers and the user lacks permission to act on both. Write validation rules that describe legal field combinations and enforce them server-side.
Treat all request fields as untrusted input
Every field in a request is an input, including fields in hidden form controls, disabled form controls, fields set by JavaScript, and fields set in a previous response and expected to receive unchanged. Fields that are not editable in the UI are fully editable at the HTTP layer. If a field matters, validate it as if it came from an attacker.
XML External Entity Prevention Cheat Sheet available
The XML External Entity Prevention Cheat Sheet is available in the OWASP series with code examples in Java, C#, C++, and PHP.
Input Validation Cheat Sheet available
The Input Validation Cheat Sheet is available in the OWASP series with code examples in Java.
Injection Prevention in Java Cheat Sheet available
The Injection Prevention in Java Cheat Sheet is available in the OWASP series for Java-specific injection prevention techniques.
Software design concept: cheap validation first in DoS defense
Validate user input using cheap resource checks first to reduce impact on CPU, memory, and bandwidth as soon as possible. Perform more expensive validation afterward.
Input validation DoS defense: limit total request size
Enforce a maximum total request size limit to make it harder for resource-consuming DoS attacks to succeed and prevent resource exhaustion.
Input validation DoS defense: prevent input-based resource allocation
Do not allow user input to directly determine resource allocation (CPU, memory, connections) to prevent DoS attacks that exploit resource exhaustion through specially crafted inputs.
Input validation DoS defense: prevent input-based function and threading interaction
Prevent unfiltered user input from determining how many times a function executes or how intensive CPU consumption becomes. User input should not directly influence resource-intensive operations to prevent resource exhaustion DoS.
Puzzles and CAPTCHAs do not defend against DoS attacks
While input-based puzzles like CAPTCHAs or simple math problems can prevent functionality abuse (such as email flooding from forms), they do not help defend against DoS attacks.
iOS deep link security controls
Implement authentication checks on any view controllers or endpoints accessed via deep links. Configure and validate Universal Links using apple-app-site-association files for secure deep linking. Sanitize and validate all parameters received through deep links to prevent injection attacks. Ensure unauthorized users are redirected to the login screen, preventing direct access to sensitive parts of the app without proper authentication.
Mobile input validation
Validate and sanitize user input in mobile applications. See the Input Validation Cheat Sheet for more information.
Mobile output validation
Validate and sanitize output in mobile applications to prevent injection and execution attacks.
Secure coding basics: Input validation
Verify that all input data is valid and of the expected type, format, and length before processing it. This can help prevent attacks such as SQL injection and buffer overflows.
SSRF Case 2 Validation Flow
For SSRF Case 2, the validation flow is: (1) Apply first validation on IP address or domain name using libraries from Case 1. (2) Apply block-list validation: for IP addresses, verify it is a public one; for domain names, verify it is public by resolving against internal DNS (should return not found) and retrieve all A + AAAA records to verify the resolved IPs are public. (3) Validate protocol parameter against allowlist (HTTP or HTTPS only). (4) Validate parameter name allowing only [a-z]{1,10}. (5) Validate token allowing only [a-zA-Z0-9]{20}. (6) Validate any business data. (7) Build HTTP POST request using only validated information and send it (with redirects disabled).
Input Validation for String Data in SSRF Case 1
For string data in Case 1 (allowlist approach), input validation can use regex for simple formats or libraries from the string object for complex formats. Example Java validation using regex: if(Pattern.matches("[a-zA-Z0-9\\s\\-]{1,50}", userInput)) to validate business data such as tokens or zip codes.
IP Address Validation Libraries by Language
For SSRF Case 1 IP address validation: JAVA uses InetAddressValidator.isValid() from Apache Commons Validator (NOT exposed to Hex, Octal, Dword, URL, Mixed encoding bypasses). .NET uses IPAddress.TryParse() (exposed to Hex, Octal, Dword, Mixed encoding but NOT URL encoding). JavaScript uses the ip-address npm library (NOT exposed to these bypasses). Ruby uses IPAddr class from SDK (NOT exposed to these bypasses). Use the output value from these methods as the IP address to compare against the allowlist.
IP Address Allowlist Validation in SSRF Case 1
After validating the format of an IP address (both IPv4 and IPv6), cross-check it against an allowlist of IP addresses belonging to identified and trusted applications using string strict comparison with case sensitivity.
Domain Name Validation Libraries by Language
For SSRF Case 1 domain name validation: JAVA uses DomainValidator.isValid() from Apache Commons Validator. .NET uses Uri.CheckHostName() from SDK. JavaScript uses the is-valid-domain npm library. Python uses validators.domain module. Ruby can use this regex (verified not to accept XSS payloads): ^(((?!-))(xn--|_{1,1})?[a-z0-9-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9-]{1,30}\.[a-z]{2,})$ These libraries do not perform DNS resolution queries.
Domain Name Allowlist in SSRF Case 1
Build an allowlist with all domain names of every identified and trusted application. Verify that the domain name received is part of this allowlist using string strict comparison with case sensitivity.
DNS Pinning Prevention for Domain Names in SSRF Case 1
To address DNS pinning bypass attacks: (1) Ensure that domains in the allowlist are resolved by internal DNS servers first in the chain of DNS resolvers. (2) Monitor the allowlist domains to detect when any of them resolves to a local IP address (V4 + V6) or internal IP addresses outside the expected range. (3) Use tools like a Python3 script with ipaddress and dnspython modules to verify DNS records resolve only to public IPs for trusted domains.
URL Validation in SSRF Case 1
Do not accept complete URLs from users because URLs are difficult to validate and the parser can be abused depending on the technology used. If network-related information is needed, accept only a valid IP address or domain name instead.
SSRF Case 2 Block-List Approach
In SSRF Case 2 (where users control URLs to external resources), use a block-list approach to identify what the application should NOT do, since an allowlist of external IPs/domains cannot be maintained upfront. The application must verify that IP addresses are public and not part of private network ranges, localhost, or IPv4/v6 link-local addresses.
Private IP Ranges for SSRF Block-List
The private IP ranges that must be blocked in SSRF block-lists include: RFC1918 Private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), Localhost (127.0.0.0/8, 0.0.0.0/8, ::1/128), Multicast (224.0.0.0/4, ff00::/8), AWS IMDS (169.254.169.254, metadata.amazonaws.com), GCP Metadata (metadata.google.internal, 169.254.169.254), and Azure IMDS (169.254.169.254).
Dangerous URL Redirect - PHP
Vulnerable PHP code that reads a URL parameter from the query string and redirects to it: $redirect_url = $_GET['url']; header("Location: " . $redirect_url); Additionally, code after the header() function continues to execute, so if the user ignores the redirect, they may access the rest of the page.
Unvalidated Redirects and Forwards definition
Unvalidated redirects and forwards occur when a web application accepts untrusted input that could cause the application to redirect the request to a URL contained within untrusted input. By modifying untrusted URL input to a malicious site, an attacker can launch phishing scams and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts may have a more trustworthy appearance. These attacks can also be used to maliciously craft a URL that passes the application's access control check and forwards the attacker to privileged functions they would normally not be able to access.
Safe URL Redirect - Java
In Java, safe redirects use response.sendRedirect() with an explicitly declared URL hardcoded in the code, not from user input: response.sendRedirect("http://www.mysite.com");
Safe URL Redirect - PHP
In PHP, safe redirects use header("Location: URL") with an explicitly declared URL: header("Location: http://www.mysite.com"); followed by exit; to prevent the rest of the code from executing.
Safe URL Redirect - ASP.NET
In ASP.NET, safe redirects use Response.Redirect() with an explicitly declared URL: Response.Redirect("~/folder/Login.aspx");
Safe URL Redirect - Rails
In Rails, safe redirects use redirect_to with an explicitly declared path: redirect_to login_path;
Safe URL Redirect - Rust actix web
In Rust actix web, safe redirects use HttpResponse::Found() with an explicitly declared LOCATION header: Ok(HttpResponse::Found().insert_header((header::LOCATION, "https://mysite.com/")).finish());
Dangerous URL Redirect - Java
Vulnerable Java code that reads a URL parameter from untrusted user input and redirects to it: response.sendRedirect(request.getParameter("url")); This allows attackers to redirect users to malicious sites.
Dangerous URL Redirect - ASP.NET
Vulnerable C# .NET code that reads a URL from the query string and redirects to it: string url = request.QueryString["url"]; Response.Redirect(url); This allows attackers to redirect users to malicious sites.
Dangerous URL Redirect - Rails
Vulnerable Rails code that redirects to a user-supplied parameter: redirect_to params[:url]; This allows attackers to redirect users to malicious sites.
Dangerous URL Redirect - Rust actix web
Vulnerable Rust actix web code that uses a user-supplied query string value in the LOCATION header: Ok(HttpResponse::Found().insert_header((header::LOCATION, query_string.path.as_str())).finish()); This allows attackers to redirect users to malicious sites.
ASP.NET MVC 2 open redirection vulnerability
ASP.NET MVC 1 & 2 websites are particularly vulnerable to open redirection attacks. To avoid this vulnerability, upgrade to MVC 3 or later. The vulnerability typically occurs in LogOn action code that redirects to a returnUrl parameter without validation: if (!String.IsNullOrEmpty(returnUrl)) { return Redirect(returnUrl); }
Dangerous Forward Example - Java servlet
Vulnerable Java servlet code that forwards requests to a user-supplied URL parameter: public class ForwardServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String query = request.getQueryString(); if (query.contains("fwd")) { String fwd = request.getParameter("fwd"); try { request.getRequestDispatcher(fwd).forward(request, response); } catch (ServletException e) { e.printStackTrace(); } } } } This allows attackers to bypass access control and access administrative functions by crafting a URL like http://www.example.com/function.jsp?fwd=admin.jsp
Prevention: Avoid redirects and forwards
The simplest way to prevent unvalidated redirect and forward attacks is to simply avoid using redirects and forwards entirely.
Prevention: Map user input to server-side URLs
Where possible, have the user provide a short name, ID, or token which is mapped server-side to a full target URL. This provides the highest degree of protection against attackers tampering with the URL. Be careful this does not introduce an enumeration vulnerability where a user could cycle through IDs to find all possible redirect targets.
Prevention: Validate and authorize user input URLs
If user input cannot be avoided, ensure that the supplied value is valid, appropriate for the application, and is authorized for the user. Both validation of format and authorization of destination must be checked.
Prevention: Allowlist trusted URLs
Sanitize input by creating a list of trusted URLs using an allowlist approach based on hosts or regex patterns. This should be based on an allowlist approach, not a denylist, to provide stronger security.
Prevention: Confirmation page before off-site redirect
Force all redirects to first go through a page notifying users that they are going off of your site, with the destination clearly displayed, and have them click a link to confirm the action.
Castor XML XXE vulnerability
Castor is a data binding framework for Java. XML features in Castor prior to version 1.3.3 are vulnerable to XXE and should be upgraded to the latest version.
XXE primary prevention: disable DTDs completely
The safest way to prevent XXE is always to disable DTDs (External Entities) completely. The method should be similar to setting the feature 'http://apache.org/xml/features/disallow-doctype-decl' to true on the XML parser factory.