All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.owasp.esapi.reference.DefaultValidator Maven / Gradle / Ivy

Go to download

The Enterprise Security API (ESAPI) project is an OWASP project to create simple strong security controls for every web platform. Security controls are not simple to build. You can read about the hundreds of pitfalls for unwary developers on the OWASP website. By providing developers with a set of strong controls, we aim to eliminate some of the complexity of creating secure web applications. This can result in significant cost savings across the SDLC.

There is a newer version: 2.5.5.0
Show newest version
/**
 * OWASP Enterprise Security API (ESAPI)
 *
 * This file is part of the Open Web Application Security Project (OWASP)
 * Enterprise Security API (ESAPI) project. For details, please see
 * http://www.owasp.org/index.php/ESAPI.
 *
 * Copyright (c) 2007 - The OWASP Foundation
 *
 * The ESAPI is published by OWASP under the BSD license. You should read and accept the
 * LICENSE before you use, modify, and/or redistribute this software.
 *
 * @author Jeff Williams Aspect Security
 * @author Jim Manico ([email protected]) Manico.net
 *
 * @created 2007
 */
package org.owasp.esapi.reference;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Encoder;
import org.owasp.esapi.Logger;
import org.owasp.esapi.SecurityConfiguration;
import org.owasp.esapi.ValidationErrorList;
import org.owasp.esapi.ValidationRule;
import org.owasp.esapi.Validator;
import org.owasp.esapi.errors.IntrusionException;
import org.owasp.esapi.errors.ValidationAvailabilityException;
import org.owasp.esapi.errors.ValidationException;
import org.owasp.esapi.reference.validation.CreditCardValidationRule;
import org.owasp.esapi.reference.validation.DateValidationRule;
import org.owasp.esapi.reference.validation.HTMLValidationRule;
import org.owasp.esapi.reference.validation.IntegerValidationRule;
import org.owasp.esapi.reference.validation.NumberValidationRule;
import org.owasp.esapi.reference.validation.StringValidationRule;

/**
 * Reference implementation of the Validator interface. This implementation
 * relies on the ESAPI Encoder, Java Pattern (regex), Date,
 * and several other classes to provide basic validation functions. This library
 * has a heavy emphasis on whitelist validation and canonicalization.
 *
 * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security
 * @author Jim Manico ([email protected]) Manico.net
 * @author Matt Seil (mseil .at. acm.org) 
 *
 * @since June 1, 2007
 * @see org.owasp.esapi.Validator
 */
public class DefaultValidator implements org.owasp.esapi.Validator {
	private static Logger logger = ESAPI.log();
    private static volatile Validator instance = null;

    public static Validator getInstance() {
        if ( instance == null ) {
            synchronized ( Validator.class ) {
                if ( instance == null ) {
                    instance = new DefaultValidator();
                }
            }
        }
        return instance;
    }

	/** A map of validation rules */
	private Map rules = new HashMap();

	/** The encoder to use for canonicalization */
	private Encoder encoder = null;

	/** The encoder to use for file system */
	private static Validator fileValidator = null;

	/** Initialize file validator with an appropriate set of codecs */
	static {
		List list = new ArrayList();
		list.add( "HTMLEntityCodec" );
		list.add( "PercentCodec" );
		Encoder fileEncoder = new DefaultEncoder( list );
		fileValidator = new DefaultValidator( fileEncoder );
	}


	/**
	 * Default constructor uses the ESAPI standard encoder for canonicalization.
	 */
	public DefaultValidator() {
	    this.encoder = ESAPI.encoder();
	}

	/**
	 * Construct a new DefaultValidator that will use the specified
	 * Encoder for canonicalization.
     *
     * @param encoder
     */
	public DefaultValidator( Encoder encoder ) {
	    this.encoder = encoder;
	}


	/**
	 * Add a validation rule to the registry using the "type name" of the rule as the key.
	 */
	public void addRule( ValidationRule rule ) {
		rules.put( rule.getTypeName(), rule );
	}

	/**
	 * Get a validation rule from the registry with the "type name" of the rule as the key.
	 */
	public ValidationRule getRule( String name ) {
		return rules.get( name );
	}


	/**
	 * Returns true if data received from browser is valid. Double encoding is treated as an attack. The
	 * default encoder supports html encoding, URL encoding, and javascript escaping. Input is canonicalized
	 * by default before validation.
	 *
	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
	 * @param input The actual user input data to validate.
	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
	 * @param maxLength The maximum post-canonicalized String length allowed.
	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
	 * @return The canonicalized user input.
	 * @throws IntrusionException
	 */
	public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws IntrusionException  {
		return isValidInput(context, input, type, maxLength, allowNull, true);
	}

        public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException  {
		return isValidInput(context, input, type, maxLength, allowNull, true, errors);
	}

	public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws IntrusionException  {
		try {
			getValidInput( context, input, type, maxLength, allowNull, canonicalize);
			return true;
		} catch( Exception e ) {
			return false;
		}
	}

        public boolean isValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errors) throws IntrusionException  {
		try {
			getValidInput( context, input, type, maxLength, allowNull, canonicalize);
			return true;
		} catch( ValidationException e ) {
			errors.addError( context, e );
			return false;
		}
	}

	/**
	 * Validates data received from the browser and returns a safe version.
	 * Double encoding is treated as an attack. The default encoder supports
	 * html encoding, URL encoding, and javascript escaping. Input is
	 * canonicalized by default before validation.
	 *
	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
	 * @param input The actual user input data to validate.
	 * @param type The regular expression name which maps to the actual regular expression from "ESAPI.properties".
	 * @param maxLength The maximum post-canonicalized String length allowed.
	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
	 * @return The canonicalized user input.
	 * @throws ValidationException
	 * @throws IntrusionException
	 */
	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull) throws ValidationException {
		return getValidInput(context, input, type, maxLength, allowNull, true);
	}

	/**
	 * Validates data received from the browser and returns a safe version. Only
	 * URL encoding is supported. Double encoding is treated as an attack.
	 *
	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
	 * @param input The actual user input data to validate.
	 * @param type The regular expression name which maps to the actual regular expression in the ESAPI validation configuration file
	 * @param maxLength The maximum String length allowed. If input is canonicalized per the canonicalize argument, then maxLength must be verified after canonicalization
     * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
	 * @param canonicalize If canonicalize is true then input will be canonicalized before validation
	 * @return The user input, may be canonicalized if canonicalize argument is true
	 * @throws ValidationException
	 * @throws IntrusionException
	 */
	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize) throws ValidationException {
		StringValidationRule rvr = new StringValidationRule( type, encoder );
		Pattern p = ESAPI.securityConfiguration().getValidationPattern( type );
		if ( p != null ) {
			rvr.addWhitelistPattern( p );
		} else {
            // Issue 232 - Specify requested type in exception message - CS
			throw new IllegalArgumentException("The selected type [" + type + "] was not set via the ESAPI validation configuration");
		}
		rvr.setMaximumLength(maxLength);
		rvr.setAllowNull(allowNull);
		rvr.setCanonicalize(canonicalize);
		return rvr.getValid(context, input);
	}

	/**
	 * Validates data received from the browser and returns a safe version. Only
	 * URL encoding is supported. Double encoding is treated as an attack. Input
	 * is canonicalized by default before validation.
	 *
	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
	 * @param input The actual user input data to validate.
	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
	 * @param maxLength The maximum String length allowed. If input is canonicalized per the canonicalize argument, then maxLength must be verified after canonicalization
	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
	 * @param errors If ValidationException is thrown, then add to error list instead of throwing out to caller
	 * @return The canonicalized user input.
	 * @throws IntrusionException
	 */
	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
		return getValidInput(context, input, type, maxLength, allowNull, true, errors);
	}

	/**
	 * Validates data received from the browser and returns a safe version. Only
	 * URL encoding is supported. Double encoding is treated as an attack.
	 *
	 * @param context A descriptive name for the field to validate. This is used for error facing validation messages and element identification.
	 * @param input The actual user input data to validate.
	 * @param type The regular expression name while maps to the actual regular expression from "ESAPI.properties".
	 * @param maxLength The maximum post-canonicalized String length allowed
	 * @param allowNull If allowNull is true then a input that is NULL or an empty string will be legal. If allowNull is false then NULL or an empty String will throw a ValidationException.
	 * @param canonicalize If canonicalize is true then input will be canonicalized before validation
	 * @param errors If ValidationException is thrown, then add to error list instead of throwing out to caller
	 * @return The user input, may be canonicalized if canonicalize argument is true
	 * @throws IntrusionException
	 */
	public String getValidInput(String context, String input, String type, int maxLength, boolean allowNull, boolean canonicalize, ValidationErrorList errors) throws IntrusionException {
		try {
			return getValidInput(context,  input,  type,  maxLength,  allowNull, canonicalize);
		} catch (ValidationException e) {
			errors.addError(context, e);
		}

		return "";
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean isValidDate(String context, String input, DateFormat format, boolean allowNull) throws IntrusionException {
		try {
			getValidDate( context, input, format, allowNull);
			return true;
		} catch( Exception e ) {
			return false;
		}
	}

        /**
	 * {@inheritDoc}
	 */
	public boolean isValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
	    getValidDate( context, input, format, allowNull, errors);
	    return errors.isEmpty();
	}

	/**
	 * {@inheritDoc}
	 */
	public Date getValidDate(String context, String input, DateFormat format, boolean allowNull) throws ValidationException, IntrusionException {
		
		ValidationErrorList vel = new ValidationErrorList();
		Date validDate =  getValidDate(context, input, format, allowNull, vel);
		
		if (vel.isEmpty()) {
		    return validDate;
		}
		
		throw vel.errors().get(0);
	}

	/**
	 * {@inheritDoc}
	 */
	public Date getValidDate(String context, String input, DateFormat format, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
	    Date safeDate = null;
	    DateValidationRule dvr = new DateValidationRule( "SimpleDate", encoder, format);
	    dvr.setAllowNull(allowNull);
	    safeDate = dvr.sanitize(context, input, errors);
	    if (!errors.isEmpty()) {
	        safeDate = null;
	    }
	    // error has been added to list, so return null
	    return safeDate;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull) throws IntrusionException {
		try {
			getValidSafeHTML( context, input, maxLength, allowNull);
			return true;
		} catch( Exception e ) {
			return false;
		}
	}

        /**
	 * {@inheritDoc}
	 */
	public boolean isValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
		try {
			getValidSafeHTML( context, input, maxLength, allowNull);
			return true;
		} catch( ValidationException e ) {
            errors.addError(context, e);
			return false;
		}
	}

	/**
	 * {@inheritDoc}
	 *
	 * This implementation relies on the OWASP AntiSamy project.
	 */
	public String getValidSafeHTML( String context, String input, int maxLength, boolean allowNull ) throws ValidationException, IntrusionException {
		HTMLValidationRule hvr = new HTMLValidationRule( "safehtml", encoder );
		hvr.setMaximumLength(maxLength);
		hvr.setAllowNull(allowNull);
		return hvr.getValid(context, input);
	}

	/**
	 * {@inheritDoc}
	 */
	public String getValidSafeHTML(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
		try {
			return getValidSafeHTML(context, input, maxLength, allowNull);
		} catch (ValidationException e) {
			errors.addError(context, e);
		}

		return "";
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean isValidCreditCard(String context, String input, boolean allowNull) throws IntrusionException {
		try {
			getValidCreditCard( context, input, allowNull);
			return true;
		} catch( Exception e ) {
			return false;
		}
	}

        /**
	 * {@inheritDoc}
	 */
	public boolean isValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
		try {
			getValidCreditCard( context, input, allowNull);
			return true;
		} catch( ValidationException e ) {
            errors.addError(context, e);
			return false;
		}
	}
	/**
	 * {@inheritDoc}
	 */
	public String getValidCreditCard(String context, String input, boolean allowNull) throws ValidationException, IntrusionException {
		CreditCardValidationRule ccvr = new CreditCardValidationRule( "creditcard", encoder );
		ccvr.setAllowNull(allowNull);
		return ccvr.getValid(context, input);
	}

	/**
	 * {@inheritDoc}
	 */
	public String getValidCreditCard(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException {
		try {
			return getValidCreditCard(context, input, allowNull);
		} catch (ValidationException e) {
			errors.addError(context, e);
		}

		return "";
	}

	/**
	 * {@inheritDoc}
	 *
	 * 

Note: On platforms that support symlinks, this function will fail canonicalization if directorypath * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real * path (/private/etc), not the symlink (/etc).

*/ public boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws IntrusionException { try { getValidDirectoryPath( context, input, parent, allowNull); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} * *

Note: On platforms that support symlinks, this function will fail canonicalization if directorypath * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real * path (/private/etc), not the symlink (/etc).

*/ public boolean isValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidDirectoryPath( context, input, parent, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * {@inheritDoc} */ public String getValidDirectoryPath(String context, String input, File parent, boolean allowNull) throws ValidationException, IntrusionException { try { if (isEmpty(input)) { if (allowNull) return null; throw new ValidationException( context + ": Input directory path required", "Input directory path required: context=" + context + ", input=" + input, context ); } File dir = new File( input ); // check dir exists and parent exists and dir is inside parent if ( !dir.exists() ) { throw new ValidationException( context + ": Invalid directory name", "Invalid directory, does not exist: context=" + context + ", input=" + input ); } if ( !dir.isDirectory() ) { throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not a directory: context=" + context + ", input=" + input ); } if ( !parent.exists() ) { throw new ValidationException( context + ": Invalid directory name", "Invalid directory, specified parent does not exist: context=" + context + ", input=" + input + ", parent=" + parent ); } if ( !parent.isDirectory() ) { throw new ValidationException( context + ": Invalid directory name", "Invalid directory, specified parent is not a directory: context=" + context + ", input=" + input + ", parent=" + parent ); } if ( !dir.getCanonicalFile().toPath().startsWith( parent.getCanonicalFile().toPath() ) ) { // Fixes GHSL-2022-008 throw new ValidationException( context + ": Invalid directory name", "Invalid directory, not inside specified parent: context=" + context + ", input=" + input + ", parent=" + parent ); } // check canonical form matches input String canonicalPath = dir.getCanonicalPath(); String canonical = fileValidator.getValidInput( context, canonicalPath, "DirectoryName", 255, false); if ( !canonical.equals( input ) ) { throw new ValidationException( context + ": Invalid directory name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context ); } return canonical; } catch (Exception e) { throw new ValidationException( context + ": Invalid directory name", "Failure to validate directory path: context=" + context + ", input=" + input, e, context ); } } /** * {@inheritDoc} */ public String getValidDirectoryPath(String context, String input, File parent, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidDirectoryPath(context, input, parent, allowNull); } catch (ValidationException e) { errors.addError(context, e); } return ""; } /** * {@inheritDoc} */ public boolean isValidFileName(String context, String input, boolean allowNull) throws IntrusionException { return isValidFileName( context, input, ESAPI.securityConfiguration().getAllowedFileExtensions(), allowNull ); } /** * {@inheritDoc} */ public boolean isValidFileName(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException { return isValidFileName( context, input, ESAPI.securityConfiguration().getAllowedFileExtensions(), allowNull, errors ); } /** * {@inheritDoc} */ public boolean isValidFileName(String context, String input, List allowedExtensions, boolean allowNull) throws IntrusionException { try { getValidFileName( context, input, allowedExtensions, allowNull); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} */ public boolean isValidFileName(String context, String input, List allowedExtensions, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidFileName( context, input, allowedExtensions, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * {@inheritDoc} */ public String getValidFileName(String context, String input, List allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException { if ((allowedExtensions == null) || (allowedExtensions.isEmpty())) { throw new ValidationException( "Internal Error", "getValidFileName called with an empty or null list of allowed Extensions, therefore no files can be uploaded" ); } String canonical = ""; // detect path manipulation try { if (isEmpty(input)) { if (allowNull) return null; throw new ValidationException( context + ": Input file name required", "Input required: context=" + context + ", input=" + input, context ); } // do basic validation canonical = new File(input).getCanonicalFile().getName(); getValidInput( context, input, "FileName", 255, true ); File f = new File(canonical); String c = f.getCanonicalPath(); String cpath = c.substring(c.lastIndexOf(File.separator) + 1); // the path is valid if the input matches the canonical path if (!input.equals(cpath)) { throw new ValidationException( context + ": Invalid file name", "Invalid directory name does not match the canonical path: context=" + context + ", input=" + input + ", canonical=" + canonical, context ); } } catch (IOException e) { throw new ValidationException( context + ": Invalid file name", "Invalid file name does not exist: context=" + context + ", canonical=" + canonical, e, context ); } // verify extensions Iterator i = allowedExtensions.iterator(); while (i.hasNext()) { String ext = i.next(); if (input.toLowerCase().endsWith(ext.toLowerCase())) { return canonical; } } throw new ValidationException( context + ": Invalid file name does not have valid extension ( "+allowedExtensions+")", "Invalid file name does not have valid extension ( "+allowedExtensions+"): context=" + context+", input=" + input, context ); } /** * {@inheritDoc} */ public String getValidFileName(String context, String input, List allowedParameters, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidFileName(context, input, allowedParameters, allowNull); } catch (ValidationException e) { errors.addError(context, e); } return ""; } /** * {@inheritDoc} */ public boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws IntrusionException { try { getValidNumber(context, input, minValue, maxValue, allowNull); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} */ public boolean isValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidNumber(context, input, minValue, maxValue, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * {@inheritDoc} */ public Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull) throws ValidationException, IntrusionException { Double minDoubleValue = new Double(minValue); Double maxDoubleValue = new Double(maxValue); return getValidDouble(context, input, minDoubleValue.doubleValue(), maxDoubleValue.doubleValue(), allowNull); } /** * {@inheritDoc} */ public Double getValidNumber(String context, String input, long minValue, long maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidNumber(context, input, minValue, maxValue, allowNull); } catch (ValidationException e) { errors.addError(context, e); } return null; } /** * {@inheritDoc} */ public boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws IntrusionException { try { getValidDouble( context, input, minValue, maxValue, allowNull ); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} */ public boolean isValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidDouble( context, input, minValue, maxValue, allowNull ); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * {@inheritDoc} */ public Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull) throws ValidationException, IntrusionException { NumberValidationRule nvr = new NumberValidationRule( "number", encoder, minValue, maxValue ); nvr.setAllowNull(allowNull); return nvr.getValid(context, input); } /** * {@inheritDoc} */ public Double getValidDouble(String context, String input, double minValue, double maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidDouble(context, input, minValue, maxValue, allowNull); } catch (ValidationException e) { errors.addError(context, e); } return new Double(Double.NaN); } /** * {@inheritDoc} */ public boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws IntrusionException { try { getValidInteger( context, input, minValue, maxValue, allowNull); return true; } catch( ValidationException e ) { return false; } } /** * {@inheritDoc} */ public boolean isValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidInteger( context, input, minValue, maxValue, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * {@inheritDoc} */ public Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull) throws ValidationException, IntrusionException { IntegerValidationRule ivr = new IntegerValidationRule( "number", encoder, minValue, maxValue ); ivr.setAllowNull(allowNull); return ivr.getValid(context, input); } /** * {@inheritDoc} */ public Integer getValidInteger(String context, String input, int minValue, int maxValue, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidInteger(context, input, minValue, maxValue, allowNull); } catch (ValidationException e) { errors.addError(context, e); } // error has been added to list, so return original input return null; } /** * {@inheritDoc} */ public boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws IntrusionException { try { getValidFileContent( context, input, maxBytes, allowNull); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} */ public boolean isValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidFileContent( context, input, maxBytes, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * {@inheritDoc} */ public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException { if (isEmpty(input)) { if (allowNull) return null; throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + Arrays.toString(input), context ); } long esapiMaxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize(); if (input.length > esapiMaxBytes ) throw new ValidationException( context + ": Invalid file content can not exceed " + esapiMaxBytes + " bytes", "Exceeded ESAPI max length", context ); if (input.length > maxBytes ) throw new ValidationException( context + ": Invalid file content can not exceed " + maxBytes + " bytes", "Exceeded maxBytes ( " + input.length + ")", context ); return input; } /** * {@inheritDoc} */ public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidFileContent(context, input, maxBytes, allowNull); } catch (ValidationException e) { errors.addError(context, e); } // return empty byte array on error return new byte[0]; } /** * {@inheritDoc} * *

Note: On platforms that support symlinks, this function will fail canonicalization if directorypath * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real * path (/private/etc), not the symlink (/etc).

*/ public boolean isValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull) throws IntrusionException { return( isValidFileName( context, filename, allowNull ) && isValidDirectoryPath( context, directorypath, parent, allowNull ) && isValidFileContent( context, content, maxBytes, allowNull ) ); } /** * {@inheritDoc} * *

Note: On platforms that support symlinks, this function will fail canonicalization if directorypath * is a symlink. For example, on MacOS X, /etc is actually /private/etc. If you mean to use /etc, use its real * path (/private/etc), not the symlink (/etc).

*/ public boolean isValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, boolean allowNull, ValidationErrorList errors) throws IntrusionException { return( isValidFileName( context, filename, allowNull, errors ) && isValidDirectoryPath( context, directorypath, parent, allowNull, errors ) && isValidFileContent( context, content, maxBytes, allowNull, errors ) ); } /** * {@inheritDoc} */ public void assertValidFileUpload(String context, String directorypath, String filename, File parent, byte[] content, int maxBytes, List allowedExtensions, boolean allowNull) throws ValidationException, IntrusionException { getValidFileName( context, filename, allowedExtensions, allowNull ); getValidDirectoryPath( context, directorypath, parent, allowNull ); getValidFileContent( context, content, maxBytes, allowNull ); } /** * {@inheritDoc} */ public void assertValidFileUpload(String context, String filepath, String filename, File parent, byte[] content, int maxBytes, List allowedExtensions, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { assertValidFileUpload(context, filepath, filename, parent, content, maxBytes, allowedExtensions, allowNull); } catch (ValidationException e) { errors.addError(context, e); } } /** * {@inheritDoc} * * Returns true if input is a valid list item. */ public boolean isValidListItem(String context, String input, List list) { try { getValidListItem( context, input, list); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} * * Returns true if input is a valid list item. */ public boolean isValidListItem(String context, String input, List list, ValidationErrorList errors) { try { getValidListItem( context, input, list); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * Returns the list item that exactly matches the canonicalized input. Invalid or non-matching input * will generate a descriptive ValidationException, and input that is clearly an attack * will generate a descriptive IntrusionException. */ public String getValidListItem(String context, String input, List list) throws ValidationException, IntrusionException { if (list.contains(input)) return input; throw new ValidationException( context + ": Invalid list item", "Invalid list item: context=" + context + ", input=" + input, context ); } /** * ValidationErrorList variant of getValidListItem * * @param errors */ public String getValidListItem(String context, String input, List list, ValidationErrorList errors) throws IntrusionException { try { return getValidListItem(context, input, list); } catch (ValidationException e) { errors.addError(context, e); } // error has been added to list, so return original input return input; } /** * {@inheritDoc} */ public boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set requiredNames, Set optionalNames) { try { assertValidHTTPRequestParameterSet( context, request, requiredNames, optionalNames); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} */ public boolean isValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set requiredNames, Set optionalNames, ValidationErrorList errors) { try { assertValidHTTPRequestParameterSet( context, request, requiredNames, optionalNames); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * Validates that the parameters in the current request contain all required parameters and only optional ones in * addition. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack * will generate a descriptive IntrusionException. * * Uses current HTTPRequest */ public void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set optional) throws ValidationException, IntrusionException { Set actualNames = request.getParameterMap().keySet(); // verify ALL required parameters are present Set missing = new HashSet(required); missing.removeAll(actualNames); if (missing.size() > 0) { throw new ValidationException( context + ": Invalid HTTP request missing parameters", "Invalid HTTP request missing parameters " + missing + ": context=" + context, context ); } // verify ONLY optional + required parameters are present Set extra = new HashSet(actualNames); extra.removeAll(required); extra.removeAll(optional); if (extra.size() > 0) { throw new ValidationException( context + ": Invalid HTTP request extra parameters " + extra, "Invalid HTTP request extra parameters " + extra + ": context=" + context, context ); } } /** * ValidationErrorList variant of assertIsValidHTTPRequestParameterSet * * Uses current HTTPRequest saved in ESAPI Authenticator * @param errors */ public void assertValidHTTPRequestParameterSet(String context, HttpServletRequest request, Set required, Set optional, ValidationErrorList errors) throws IntrusionException { try { assertValidHTTPRequestParameterSet(context, request, required, optional); } catch (ValidationException e) { errors.addError(context, e); } } /** * {@inheritDoc} * * Checks that all bytes are valid ASCII characters (between 33 and 126 * inclusive). This implementation does no decoding. http://en.wikipedia.org/wiki/ASCII. */ public boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws IntrusionException { try { getValidPrintable( context, input, maxLength, allowNull); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} * * Checks that all bytes are valid ASCII characters (between 33 and 126 * inclusive). This implementation does no decoding. http://en.wikipedia.org/wiki/ASCII. */ public boolean isValidPrintable(String context, char[] input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidPrintable( context, input, maxLength, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * Returns canonicalized and validated printable characters as a byte array. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack * will generate a descriptive IntrusionException. * * @throws IntrusionException */ public char[] getValidPrintable(String context, char[] input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException { if (isEmpty(input)) { if (allowNull) return null; throw new ValidationException(context + ": Input bytes required", "Input bytes required: HTTP request is null", context ); } if (input.length > maxLength) { throw new ValidationException(context + ": Input bytes can not exceed " + maxLength + " bytes", "Input exceeds maximum allowed length of " + maxLength + " by " + (input.length-maxLength) + " bytes: context=" + context + ", input=" + new String( input ), context); } for (int i = 0; i < input.length; i++) { if (input[i] <= 0x20 || input[i] >= 0x7E ) { throw new ValidationException(context + ": Invalid input bytes: context=" + context, "Invalid non-ASCII input bytes, context=" + context + ", input=" + new String( input ), context); } } return input; } /** * ValidationErrorList variant of getValidPrintable * * @param errors */ public char[] getValidPrintable(String context, char[] input,int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidPrintable(context, input, maxLength, allowNull); } catch (ValidationException e) { errors.addError(context, e); } // error has been added to list, so return original input return input; } /** * {@inheritDoc} * * Returns true if input is valid printable ASCII characters (32-126). */ public boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull) throws IntrusionException { try { getValidPrintable( context, input, maxLength, allowNull); return true; } catch( Exception e ) { return false; } } /** * {@inheritDoc} * * Returns true if input is valid printable ASCII characters (32-126). */ public boolean isValidPrintable(String context, String input, int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { getValidPrintable( context, input, maxLength, allowNull); return true; } catch( ValidationException e ) { errors.addError(context, e); return false; } } /** * Returns canonicalized and validated printable characters as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack * will generate a descriptive IntrusionException. * * @throws IntrusionException */ public String getValidPrintable(String context, String input, int maxLength, boolean allowNull) throws ValidationException, IntrusionException { try { String canonical = encoder.canonicalize(input); return new String( getValidPrintable( context, canonical.toCharArray(), maxLength, allowNull) ); //TODO - changed this to base Exception since we no longer need EncodingException //TODO - this is a bit lame: we need to re-think this function. } catch (Exception e) { throw new ValidationException( context + ": Invalid printable input", "Invalid encoding of printable input, context=" + context + ", input=" + input, e, context); } } /** * ValidationErrorList variant of getValidPrintable * * @param errors */ public String getValidPrintable(String context, String input,int maxLength, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidPrintable(context, input, maxLength, allowNull); } catch (ValidationException e) { errors.addError(context, e); } // error has been added to list, so return original input return input; } /** * Returns true if input is a valid redirect location. */ public boolean isValidRedirectLocation(String context, String input, boolean allowNull) throws IntrusionException { SecurityConfiguration sc = ESAPI.securityConfiguration(); return ESAPI.validator().isValidInput( context, input, "Redirect", sc.getIntProp("HttpUtilities.maxRedirectLength"), allowNull); } /** * Returns true if input is a valid redirect location. */ public boolean isValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException { SecurityConfiguration sc = ESAPI.securityConfiguration(); return ESAPI.validator().isValidInput( context, input, "Redirect", sc.getIntProp("HttpUtilities.maxRedirectLength"), allowNull, errors); } /** * Returns a canonicalized and validated redirect location as a String. Invalid input will generate a descriptive ValidationException, and input that is clearly an attack * will generate a descriptive IntrusionException. */ public String getValidRedirectLocation(String context, String input, boolean allowNull) throws ValidationException, IntrusionException { SecurityConfiguration sc = ESAPI.securityConfiguration(); return ESAPI.validator().getValidInput( context, input, "Redirect", sc.getIntProp("HttpUtilities.maxRedirectLength"), allowNull); } /** * ValidationErrorList variant of getValidRedirectLocation * * @param errors */ public String getValidRedirectLocation(String context, String input, boolean allowNull, ValidationErrorList errors) throws IntrusionException { try { return getValidRedirectLocation(context, input, allowNull); } catch (ValidationException e) { errors.addError(context, e); } // error has been added to list, so return original input return input; } /** * {@inheritDoc} * * This implementation reads until a newline or the specified number of * characters. * * @param in * @param max */ public String safeReadLine(InputStream in, int max) throws ValidationException { if (max <= 0) { throw new ValidationAvailabilityException( "Invalid input", "Invalid readline. Must read a positive number of bytes from the stream"); } StringBuilder sb = new StringBuilder(); int count = 0; int c; try { while (true) { c = in.read(); if ( c == -1 ) { if (sb.length() == 0) { return null; } break; } if (c == '\n' || c == '\r') { break; } count++; if (count > max) { throw new ValidationAvailabilityException( "Invalid input", "Invalid readLine. Read more than maximum characters allowed (" + max + ")"); } sb.append((char) c); } return sb.toString(); } catch (IOException e) { throw new ValidationAvailabilityException( "Invalid input", "Invalid readLine. Problem reading from input stream", e); } } /** * Helper function to check if a String is empty * * @param input string input value * @return boolean response if input is empty or not */ private final boolean isEmpty(String input) { return (input==null || input.trim().length() == 0); } /** * Helper function to check if a byte array is empty * * @param input string input value * @return boolean response if input is empty or not */ private final boolean isEmpty(byte[] input) { return (input==null || input.length == 0); } /** * Helper function to check if a char array is empty * * @param input string input value * @return boolean response if input is empty or not */ private final boolean isEmpty(char[] input) { return (input==null || input.length == 0); } /** * {@inheritDoc} */ public boolean isValidURI(String context, String input, boolean allowNull) { boolean isValid = false; boolean inputIsNullOrEmpty = input == null || "".equals(input); Encoder encoder = ESAPI.encoder(); try{ URI compliantURI = null == input ? new URI("") : this.getRfcCompliantURI(input); if(null != compliantURI && input != null){ String canonicalizedURI = encoder.getCanonicalizedURI(compliantURI); //if getCanonicalizedURI doesn't throw an IntrusionException, then the URI contains no mixed or //double-encoding attacks. logger.debug(Logger.SECURITY_SUCCESS, "We did not detect any mixed or multiple encoding in the uri:[" + input + "]"); Validator v = ESAPI.validator(); //This part will use the regex from validation.properties. This regex should be super-simple, and //used mainly to restrict certain parts of a URL. Pattern p = ESAPI.securityConfiguration().getValidationPattern( "URL" ); if(p != null){ //We're doing this instead of using the normal validator API, because it will canonicalize the input again //and if the URI has any queries that also happen to match HTML entities, like ¶ //it will cease conforming to the regex we now specify for a URL. isValid = p.matcher(canonicalizedURI).matches(); }else{ logger.error(Logger.EVENT_FAILURE, "Invalid regex pulled from configuration. Check the regex for URL and correct."); } }else{ if(allowNull && inputIsNullOrEmpty ){ isValid = true; } } }catch (IntrusionException e){ logger.error(Logger.SECURITY_FAILURE, e.getMessage()); isValid = false; } catch (URISyntaxException e) { logger.error(Logger.EVENT_FAILURE, e.getMessage()); } return isValid; } /** * {@inheritDoc} */ public URI getRfcCompliantURI(String input){ URI rval = null; try { rval = new URI(input); } catch (URISyntaxException e) { logger.error(Logger.EVENT_FAILURE, e.getMessage()); } return rval; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy