淘宝商城(天猫)高级技术专家.3年研发+3年性能测试调优/系统测试+4年团队管理与测试架构、研发系统实践. 新舞台新气象, 深化测试基础架构及研发架构,希望能在某个技术领域成为真正的技术大牛。欢迎荐才http://bbs.51testing.com/viewthread.php?tid=120496&extra=&page=1 .邮件: jianzhao.liangjz@alibaba-inc.com,MSN:liangjianzhao@163.com.微博:http://t.sina.com.cn/1674816524

发布新日志

  • Acunetix web vulnerality scanner安全测试

    2008-04-19 12:50:53

  • 免费跨站攻击扫描工具paros与appscan 对比

    2008-04-11 23:36:56

     

    2者对比发现paros的结果有很多的不足

     

    1)      SQL 注入扫描貌似有问题

    2)      跨站脚本有遗漏

    3)      Lotus dimino 有安全问题误报

    4)      报表、修复建议没有appscan强大

    5)      Appscan集成强大的安全规范

    6)     

    更多见http://bbs.51testing.com/viewthread.php?tid=110473&extra=page%3D1&frombbs=1

  • 修改HTTP Post/get数据利器TamperIE

    2008-04-11 23:35:11


    偶在此登记,避免遗漏:)

     

    近期关注安全测试,将陆续介绍接触到的一些很好的工具。

    安全工具是一把双刃剑,用好了给测试工作带来很大的便利。

    本期介绍修改HTTP Post/get数据利器TamperIE。

    http://bbs.51testing.com/thread-110474-1-1.html

  • APPSCAN 修复SQL注入安全缺陷建议[zt]

    2008-03-20 22:29:29

     最近在了解IBM 的安全测试工具APPSCAN.

     通过使用demo.testfire.com。感觉功能确实NB。

     找个临时license尝试下扫描自己的测试环境,看看是否也这么好的命中率?

     另外,还有疑问它工作原理是否通过网络截取包,并解析特征字?

      对于hibernate、ibatis 这种封装SQL的代码是否也能判断到?

     

    Blind SQL Injection
    Fix Recommendation

    --------------------------------------------------------------------------------
    General
    There are several issues whose remediation lies in sanitizing user input.
    By verifying that user input does not contain hazardous characters, it is possible to prevent malicious users from causing your application to execute unintended operations, such as launch arbitrary SQL queries, embed Javascrīpt code to be executed on the client side, run various operating system commands etc.

    It is advised to filter out all the following characters:

    [1] | (pipe sign)
    [2] & (ampersand sign)
    [3] ; (semicolon sign)
    [4] $ (dollar sign)
    [5] % (percent sign)
    [6] @ (at sign)
    [7] ' (single apostrophe)
    [8] " (quotation mark)
    [9] \' (backslash-escaped apostrophe)
    [10] \" (backslash-escaped quotation mark)
    [11] <> (triangular parenthesis)
    [12] () (parenthesis)
    [13] + (plus sign)
    [14] CR (Carriage return, ASCII 0x0d)
    [15] LF (Line feed, ASCII 0x0a)
    [16] , (comma sign)
    [17] \ (backslash)


    The following sections describe the various issues, their fix recommendations and the hazardous characters that might trigger these issues:


    SQL injection and blind SQL injection:
    A. Make sure the value and type (such as Integer, Date, etc.) of the user input is valid and expected by the application.
    B. Use stored procedures to abstract data access so that users do not directly access tables or views. When using stored procedures, use the ADO command object to implement them, so that variables are strongly typed.
    C. Sanitize input to exclude context-changing symbols such as:

    [1] ' (single apostrophe)
    [2] " (quotation mark)
    [3] \' (backslash-escaped apostrophe)
    [4] \" (backslash-escaped quotation mark)
    [5] ) (closing parenthesis)
    [6] ; (semicolon)


    Cross site scrīpting:
    A. Sanitize user input and filter out Javascrīpt code. We suggest that you filter the following characters:

    [1] <> (triangular parenthesis)
    [2] " (quotation mark)
    [3] ' (single apostrophe)
    [4] % (percent sign)
    [5] ; (semicolon)
    [6] () (parenthesis)
    [7] & (ampersand sign)
    [8] + (plus sign)
    B. To fix the <%00scrīpt> variant see MS article 821349
    C. For UTF-7 attacks:

    [-] When possible, it is recommended to enforce a specific charset encoding (using 'Content-Type' header or <meta> tag).

    HTTP response splitting:
    Sanitize user input (at least, such input that is later embedded in HTTP responses).
    Make sure that malicious characters are not part of the input, such as:

    [1] CR (Carriage return, ASCII 0x0d)
    [2] LF (Line feed, ASCII 0x0a)


    Remote command execution:
    Sanitize input to exclude symbols that are meaningful to the operating system's command execution, such as:

    [1] | (pipe sign)
    [2] & (ampersand sign)
    [3] ; (semicolon sign)

    Shell command execution:
    A. Never pass unchecked user-input to Perl commands such as: eval(), open(), sysopen(), system().
    B. Make sure malicious characters are not part of the input, such as:

    [1] $ (dollar sign)
    [2] % (percent sign)
    [3] @ (at sign)


    XPath injection:
    Sanitize input to exclude context changing symbols such as:

    [1] ' (single apostrophe)
    [2] " (quotation mark)
    Etc.

    LDAP injection:
    A. Use positive validation. Alphanumeric filtering (A..Z,a..z,0..9) is suitable for most LDAP queries.
    B. Special LDAP characters which should be filtered out or escaped:

    [1] A space or "#" character at the beginning of the string
    [2] A space character at the end of the string
    [3] , (comma sign)
    [4] + (plus sign)
    [5] " (quotation mark)
    [6] \ (backslash)
    [7] <> (triangular parenthesis)
    [8] ; (semicolon sign)
    [9] () (parenthesis)
    Asp.Net
    Here are two possible ways to protect your web application against SQL injection attacks:

    [1] Use a stored procedure rather than dynamically built SQL query string. The way parameters are passed to SQL Server stored procedures, prevents the use of apostrophes and hyphens.

    Here is a simple example of how to use stored procedures in ASP.NET:


      ' Visual Basic example
      Dim DS As DataSet
      Dim MyConnection As SqlConnection
      Dim MyCommand As SqlDataAdapter
      Dim SelectCommand As String = "select * from users where username = @username"
      ...
      MyCommand.SelectCommand.Parameters.Add(New SqlParameter("@username", SqlDbType.NVarChar, 20))
      MyCommand.SelectCommand.Parameters("@username").Value = UserNameField.Value
      // C# example
      String selectCmd = "select * from Authors where state = @username";
      SqlConnection myConnection = new SqlConnection("server=...");
      SqlDataAdapter myCommand = new SqlDataAdapter(selectCmd, myConnection);
      myCommand.SelectCommand.Parameters.Add(new SqlParameter("@username", SqlDbType.NVarChar, 20));
      myCommand.SelectCommand.Parameters["@username"].Value = UserNameField.Value;

     

    [2] You can add input validation to Web Forms pages by using validation controls. Validation controls provide an easy-to-use mechanism for all common types of standard validation - for example, testing for valid dates or values within a range - plus ways to provide custom-written validation. In addition, validation controls allow you to completely customize how error information is displayed to the user. Validation controls can be used with any controls that are processed in a Web Forms page's class file, including both HTML and Web server controls.

    In order to make sure user input contains only valid values, you can use one of the following validation controls:


    a. "RangeValidator": checks that a user's entry (value) is between specified lower and upper boundaries. You can check ranges within pairs of numbers, alphabetic characters, and dates.


    b. "RegularExpressionValidator": checks that the entry matches a pattern defined by a regular expression. This type of validation allows you to check for predictable sequences of characters, such as those in social security numbers, e-mail addresses, telephone numbers, postal codes, and so on.

    Important note: validation controls do not block user input or change the flow of page processing; they only set an error state, and produce error messages. It is the programmer's responsibility to test the state of the controls in the code before performing further application-specific actions.

    There are two ways to check for user input validity:

    1. Testing for a general error state:

    In your code, test the page's IsValid property. This property rolls up the values of the IsValid properties of all the validation controls on the page (using a logical AND). If one of the validation controls is set to invalid, the page's property will return false.

    2. Testing for the error state of individual controls:

    Loop through the page's Validators collection, which contains references to all the validation controls. You can then examine the IsValid property of each validation control.
    J2EE
    ** Prepared Statements:

    There are 3 possible ways to protect your application against SQL injection, i.e. malicious tampering of SQL parameters. Instead of dynamically building SQL statements, use:

    [1] PreparedStatement, which is precompiled and stored in a pool of PreparedStatement objects. PreparedStatement defines setters to register input parameters that are compatible with the supported JDBC SQL data types. For example, setString should be used for input parameters of type VARCHAR or LONGVARCHAR (refer to the Java API for further details). This way of setting input parameters prevents an attacker from manipulating the SQL statement through injection of bad characters, such as apostrophe.

    Example of how to use a PreparedStatement in J2EE:


      // J2EE PreparedStatemenet Example
      // Get a connection to the database
      Connection myConnection;
      if (isDataSourceEnabled()) {
          // using the DataSource to get a managed connection
          Context ctx = new InitialContext();
          myConnection = ((DataSource)ctx.lookup(datasourceName)).getConnection(dbUserName, dbPassword);
      } else {
          try {
              // using the DriverManager to get a JDBC connection
              Class.forName(jdbcDriverClassPath);
              myConnection = DriverManager.getConnection(jdbcURL, dbUserName, dbPassword);
          } catch (ClassNotFoundException e) {
              ...
          }
      }
      ...
      try {
          PreparedStatement myStatement = myConnection.prepareStatement("select * from users where username = ?");
          myStatement.setString(1, userNameField);
          ResultSet rs = myStatement.executeQuery();
          ...
          rs.close();
      } catch (SQLException sqlException) {
          ...
      } finally {
          myStatement.close();
          myConnection.close();
      }

     

    [2] CallableStatement, which extends PreparedStatement to execute database SQL stored procedures. This class inherits input setters from PreparedStatement (see [1] above).

    The following example assumes that this database stored procedure has been created:

    CREATE PROCEDURE select_user (@username varchar(20))
    AS SELECT * FROM USERS WHERE USERNAME = @username;

    Example of how to use a CallableStatement in J2EE to execute the above stored procedure:


      // J2EE PreparedStatemenet Example
      // Get a connection to the database
      Connection myConnection;
      if (isDataSourceEnabled()) {
          // using the DataSource to get a managed connection
          Context ctx = new InitialContext();
          myConnection = ((DataSource)ctx.lookup(datasourceName)).getConnection(dbUserName, dbPassword);
      } else {
          try {
              // using the DriverManager to get a JDBC connection
              Class.forName(jdbcDriverClassPath);
              myConnection = DriverManager.getConnection(jdbcURL, dbUserName, dbPassword);
          } catch (ClassNotFoundException e) {
              ...
          }
      }
      ...
      try {
          PreparedStatement myStatement = myConnection.prepareCall("{?= call select_user ?,?}");
          myStatement.setString(1, userNameField);
          myStatement.registerOutParameter(1, Types.VARCHAR);
          ResultSet rs = myStatement.executeQuery();
          ...
          rs.close();
      } catch (SQLException sqlException) {
          ...
      } finally {
          myStatement.close();
          myConnection.close();
      }

     

    [3] Entity Bean, which represents an EJB business object in a persistent storage mechanism. There are two types of entity beans: bean-managed and container-managed. With bean-managed persistence, the developer is responsible of writing the SQL code to access the database (refer to sections [1] and [2] above). With container-managed persistence, the EJB container automatically generates the SQL code. As a result, the container is responsible of preventing malicious attempts to tamper with the generated SQL code.

    Example of how to use an Entity Bean in J2EE:


      // J2EE EJB Example
      try {
          // lookup the User home interface
          UserHome userHome = (UserHome)context.lookup(User.class);   
          // find the User remote interface
          User = userHome.findByPrimaryKey(new UserKey(userNameField));   
          ...   
      } catch (Exception e) {
          ...
      }

     

    RECOMMENDED JAVA TOOLS
    N/A

    REFERENCES
    http://java.sun.com/j2se/1.4.1/docs/api/java/sql/PreparedStatement.html
    http://java.sun.com/j2se/1.4.1/docs/api/java/sql/CallableStatement.html


    ** Input Data Validation:

    While data validations may be provided as a user convenience on the client-tier, data validation must be performed on the server-tier using Servlets. Client-side validations are inherently insecure because they can be easily bypassed, e.g. by disabling Javascrīpt.

    A good design usually requires the web application framework to provide server-side utility routines to validate the following:
    [1] Required field
    [2] Field data type (all HTTP request parameters are Strings by default)
    [3] Field length
    [4] Field range
    [5] Field options
    [6] Field pattern
    [7] Cookie values
    [8] HTTP Response

    A good practice is to implement the above routine as static methods in a "Validator" utility class. The following sections describe an example validator class.

    [1] Required field
    Always check that the field is not null and its length is greater than zero, excluding leading and trailing white spaces.

    Example of how to validate required fields:


      // Java example to validate required fields
      public Class Validator {
          ...
          public static boolean validateRequired(String value) {
              boolean isFieldValid = false;
              if (value != null && value.trim().length() > 0) {
                  isFieldValid = true;
              }
              return isFieldValid;
          }
          ...
      }
      ...
      String fieldValue = request.getParameter("fieldName");
      if (Validator.validateRequired(fieldValue)) {
          // fieldValue is valid, continue processing request
          ...
      }

     

    [2] Field data type
    In web applications, input parameters are poorly typed. For example, all HTTP request parameters or cookie values are of type String. The developer is responsible for verifying the input is of the correct data type. Use the Java primitive wrapper classes to check if the field value can be safely converted to the desired primitive data type.

    Example of how to validate a numeric field (type int):


      // Java example to validate that a field is an int number
      public Class Validator {
          ...
          public static boolean validateInt(String value) {
              boolean isFieldValid = false;
              try {
                  Integer.parseInt(value);
                  isFieldValid = true;
              } catch (Exception e) {
                  isFieldValid = false;
              }
              return isFieldValid;
          }
          ...
      }
      ...
      // check if the HTTP request parameter is of type int
      String fieldValue = request.getParameter("fieldName");
      if (Validator.validateInt(fieldValue)) {
          // fieldValue is valid, continue processing request
          ...
      }

     

    A good practice is to convert all HTTP request parameters to their respective data types. For example, the developer should store the "integerValue" of a request parameter in a request attribute and use it as shown in the following example:


      // Example to convert the HTTP request parameter to a primitive wrapper data type
      // and store this value in a request attribute for further processing
      String fieldValue = request.getParameter("fieldName");
      if (Validator.validateInt(fieldValue)) {
          // convert fieldValue to an Integer
          Integer integerValue = Integer.getInteger(fieldValue);
          // store integerValue in a request attribute
          request.setAttribute("fieldName", integerValue);
      }
      ...
      // Use the request attribute for further processing
      Integer integerValue = (Integer)request.getAttribute("fieldName");
      ...

     

    The primary Java data types that the application should handle:
    - Byte
    - Short
    - Integer
    - Long
    - Float
    - Double
    - Date

    [3] Field length
    Always ensure that the input parameter (whether HTTP request parameter or cookie value) is bounded by a minimum length and/or a maximum length.

    Example to validate that the length of the userName field is between 8 and 20 characters:


      // Example to validate the field length
      public Class Validator {
          ...
          public static boolean validateLength(String value, int minLength, int maxLength) {
              String validatedValue = value;
              if (!validateRequired(value)) {
                  validatedValue = "";
              }
              return (validatedValue.length() >= minLength &&
                          validatedValue.length() <= maxLength);
          }
          ...
      }
      ...
      String userName = request.getParameter("userName");
      if (Validator.validateRequired(userName)) {
          if (Validator.validateLength(userName, 8, 20)) {
              // userName is valid, continue further processing
              ...
          }
      }

     

    [4] Field range
    Always ensure that the input parameter is within a range as defined by the functional requirements.

    Example to validate that the input numberOfChoices is between 10 and 20:


      // Example to validate the field range
      public Class Validator {
          ...
          public static boolean validateRange(int value, int min, int max) {
              return (value >= min && value <= max);
          }
          ...
      }
      ...
      String fieldValue = request.getParameter("numberOfChoices");
      if (Validator.validateRequired(fieldValue)) {
          if (Validator.validateInt(fieldValue)) {
              int numberOfChoices = Integer.parseInt(fieldValue);
              if (Validator.validateRange(numberOfChoices, 10, 20)) {
                  // numberOfChoices is valid, continue processing request
                  ...
              }
          }
      }

     

    [5] Field options
    Often, the web application presents the user with a set of options to choose from, e.g. using the SELECT HTML tag, but fails to perform server-side validation to ensure that the selected value is one of the allowed options. Remember that a malicious user can easily modify any option value. Always validate the selected user value against the allowed options as defined by the functional requirements.

    Example to validate the user selection against a list of allowed options:


      // Example to validate user selection against a list of options
      public Class Validator {
          ...
          public static boolean validateOption(Object[] options, Object value) {
              boolean isValidValue = false;
              try {
                  List list = Arrays.asList(options);
                  if (list != null) {
                      isValidValue = list.contains(value);
                  }
              } catch (Exception e) {
              }
              return isValidValue;
          }
          ...
      }
      ...
      // Allowed options
      String[] ōptions = {"option1", "option2", "option3");
      // Verify that the user selection is one of the allowed options
      String userSelection = request.getParameter("userSelection");
      if (Validator.validateOption(options, userSelection)) {
          // valid user selection, continue processing request
          ...
      }

     

    [6] Field pattern
    Always check that the user input matches a pattern as defined by the functionality requirements. For example, if the userName field should only allow alpha-numeric characters, case insensitive, then use the following regular expression:
    ^[a-zA-Z0-9]*$

    Java 1.3 or earlier versions do not include any regular expression packages. Apache Regular Expression Package (see Resources below) is recommended for use with Java 1.3 to resolve this lack of support. Example to perform regular expression validation:


      // Example to validate that a given value matches a specified pattern
      // using the Apache regular expression package
      import org.apache.regexp.RE;
      import org.apache.regexp.RESyntaxException;
      public Class Validator {
          ...
          public static boolean matchPattern(String value, String expression) {
              boolean match = false;
              if (validateRequired(expression)) {
                   RE r = new RE(expression);
                   match = r.match(value);            
              }
              return match;
          }
          ...
      }
      ...
      // Verify that the userName request parameter is alpha-numeric
      String userName = request.getParameter("userName");
      if (Validator.matchPattern(userName, "^[a-zA-Z0-9]*$")) {
          // userName is valid, continue processing request
          ...
      }

     

    Java 1.4 introduced a new regular expression package (java.util.regex). Here is a modified version of Validator.matchPattern using the new Java 1.4 regular expression package:


      // Example to validate that a given value matches a specified pattern
      // using the Java 1.4 regular expression package
      import java.util.regex.Pattern;
      import java.util.regexe.Matcher;
      public Class Validator {
          ...
          public static boolean matchPattern(String value, String expression) {
              boolean match = false;
              if (validateRequired(expression)) {
                  match = Pattern.matches(expression, value);
              }
              return match;
          }
          ...
      }

     

    [7] Cookie value
    Use the javax.servlet.http.Cookie object to validate the cookie value. The same validation rules (described above) apply to cookie values depending on the application requirements, e.g. validate a required value, validate length, etc.

    Example to validate a required cookie value:


      // Example to validate a required cookie value
      // First retrieve all available cookies submitted in the HTTP request
      Cookie[] cookies = request.getCookies();
      if (cookies != null) {
          // find the "user" cookie
          for (int i=0; i<cookies.length; ++i) {
              if (cookies[i].getName().equals("user")) {
                  // validate the cookie value
                  if (Validator.validateRequired(cookies[i].getValue()) {
                      // valid cookie value, continue processing request
                      ...
                  }
              }   
          }
      }

     

    [8] HTTP Response
    [8-1] Filter user input
    To guard the application against cross-site scrīpting, sanitize HTML by converting sensitive characters to their corresponding character entities. These are the HTML sensitive characters:
    < > " ' % ; ) ( & +

    Example to filter a specified string by converting sensitive characters to their corresponding character entities:


      // Example to filter sensitive data to prevent cross-site scrīpting
      public Class Validator {
          ...
          public static String filter(String value) {
              if (value == null) {
                  return null;
              }       
              StringBuffer result = new StringBuffer(value.length());
              for (int i=0; i<value.length(); ++i) {
                  switch (value.charAt(i)) {
                  case '<':
                      result.append("&lt;");
                      break;
                  case '>':
                      result.append("&gt;");
                      break;
                  case '"':
                      result.append("&quot;");
                      break;
                  case '\'':
                      result.append("&#39;");
                      break;
                  case '%':
                      result.append("&#37;");
                      break;
                  case ';':
                      result.append("&#59;");
                      break;
                  case '(':
                      result.append("&#40;");
                      break;
                  case ')':
                      result.append("&#41;");
                      break;
                  case '&':
                      result.append("&amp;");
                      break;
                  case '+':
                      result.append("&#43;");
                      break;
                  default:
                      result.append(value.charAt(i));
                      break;
              }       
              return result;
          }
          ...
      }
      ...
      // Filter the HTTP response using Validator.filter
      PrintWriter ōut = response.getWriter();
      // set output response
      out.write(Validator.filter(response));
      out.close();

     

    The Java Servlet API 2.3 introduced Filters, which supports the interception and transformation of HTTP requests or responses.

    Example of using a Servlet Filter to sanitize the response using Validator.filter:


      // Example to filter all sensitive characters in the HTTP response using a Java Filter.
      // This example is for illustration purposes since it will filter all content in the response, including HTML tags!
      public class SensitiveCharsFilter implements Filter {
          ...
          public void doFilter(ServletRequest request,
                          ServletResponse response,
                          FilterChain chain)
                  throws IOException, ServletException {
              PrintWriter ōut = response.getWriter();
              ResponseWrapper wrapper = new ResponseWrapper((HttpServletResponse)response);
              chain.doFilter(request, wrapper);
              CharArrayWriter caw = new CharArrayWriter();
              caw.write(Validator.filter(wrapper.toString()));
             
              response.setContentType("text/html");
              response.setContentLength(caw.toString().length());
              out.write(caw.toString());
              out.close();
          }
          ...
          public class CharResponseWrapper extends HttpServletResponseWrapper {
              private CharArrayWriter output;
              public String toString() {
                  return output.toString();
              }
         
              public CharResponseWrapper(HttpServletResponse response){
                  super(response);
                  ōutput = new CharArrayWriter();
              }
             
              public PrintWriter getWriter(){
                  return new PrintWriter(output);
              }
          }
      }
      }

     

    [8-2] Secure the cookie
    When storing sensitive data in a cookie, make sure to set the secure flag of the cookie in the HTTP response, using Cookie.setSecure(boolean flag) to instruct the browser to send the cookie using a secure protocol, such as HTTPS or SSL.

    Example to secure the "user" cookie:


      // Example to secure a cookie, i.e. instruct the browser to
      // send the cookie using a secure protocol
      Cookie cookie = new Cookie("user", "sensitive");
      cookie.setSecure(true);
      response.addCookie(cookie);

     

    RECOMMENDED JAVA TOOLS
    The two main Java frameworks for server-side validation are:
    [1] Jakarta Commons Validator (integrated with Struts 1.1)
    The Jakarta Commons Validator is a powerful framework that implements all the above data validation requirements. These rules are configured in an XML file that defines input validation rules for form fields. Struts supports output filtering of dangerous characters in the [8] HTTP Response by default on all data written using the Struts 'bean:write' tag. This filtering may be disabled by setting the 'filter=false' flag.

    Struts defines the following basic input validators, but custom validators may also be defined:
    required: succeeds if the field contains any characters other than whitespace.
    mask: succeeds if the value matches the regular expression given by the mask attribute.
    range: succeeds if the value is within the values given by the min and max attributes ((value >= min) & (value <= max)).
    maxLength: succeeds if the field is length is less than or equal to the max attribute.
    minLength: succeeds if the field is length is greater than or equal to the min attribute.
    byte, short, integer, long, float, double: succeeds if the value can be converted to the corresponding primitive.
    date: succeeds if the value represents a valid date. A date pattern may be provided.
    creditCard: succeeds if the value could be a valid credit card number.
    email: succeeds if the value could be a valid e-mail address.

    Example to validate the userName field of a loginForm using Struts Validator:

      <form-validation>
          <global>
              ...
              <validator name="required"
                  classname="org.apache.struts.validator.FieldChecks"
                  method="validateRequired"
                  msg="errors.required">
              </validator>
              <validator name="mask"
                  classname="org.apache.struts.validator.FieldChecks"
                  method="validateMask"
                  msg="errors.invalid">
              </validator>
              ...
          </global>
          <formset>
              <form name="loginForm">
                  <!-- userName is required and is alpha-numeric case insensitive -->
                  <field property="userName" depends="required,mask">
                      <!-- message resource key to display if validation fails -->
                      <msg name="mask" key="login.userName.maskmsg"/>
                      <arg0 key="login.userName.displayname"/>
                      <var>
                          <var-name>mask</var-name>
                          <var-value>^[a-zA-Z0-9]*$</var-value>
                      </var>
                  </field>
              ...
              </form>
              ...
          </formset>
      </form-validation>

     

    [2] JavaServer Faces Technology
    JavaServer Faces Technology is a set of Java APIs (JSR 127) to represent UI components, manage their state, handle events and input validation.

    The JavaServer Faces API implements the following basic validators, but custom validators may be defined:
    validate_doublerange: registers a DoubleRangeValidator on a component
    validate_length: registers a LengthValidator on a component
    validate_longrange: registers a LongRangeValidator on a component
    validate_required: registers a RequiredValidator on a component
    validate_stringrange: registers a StringRangeValidator on a component
    validator: registers a custom Validator on a component

    The JavaServer Faces API defines the following UIInput and UIOutput Renderers (Tags):
    input_date: accepts a java.util.Date formatted with a java.text.Date instance
    output_date: displays a java.util.Date formatted with a java.text.Date instance
    input_datetime: accepts a java.util.Date formatted with a java.text.DateTime instance
    output_datetime: displays a java.util.Date formatted with a java.text.DateTime instance
    input_number: accepts a numeric data type (java.lang.Number or primitive), formatted with a java.text.NumberFormat
    output_number: accepts a numeric data type (java.lang.Number or primitive), formatted with a java.text.NumberFormat
    input_text: accepts a text string of one line.
    output_text: displays a text string of one line.
    input_time: accepts a java.util.Date, formatted with a java.text.DateFormat time instance
    output_time: displays a java.util.Date, formatted with a java.text.DateFormat time instance
    input_hidden: allows a page author to include a hidden variable in a page
    input_secret: accepts one line of text with no spaces and displays it as a set of asterisks as it is typed
    input_textarea: accepts multiple lines of text
    output_errors: displays error messages for an entire page or error messages associated with a specified client identifier
    output_label: displays a nested component as a label for a specified input field
    output_message: displays a localized message

    Example to validate the userName field of a loginForm using JavaServer Faces:

      <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
      <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
      ...
      <jsp:useBean id="UserBean"
          class="myApplication.UserBean" scope="session" />
      <f:use_faces>
        <h:form formName="loginForm" >
          <h:input_text id="userName" size="20" modelReference="UserBean.userName">
              <f:validate_required/>
              <f:validate_length minimum="8" maximum="20"/>   
          </h:input_text>
          <!-- display errors if present -->
          <h:output_errors id="loginErrors" clientId="userName"/>
          <h:command_button id="submit" label="Submit" commandName="submit" /><p>
        </h:form>
      </f:use_faces>

     


    REFERENCES
    Java API 1.3 - http://java.sun.com/j2se/1.3/docs/api/
    Java API 1.4 - http://java.sun.com/j2se/1.4/docs/api/
    Java Servlet API 2.3 - http://java.sun.com/products/servlet/2.3/javadoc/
    Java Regular Expression Package - http://jakarta.apache.org/regexp/
    Jakarta Validator - http://jakarta.apache.org/commons/validator/
    JavaServer Faces Technology - http://java.sun.com/j2ee/javaserverfaces/

    ** Error Handling:

    Many J2EE web application architectures follow the Model View Controller (MVC) pattern. In this pattern a Servlet acts as a Controller. A Servlet delegates the application processing to a JavaBean such as an EJB Session Bean (the Model). The Servlet then forwards the request to a JSP (View) to render the processing results. Servlets should check all input, output, return codes, error codes and known exceptions to ensure that the expected processing actually occurred.

    While data validation protects applications against malicious data tampering, a sound error handling strategy is necessary to prevent the application from inadvertently disclosing internal error messages such as exception stack traces. A good error handling strategy addresses the following items:

    [1] Defining Errors
    [2] Reporting Errors
    [3] Rendering Errors
    [4] Error Mapping

    [1] Defining Errors
    Hard-coded error messages in the application layer (e.g. Servlets) should be avoided. Instead, the application should use error keys that map to known application failures. A good practice is to define error keys that map to validation rules for HTML form fields or other bean properties. For example, if the "user_name" field is required, is alphanumeric, and must be unique in the database, then the following error keys should be defined:

    (a) ERROR_USERNAME_REQUIRED: this error key is used to display a message notifying the user that the "user_name" field is required;
    (b) ERROR_USERNAME_ALPHANUMERIC: this error key is used to display a message notifying the user that the "user_name" field should be alphanumeric;
    (c) ERROR_USERNAME_DUPLICATE: this error key is used to display a message notifying the user that the "user_name" value is a duplicate in the database;
    (d) ERROR_USERNAME_INVALID: this error key is used to display a generic message notifying the user that the "user_name" value is invalid;

    A good practice is to define the following framework Java classes which are used to store and report application errors:

    - ErrorKeys: defines all error keys


          // Example: ErrorKeys defining the following error keys:   
          //    - ERROR_USERNAME_REQUIRED
          //    - ERROR_USERNAME_ALPHANUMERIC
          //    - ERROR_USERNAME_DUPLICATE
          //    - ERROR_USERNAME_INVALID
          //    ...
          public Class ErrorKeys {
              public static final String ERROR_USERNAME_REQUIRED = "error.username.required";
              public static final String ERROR_USERNAME_ALPHANUMERIC = "error.username.alphanumeric";
              public static final String ERROR_USERNAME_DUPLICATE = "error.username.duplicate";
              public static final String ERROR_USERNAME_INVALID = "error.username.invalid";
              ...
          }

     

    - Error: encapsulates an individual error


          // Example: Error encapsulates an error key.
          // Error is serializable to support code executing in multiple JVMs.
          public Class Error implements Serializable {
             
              // Constructor given a specified error key
              public Error(String key) {
                  this(key, null);
              }
             
              // Constructor given a specified error key and array of placeholder objects
              public Error(String key, Object[] values) {
                  this.key = key;
                  this.values = values;
              }
             
              // Returns the error key
              public String getKey() {
                  return this.key;
              }
             
              // Returns the placeholder values
              public Object[] getValues() {
                  return this.values;
              }
             
              private String key = null;
              private Object[] values = null;
          }   

     

    - Errors: encapsulates a Collection of errors


          // Example: Errors encapsulates the Error objects being reported to the presentation layer.
          // Errors are stored in a HashMap where the key is the bean property name and value is an
          // ArrayList of Error objects.
          public Class Errors implements Serializable {
         
              // Adds an Error object to the Collection of errors for the specified bean property.
              public void addError(String property, Error error) {
                  ArrayList propertyErrors = (ArrayList)errors.get(property);
                  if (propertyErrors == null) {
                      propertyErrors = new ArrayList();
                      errors.put(property, propertyErrors);
                  }
                  propertyErrors.put(error);           
              }
             
              // Returns true if there are any errors
              public boolean hasErrors() {
                  return (errors.size > 0);
              }
             
              // Returns the Errors for the specified property
              public ArrayList getErrors(String property) {
                  return (ArrayList)errors.get(property);
              }
              private HashMap errors = new HashMap();
          }

     

    Using the above framework classes, here is an example to process validation errors of the "user_name" field:


      // Example to process validation errors of the "user_name" field.
      Errors errors = new Errors();
      String userName = request.getParameter("user_name");
      // (a) Required validation rule
      if (!Validator.validateRequired(userName)) {
          errors.addError("user_name", new Error(ErrorKeys.ERROR_USERNAME_REQUIRED));
      } // (b) Alpha-numeric validation rule
      else if (!Validator.matchPattern(userName, "^[a-zA-Z0-9]*$")) {
          errors.addError("user_name", new Error(ErrorKeys.ERROR_USERNAME_ALPHANUMERIC));
      }
      else
      {
          // (c) Duplicate check validation rule
          // We assume that there is an existing UserValidationEJB session bean that implements
          // a checkIfDuplicate() method to verify if the user already exists in the database.
          try {
              ...       
              if (UserValidationEJB.checkIfDuplicate(userName)) {
                  errors.addError("user_name", new Error(ErrorKeys.ERROR_USERNAME_DUPLICATE));
              }
          } catch (RemoteException e) {
              // log the error
              logger.error("Could not validate user for specified userName: " + userName);
              errors.addError("user_name", new Error(ErrorKeys.ERROR_USERNAME_DUPLICATE);
          }
      }
      // set the errors object in a request attribute called "errors"
      request.setAttribute("errors", errors);
      ...

     

    [2] Reporting Errors
    There are two ways to report web-tier application errors:
    (a) Servlet Error Mechanism
    (b) JSP Error Mechanism

    [2-a] Servlet Error Mechanism
    A Servlet may report errors by:
    - forwarding to the input JSP (having already stored the errors in a request attribute), OR
    - calling response.sendError with an HTTP error code argument, OR
    - throwing an exception

    It is good practice to process all known application errors (as described in section [1]), store them in a request attribute, and forward to the input JSP. The input JSP should display the error messages and prompt the user to re-enter the data. The following example illustrates how to forward to an input JSP (userInput.jsp):


      // Example to forward to the userInput.jsp following user validation errors
      RequestDispatcher rd = getServletContext().getRequestDispatcher("/user/userInput.jsp");
      if (rd != null) {
          rd.forward(request, response);
      }

     

    If the Servlet cannot forward to a known JSP page, the second option is to report an error using the response.sendError method with HttpServletResponse.SC_INTERNAL_SERVER_ERROR (status code 500) as argument. Refer to the javadoc of javax.servlet.http.HttpServletResponse for more details on the various HTTP status codes. Example to return a HTTP error:


      // Example to return a HTTP error code
      RequestDispatcher rd = getServletContext().getRequestDispatcher("/user/userInput.jsp");
      if (rd == null) {
          // messages is a resource bundle with all message keys and values
          response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                  messages.getMessage(ErrorKeys.ERROR_USERNAME_INVALID));
      }

     

    As a last resort, Servlets can throw an exception, which must be a subclass of one of the following classes:
    - RuntimeException
    - ServletException
    - IOException

    [2-b] JSP Error Mechanism
    JSP pages provide a mechanism to handle runtime exceptions by defining an errorPage directive as shown in the following example:


          <%@ page errorPage="/errors/userValidation.jsp" %>

     

    Uncaught JSP exceptions are forwarded to the specified errorPage, and the original exception is set in a request parameter called javax.servlet.jsp.jspException. The error page must include a isErrorPage directive as shown below:


          <%@ page isErrorPage="true" %>

     

    The isErrorPage directive causes the "exception" variable to be initialized to the exception object being thrown.

    [3] Rendering Errors
    The J2SE Internationalization APIs provide utility classes for externalizing application resources and formatting messages including:

    (a) Resource Bundles
    (b) Message Formatting

    [3-a] Resource Bundles
    Resource bundles support internationalization by separating localized data from the source code that uses it. Each resource bundle stores a map of key/value pairs for a specific locale.

    It is common to use or extend java.util.PropertyResourceBundle, which stores the content in an external properties file as shown in the following example:


      ################################################
      # ErrorMessages.properties
      ################################################
      # required user name error message
      error.username.required=User name field is required
      # invalid user name format
      error.username.alphanumeric=User name must be alphanumeric
      # duplicate user name error message
      error.username.duplicate=User name {0} already exists, please choose another one
      ...

     

    Multiple resources can be defined to support different locales (hence the name resource bundle). For example, ErrorMessages_fr.properties can be defined to support the French member of the bundle family. If the resource member of the requested locale does not exist, the default member is used. In the above example, the default resource is ErrorMessages.properties. Depending on the user's locale, the application (JSP or Servlet) retrieves content from the appropriate resource.

    [3-b] Message Formatting
    The J2SE standard class java.util.MessageFormat provides a generic way to create messages with replacement placeholders. A MessageFormat object contains a pattern string with embedded format specifiers as shown below:


      // Example to show how to format a message using placeholder parameters
      String pattern = "User name {0} already exists, please choose another one";
      String userName = request.getParameter("user_name");
      Object[] args = new Object[1];
      args[0] = userName;
      String message = MessageFormat.format(pattern, args);

     

    Here is a more comprehensive example to render error messages using ResourceBundle and MessageFormat:


      // Example to render an error message from a localized ErrorMessages resource (properties file)
      // Utility class to retrieve locale-specific error messages
      public Class ErrorMessageResource {
         
          // Returns the error message for the specified error key in the environment locale
          public String getErrorMessage(String errorKey) {
              return getErrorMessage(errorKey, defaultLocale);
          }
         
          // Returns the error message for the specified error key in the specified locale
          public String getErrorMessage(String errorKey, Locale locale) {
              return getErrorMessage(errorKey, null, locale);
          }
         
          // Returns a formatted error message for the specified error key in the specified locale
          public String getErrorMessage(String errorKey, Object[] args, Locale locale) {   
              // Get localized ErrorMessageResource
              ResourceBundle errorMessageResource = ResourceBundle.getBundle("ErrorMessages", locale);
              // Get localized error message
              String errorMessage = errorMessageResource.getString(errorKey);
              if (args != null) {
                  // Format the message using t

Open Toolbar