輸入數據驗證:雖然爲方便用戶而在客戶端層上提供數據驗證,但仍必須使用 Servlet 在服務器層上執行數據驗證。客戶端驗證本身就不安全,因爲這
些驗證可輕易繞過,例如,通過禁用 Javascript。
一份好的設計通常需要 Web 應用程序框架,以提供服務器端實用程序例程,從而驗證以下內容:[1] 必需字段[2] 字段數據類型(缺省情況下,所有
HTTP 請求參數都是“字符串”)[3] 字段長度[4] 字段範圍[5] 字段選項[6] 字段模式[7] cookie 值[8] HTTP 響應好的做法是將以上例程作爲“驗證器”實用程序
類中的靜態方法實現。以下部分描述驗證器類的一個示例。
[1] 必需字段“始終”檢查字段不爲空,並且其長度要大於零,不包括行距和後面的空格。
如何驗證必需字段的示例:
// 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] 輸入的 Web 應用程序中的字段數據類型和輸入參數欠佳。例如,所有 HTTP 請求參數或 cookie 值的類型都是“字符串”。開發者負責驗證輸入的數據
2014/5/30 353
類型是否正確。使用 Java 基本包裝程序類,來檢查是否可將字段值安全地轉換爲所需的基本數據類型。
驗證數字字段(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
...
}
好的做法是將所有 HTTP 請求參數轉換爲其各自的數據類型。例如,開發者應將請求參數的“integerValue”存儲在請求屬性中,並按以下示例所示來使
用:
// 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");
...
應用程序應處理的主要 Java 數據類型:
Byte
Short
Integer
Long
Float
Double
Date
[3] 字段長度“始終”確保輸入參數(HTTP 請求參數或 cookie 值)有最小長度和/或最大長度的限制。以下示例驗證 userName 字段的長度是否在 8 至 20
個字符之間:
// 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);
}
...
}
2014/5/30 354
...
String userName = request.getParameter("userName");
if (Validator.validateRequired(userName)) {
if (Validator.validateLength(userName, 8, 20)) {
// userName is valid, continue further processing
...
}
}
[4] 字段範圍
始終確保輸入參數是在由功能需求定義的範圍內。
以下示例驗證輸入 numberOfChoices 是否在 10 至 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] 字段選項 Web 應用程序通常會爲用戶顯示一組可供選擇的選項(例如,使用 SELECT HTML 標記),但不能執行服務器端驗證以確保選定的值是其
中一個允許的選項。請記住,惡意用戶能夠輕易修改任何選項值。始終針對由功能需求定義的受允許的選項來驗證選定的用戶值。以下示例驗證用戶針
對允許的選項列表進行的選擇:
// 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[] options = {"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] 字段模式
始終檢查用戶輸入與由功能需求定義的模式是否匹配。例如,如果 userName 字段應僅允許字母數字字符,且不區分大小寫,那麼請使用以下正則表達
式:^[azAZ09]*$
2014/5/30 355
Java 1.3 或更早的版本不包含任何正則表達式包。建議將“Apache 正則表達式包”(請參閱以下“資源”)與 Java 1.3 一起使用,以解決該缺乏支持的問
題。執行正則表達式驗證的示例:
// 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 引進了一種新的正則表達式包(java.util.regex)。以下是使用新的 Java 1.4 正則表達式包的 Validator.matchPattern 修訂版:
// 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 值使用 javax.servlet.http.Cookie 對象來驗證 cookie 值。適用於 cookie 值的相同的驗證規則(如上所述)取決於應用程序需求(如驗證必需
值、驗證長度等)。
驗證必需 cookie 值的示例:
// 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
...
}
}
}
}
2014/5/30 356
[8] HTTP 響應
[81]
過濾用戶輸入要保護應用程序免遭跨站點腳本編制的***,請通過將敏感字符轉換爲其對應的字符實體來清理 HTML。這些是 HTML 敏感字符:<
> " ' % ) ( & +
以下示例通過將敏感字符轉換爲其對應的字符實體來過濾指定字符串:
// Example to filter sensitive data to prevent cross-site scripting
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("<");
break;
case '>':
result.append(">");
break;
case '"':
result.append(""");
break;
case '\'':
result.append("'");
break;
case '%':
result.append("%");
break;
case ';':
result.append(";");
break;
case '(':
result.append("(");
break;
case ')':
result.append(")");
break;
case '&':
result.append("&");
break;
case '+':
result.append("+");
break;
default:
result.append(value.charAt(i));
break;
}
return result;
}
...
}
...
// Filter the HTTP response using Validator.filter
PrintWriter out = response.getWriter();
// set output response
out.write(Validator.filter(response));
out.close();
Java Servlet API 2.3 引進了“過濾器”,它支持攔截和轉換 HTTP 請求或響應。
以下示例使用 Validator.filter 來用“Servlet 過濾器”清理響應:
// 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 out = response.getWriter();
ResponseWrapper wrapper = new ResponseWrapper((HttpServletResponse)response);
2014/5/30 357
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);
output = new CharArrayWriter();
}
public PrintWriter getWriter(){
return new PrintWriter(output);
}
}
}
}
[82]
保護 cookie
在 cookie 中存儲敏感數據時,確保使用 Cookie.setSecure(布爾標誌)在 HTTP 響應中設置 cookie 的安全標誌,以指導瀏覽器使用安全協議(如
HTTPS 或 SSL)發送 cookie。
保護“用戶”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);
推薦