标签:html 区分 undle cookies arrays 示例 查看 重复 密码
可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务在以下情况下会发生跨站点脚本编制 (XSS) 脆弱性:
[1] 不可信数据进入 Web 应用程序,通常来自 Web 请求。
[2] Web 应用程序动态生成了包含此不可信数据的 Web 页面。
[3] 页面生成期间,应用程序不会禁止数据包含可由 Web 浏览器执行的内容,例如 JavaScript、HTML 标记、HTML 属性、鼠标事件、Flash 和 ActiveX。
[4] 受害者通过 Web 浏览器访问生成的 Web 页面,该页面包含已使用不可信数据注入的恶意脚本。
[5] 由于脚本来自 Web 服务器发送的 Web 页面,因此受害者的 Web 浏览器在 Web 服务器的域的上下文中执行恶意脚本。
[6] 这实际违反了 Web 浏览器的同源策略的意图,该策略声明一个域中的脚本不应该能够访问其他域中的资源或运行其他域中的代码。
一旦注入恶意脚本后,攻击者就能够执行各种恶意活动。攻击者可能将私有信息(例如可能包含会话信息的 cookie)从受害者的机器传输给攻击者。攻击者可能以受害者的身份将恶意请求发送到 Web 站点,如果受害者具有管理该站点的管理员特权,这可能对站点尤其危险。
网络钓鱼攻击可用于模仿可信站点,并诱导受害者输入密码,从而使攻击者能够危及受害者在该 Web 站点上的帐户。最后,脚本可利用 Web 浏览器本身中的脆弱性,可能是接管受害者的机器(有时称为“路过式入侵”)。主要有三种类型的 XSS:
[REQUEST]
GET /index.aspx?name=JSmith HTTP/1.1
[RESPONSE]
HTTP/1.1 200 OK
Server: SomeServer
Date: Sun, 01 Jan 2002 00:31:19 GMT
Content-Type: text/html
Accept-Ranges: bytes
Content-Length: 27
<HTML>
Hello JSmith
</HTML>
攻击者可能会利用类似以下情况的攻击:
[ATTACK REQUEST]
GET /index.aspx?name=>"‘><script>alert(‘PWND‘)</script> HTTP/1.1
[ATTACK RESPONSE]
HTTP/1.1 200 OK
Server: SomeServer
Date: Sun, 01 Jan 2002 00:31:19 GMT
Content-Type: text/html
Accept-Ranges: bytes
Content-Length: 83
<HTML>
Hello >"‘><script>alert(‘PWND‘)</script>
</HTML>
在这种情况下,JavaScript 代码将由浏览器执行(>"‘> 部分在此处并不相关)。该问题可能会影响各种类型的产品。
[2] 了解将在其中使用数据的上下文,以及预期的编码。在不同组件之间传输数据时,或在生成可同时包含多个编码的输出(如 Web 页面或多部分邮件消息)时,这尤为重要。研究所有预期的通信协议和数据表示法以确定所需的编码策略。对于将输出到另一个 Web 页面的任何数据(尤其是从外部输入接收到的任何数据),请对所有非字母数字字符使用恰当的编码。
相同输出文档的某些部分可能需要不同的编码,具体取决于输出是在以下哪一项中:
[-] HTML 主体
[-] 元素属性(如 src="XYZ")
[-] URI
[-] JavaScript 段
[-] 级联样式表和样式属性
请注意,“HTML 实体编码”仅适用于 HTML 主体。
请咨询 XSS Prevention Cheat Sheet
http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
以获取有关所需编码和转义类型的更多详细信息。
[3] 策略:识别和减少攻击出现的机会
了解您的软件中可能出现不可信输入的所有潜在区域:参数或自变量、cookie、从网络读取的任何内容、环境变量、反向 DNS 查找、查询结果、请求头、URL 组成部分、电子邮件、文件、文件名、数据库以及向应用程序提供数据的任何外部系统。请记住,此类输入可通过 API 调用间接获取。
[4] 策略:输出编码
对于生成的每个 Web 页面,请使用并指定 ISO-8859-1 或 UTF-8 之类的字符编码。如果未指定编码,Web 浏览器可能通过猜测 Web 页面实际使用的编码来选择不同的编码。这可能导致 Web 浏览器将特定序列视为特殊序列,从而使客户机暴露在不易察觉的 XSS 攻击之下。请参阅 CWE-116 以获取与编码/转义相关的更多减轻威胁的方法。
[5] 策略:识别和减少攻击出现的机会
要帮助减轻针对用户会话 cookie 的 XSS 攻击带来的威胁,请将会话 cookie 设置为 HttpOnly。在支持 HttpOnly 功能的浏览器(如 Internet Explorer 和 Firefox 的较新版本)中,此属性可防止使用 document.cookie 的恶意客户机端脚本访问用户的会话 cookie。这不是完整的解决方案,因为 HttpOnly 并不受所有浏览器支持。更重要的是,XMLHTTPRequest 和其他功能强大的浏览器技术提供了对 HTTP 头的读访问权,包括在其中设置
HttpOnly 标志的 Set-Cookie 头。
[6] 策略
输入验证假定所有输入都是恶意的。使用“接受已知善意”输入验证策略:严格遵守规范的可接受输入的白名单。拒绝任何没有严格遵守规范的输入,或者将其转换为遵守规范的内容。不要完全依赖于将恶意或格式错误的输入加入黑名单。但是,黑名单可帮助检测潜在攻击,或者确定哪些输入格式不正确,以致应当将其彻底拒绝。
执行输入验证时,请考虑所有潜在相关属性,包括长度、输入类型、可接受值的完整范围、缺失或多余输入、语法、跨相关字段的一致性以及业务规则一致性。以业务规则逻辑为例,“boat”可能在语法上有效,因为它仅包含字母数字字符,但如果预期为颜色(如“red”或“blue”),那么它无效。
动态构造 Web 页面时,请使用严格的白名单以根据请求中参数的预期值来限制字符集。所有输入都应进行验证和清理,不仅限于用户应指定的参数,而是涉及请求中的所有数据,包括隐藏字段、cookie、头、URL 本身,等等。导致 XSS 脆弱性持续存在的一个常见错误是仅验证预期会由站点重新显示的字段。常见的情况是,在请求中出现由应用程序服务器或应用程序反射的其他数据,而开发团队却未能预料到此情况。另外,将来的开发者可能会使用当前未反映的字段。因此,建议验证 HTTP 请求的所有部分。请注意,适当的输出编码、转义和引用是防止
XSS 的最有效解决方案,虽然输入验证可能会提供一定的深度防御。输入验证会有效限制将在输出中出现的内容。它并不总是能够防止 XSS,尤其是在您需要支持可包含任意字符的自由格式文本字段的情况下。例如,在聊天应用程序中,心型表情图标(“<3”)可能会通过验证步骤,因为它的使用频率很高。但是,不能将其直接插入到 Web 页面中,因为它包含“<”字符,该字符需要转义或以其他方式进行处理。在此情况下,消除“<”可能会降低 XSS 的风险,但是这会产生不正确的行为,因为这样就不会记录表情图标。
这可能看起来只是略有不便,但在需要表示不等式的数学论坛中,这种情况就更为重要。即使在验证中出错(例如,在 100 个输入字段中忘记一个字段),相应的编码仍有可能针对基于注入的攻击为您提供防护。只要输入验证不是孤立完成的,便仍是有用的技巧,因为它可以大大减少攻击出现的机会,使您能够检测某些攻击,并提供正确编码所无法解决的其他安全性优势。请确保在应用程序内定义良好的界面中执行输入验证。即使某个组件进行了复用或移动到其他位置,这也将有助于保护应用程序
我们建议将服务器升级至 .NET Framework 2.0(或更新的版本),它本身就包括针对跨站点脚本编制攻击进行保护的安全检查。
要确保用户输入仅包含有效值,您可以使用以下验证控件中的一种:
[1] “RangeValidator”:检查用户条目(值)是否在指定的上下界限之间。您可以检查配对数字、字母字符和日期内的范围。
[2] “RegularExpressionValidator”:检查条目是否与正则表达式定义的模式相匹配。此类型的验证使您能够检查可预见的字符序列,如社会保险号码、电子邮件地址、电话号码、邮政编码等中的字符序列。
有助于阻止跨站点脚本编制的正则表达式示例:
- 可以拒绝基本跨站点脚本编制变体的正则表达式可能如下:^([^<]|\<[^a-zA-Z])*[<]?$
- 拒绝上述所有字符的一般正则表达式可能如下:^([^\<\>\"\‘\%\;\)\(\&\+]*)$
重要注意事项:验证控件不会阻止用户输入或更改页面处理流程;它们只会设置错误状态,并产生错误消息。程序员的职责是,在执行进一步的应用程序特定操作前,测试代码中控件的状态。
有两种方法可检查用户输入的有效性:
1. 测试常规错误状态:
在代码中,测试页面的 IsValid 属性。 该属性会将页面上所有验证控件的 IsValid 属性值汇总(使用逻辑 AND)。如果将其中一个验证控件设置为无效,那么页面属性将会返回 false。
2. 测试个别控件的错误状态:
在页面的“验证器”集合中循环,该集合包含对所有验证控件的引用。 然后,您就可以检查每个验证控件的 IsValid 属性。
最后,我们建议使用 Microsoft Anti-Cross Site Scripting Library(V1.5 更高版本)对不受信任的用户输入进行编码。
Anti-Cross Site Scripting Library 显现下列方法:
[1] HtmlEncode - 将在 HTML 中使用的输入字符串编码
[2] HtmlAttributeEncode - 将在 HTML 属性中使用的输入字符串编码
[3] JavaScriptEncode - 将在 JavaScript 中使用的输入字符串编码
[4] UrlEncode - 将在“统一资源定位器 (URL)”中使用的输入字符串编码
[5] VisualBasicScriptEncode - 将在 Visual Basic 脚本中使用的输入字符串编码
[6] XmlEncode - 将在 XML 中使用的输入字符串编码
[7] XmlAttributeEncode - 将在 XML 属性中使用的输入字符串编码
如果要适当使用 Microsoft Anti-Cross Site Scripting Library 来保护 ASP.NET Web 应用程序,您必须运行下列操作:
第 1 步:复查生成输出的 ASP.NET 代码
第 2 步:判断是否包括不受信任的输入参数
第 3 步:判断不受信任的输入的上下文是否作为输出,判断要使用哪个编码方法
第 4 步:编码输出
第 3 步骤的示例:注意:如果要使用不受信任的输入来安装 HTML 属性,便应该使用 Microsoft.Security.Application.HtmlAttributeEncode 方法,将不受信任的输入编码。
另外,如果要在 JavaScript 的上下文中使用不受信任的输入,便应该使用 Microsoft.Security.Application.JavaScriptEncode 来编码。
// Vulnerable code
// Note that untrusted input is being treated as an HTML attribute
Literal1.Text = "<hr noshade size=[untrusted input here]>";
// Modified code
Literal1.Text = "<hr noshade size="+Microsoft.Security.Application.AntiXss.HtmlAttributeEncode([untrusted input here])+">";
第 4 步骤的示例:将输出编码时,必须记住的一些重要事项:
[1] 输出应该编码一次。
[2] 输出的编码与实际撰写,应该尽可能接近。 例如,如果应用程序读取用户输入、处理输入,再用某种形式将它重新写出,便应该紧接在撰写输出之前进行编码。
// Incorrect sequence
protected void Button1_Click(object sender, EventArgs e)
{
// Read input
String Input = TextBox1.Text;
// Encode untrusted input
Input = Microsoft.Security.Application.AntiXss.HtmlEncode(Input);
// Process input
...
// Write Output
Response.Write("The input you gave was"+Input);
}
// Correct Sequence
protected void Button1_Click(object sender, EventArgs e)
{
// Read input
String Input = TextBox1.Text;
// Process input
...
// Encode untrusted input and write output
Response.Write("The input you gave was"+
Microsoft.Security.Application.AntiXss.HtmlEncode(Input));
}
** 输入数据验证:虽然为了用户的方便,可以提供“客户端”层数据的数据验证,但必须使用 Servlet 在服务器层执行验证。 客户端验证本身就不安全,因为这些验证可轻易绕过,例如,通过禁用 Javascript。
一份好的设计通常需要 Web 应用程序框架,以提供服务器端实用程序例程,从而验证以下内容:
必需字段“始终”检查字段不为空,并且其长度要大于零,不包括行距和后面的空格。如何验证必需字段的示例:
// 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
...
}
输入的 Web 应用程序中的字段数据类型和输入参数欠佳。例如,所有 HTTP 请求参数或 cookie 值的类型都是“字符串”。开发者负责验证输入的数据类型是否正确。 使用 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
“始终”确保输入参数(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);
}
}
String userName = request.getParameter("userName");
if (Validator.validateRequired(userName)) {
if (Validator.validateLength(userName, 8, 20)) {
// userName is valid, continue further processing
}
}
始终确保输入参数是在由功能需求定义的范围内。以下示例验证输入 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
}
}
}
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
}
始终检查用户输入与由功能需求定义的模式是否匹配。例如,如果 userName 字段应仅允许字母数字字符,且不区分大小写,那么请使用以下正则表达式:^[a-zA-Z0-9]*$
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;
}
}
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
}
}
}
}
[8-1] 过滤用户输入要保护应用程序免遭跨站点脚本编制的攻击,请通过将敏感字符转换为其对应的字符实体来清理 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);
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);
}
}
}
}
[8-2] 保护 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);
许多 J2EE Web 应用程序体系结构都遵循“模型视图控制器(MVC)”模式。在该模式中,Servlet 扮演“控制器”的角色。Servlet 将应用程序处理委派给 EJB 会话 Bean(模型)之类的 JavaBean。然后,Servlet 再将请求转发给 JSP(视图),以呈现处理结果。Servlet 应检查所有的输入、输出、返回码、错误代码和已知的异常,以确保实际处理按预期进行。
数据验证可保护应用程序免遭恶意数据篡改,而有效的错误处理策略则是防止应用程序意外泄露内部错误消息(如异常堆栈跟踪)所不可或缺的。好的错误处理策略会处理以下项:
[1] 定义错误
[2] 报告错误
[3] 呈现错误
[4] 错误映射
[1] 定义错误
应避免在应用程序层(如 Servlet)中硬编码错误消息。 相反地,应用程序应该使用映射到已知应用程序故障的错误密钥。好的做法是定义错误密钥,且该错误密钥映射到 HTML 表单字段或其他 Bean 属性的验证规则。例如,如果需要“user_name”字段,其内容为字母数字,并且必须在数据库中是唯一的,那么就应定义以下错误密钥:
(a) ERROR_USERNAME_REQUIRED:该错误密钥用于显示消息,以通知用户需要“user_name”字段;
(b) ERROR_USERNAME_ALPHANUMERIC:该错误密钥用于显示消息,以通知用户“user_name”字段应该是字母数字;
(c) ERROR_USERNAME_DUPLICATE:该错误密钥用于显示消息,以通知用户“user_name”值在数据库中重复;
(d) ERROR_USERNAME_INVALID:该错误密钥用于显示一般消息,以通知用户“user_name”值无效;
好的做法是定义用于存储和报告应用程序错误的以下框架 Java 类:
- ErrorKeys:定义所有错误密钥
// 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:封装个别错误
// 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:封装错误的集合
// 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();
}
以下是使用上述框架类来处理“user_name”字段验证错误的示例:
// 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] 报告错误
有两种方法可报告 web 层应用程序错误:
(a) Servlet 错误机制
(b) JSP 错误机制
[2-a] Servlet 错误机制
Servlet 可通过以下方式报告错误:
- 转发给输入 JSP(已将错误存储在请求属性中),或
- 使用 HTTP 错误代码参数来调用 response.sendError,或
- 抛出异常
好的做法是处理所有已知应用程序错误(如 [1] 部分所述),将这些错误存储在请求属性中,然后转发给输入 JSP。输入 JSP 应显示错误消息,并提示用户重新输入数据。以下示例阐明转发给输入 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);
}
如果 Servlet 无法转发给已知的 JSP 页面,那么第二个选项是使用 response.sendError 方法,将 HttpServletResponse.SC_INTERNAL_SERVER_ERROR(状态码 500)作为参数,来报告错误。 请参阅 javax.servlet.http.HttpServletResponse 的 Javadoc,以获取有关各种 HTTP 状态码的更多详细信息。返回 HTTP 错误的示例:
// 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));
}
作为最后的手段,Servlet 可以抛出异常,且该异常必须是以下其中一类的子类: - RuntimeException - ServletException - IOException
[2-b] JSP 错误机制
JSP 页面通过定义 errorPage 伪指令来提供机制,以处理运行时异常,如以下示例所示:
<%@ page errorPage="/errors/userValidation.jsp" %>
未捕获的 JSP 异常被转发给指定的 errorPage,并且原始异常设置在名称为 javax.servlet.jsp.jspException 的请求参数中。错误页面必须包括 isErrorPage 伪指令:
<%@ page isErrorPage="true" %>
isErrorPage 伪指令导致“exception”变量初始化为所抛出的异常对象。
[3] 呈现错误
J2SE Internationalization API 提供使应用程序资源外部化以及将消息格式化的实用程序类,其中包括:
(a) 资源束
(b) 消息格式化
[3-a] 资源束
资源束通过将本地化数据从使用该数据的源代码中分离来支持国际化。每一资源束都会为特定的语言环境存储键/值对的映射。
java.util.PropertyResourceBundle 将内容存储在外部属性文件中,对其进行使用或扩展都很常见,如以下示例所示:
################################################
# 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
可定义多种资源,以支持不同的语言环境(因此名为资源束)。例如,可定义 ErrorMessages_fr.properties 以支持该束系列的法语成员。如果请求的语言环境的资源成员不存在,那么会使用缺省成员。在以上示例中,缺省资源是 ErrorMessages.properties。应用程序(JSP 或 Servlet)会根据用户的语言环境从适当的资源检索内容。
[3-b] 消息格式化
J2SE 标准类 java.util.MessageFormat 提供使用替换占位符来创建消息的常规方法。MessageFormat 对象包含嵌入了格式说明符的模式字符串,如下所示:
// 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);
以下是使用 ResourceBundle 和 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 the specified placeholders args
return MessageFormat.format(errorMessage, args);
} else {
return errorMessage;
}
}
// default environment locale
private Locale defaultLocale = Locale.getDefaultLocale();
}
// Get the user‘s locale
Locale userLocale = request.getLocale();
// Check if there were any validation errors
Errors errors = (Errors)request.getAttribute("errors");
if (errors != null && errors.hasErrors()) {
// iterate through errors and output error messages corresponding to the "user_name" property
ArrayList userNameErrors = errors.getErrors("user_name");
ListIterator iterator = userNameErrors.iterator();
while (iterator.hasNext()) {
// Get the next error object
Error error = (Error)iterator.next();
String errorMessage = ErrorMessageResource.getErrorMessage(error.getKey(), userLocale);
output.write(errorMessage + "\r\n");
}
}
建议定义定制 JSP 标记(如 displayErrors),以迭代处理并呈现错误消息,如以上示例所示。
[4] 错误映射
通常情况下,“Servlet 容器”会返回与响应状态码或异常相对应的缺省错误页面。可以使用定制错误页面来指定状态码或异常与 Web 资源之间的映射。好的做法是开发不会泄露内部错误状态的静态错误页面(缺省情况下,大部分 Servlet 容器都会报告内部错误消息)。该映射配置在“Web 部署描述符(web.xml)”中,如以下示例所指定:
<!-- Mapping of HTTP error codes and application exceptions to error pages -->
<error-page>
<exception-type>UserValidationException</exception-type>
<location>/errors/validationError.html</error-page>
</error-page>
<error-page>
<error-code>500</exception-type>
<location>/errors/internalError.html</error-page>
</error-page>
<error-page>
...
</error-page>
...
虽然为方便用户而在客户端层上提供数据验证,但仍必须始终在服务器层上执行数据验证。客户端验证本身就不安全,因为这些验证可轻易绕过,例如,通过禁用 Javascript。一份好的设计通常需要 Web 应用程序框架,以提供服务器端实用程序例程,从而验证以下内容:[1] 必需字段[2] 字段数据类型(缺省情况下,所有 HTTP 请求参数都是“字符串”)[3] 字段长度[4] 字段范围[5] 字段选项[6] 字段模式[7] cookie 值[8] HTTP 响应好的做法是实现一个或多个验证每个应用程序参数的函数。以下部分描述一些检查的示例。
[1] 必需字段“始终”检查字段不为空,并且其长度要大于零,不包括行距和后面的空格。如何验证必需字段的示例:
// PHP example to validate required fields
function validateRequired($input) {
$pass = false;
if (strlen(trim($input))>0){
$pass = true;
}
return $pass;
}
if (validateRequired($fieldName)) {
// fieldName is valid, continue processing request
...
}
[2] 输入的 Web 应用程序中的字段数据类型和输入参数欠佳。例如,所有 HTTP 请求参数或 cookie 值的类型都是“字符串”。开发者负责验证输入的数据类型是否正确。
[3] 字段长度“始终”确保输入参数(HTTP 请求参数或 cookie 值)有最小长度和/或最大长度的限制。
[4] 字段范围。始终确保输入参数是在由功能需求定义的范围内。
[5] 字段选项 Web 应用程序通常会为用户显示一组可供选择的选项(例如,使用 SELECT HTML 标记),但不能执行服务器端验证以确保选定的值是其中一个允许的选项。请记住,恶意用户能够轻易修改任何选项值。始终针对由功能需求定义的受允许的选项来验证选定的用户值。
[6] 字段模式。始终检查用户输入与由功能需求定义的模式是否匹配。例如,如果 userName 字段应仅允许字母数字字符,且不区分大小写,那么请使用以下正则表达式:^[a-zA-Z0-9]+$
[7] cookie 值。适用于 cookie 值的相同的验证规则(如上所述)取决于应用程序需求(如验证必需值、验证长度等)。
[8] HTTP 响应
[8-1] 过滤用户输入要保护应用程序免遭跨站点脚本编制的攻击,开发者应通过将敏感字符转换为其对应的字符实体来清理 HTML。这些是 HTML 敏感字符:< > " ‘ % ; ) ( & +
PHP 包含一些自动化清理实用程序函数,如 htmlentities():
$input = htmlentities($input, ENT_QUOTES, ‘UTF-8‘);
此外,为了避免“跨站点脚本编制”的 UTF-7 变体,您应该显式定义响应的 Content-Type 头,例如:
<?php
header(‘Content-Type: text/html; charset=UTF-8‘);
?>
[8-2] 保护 cookie
在 cookie 中存储敏感数据且通过 SSL 来传输时,请确保先在 HTTP 响应中设置 cookie 的安全标志。这将会指示浏览器仅通过 SSL 连接来使用该 cookie。为了保护 cookie,您可以使用以下代码示例:
<$php
$value = "some_value";
$time = time()+3600;
$path = "/application/";
$domain = ".example.com";
$secure = 1;
setcookie("CookieName", $value, $time, $path, $domain, $secure, TRUE);
?>
此外,我们建议您使用 HttpOnly 标志。当 HttpOnly 标志设置为 TRUE 时,将只能通过 HTTP 协议来访问 cookie。这意味着无法用脚本语言(如 JavaScript)来访问 cookie。该设置可有效地帮助减少通过 XSS 攻击盗用身份的情况(虽然并非所有浏览器都支持该设置)。在 PHP 5.2.0 中添加了 HttpOnly 标志。
欢迎大家分享更好的思路,热切期待^^_^^ !
标签:html 区分 undle cookies arrays 示例 查看 重复 密码
原文地址:http://blog.csdn.net/qq_29277155/article/details/52895135