2023年6月25日发(作者:)
常见安全漏洞及修复方法
一、SQL 注入
1.1概况及技术描述
严重性:高
类 型:应用程序级别测试
WASC威胁分类:命令执行类型:SQL注入
CVE引用:不适用
安全风险:可能会在WEB服务器上运行远程命令,这通常意味着完全破坏服务器及其内容。
可能原因:
未对用户输入正确执行危险字符清理
技术描述:
这个“SQL 注入”变体试图将易受到攻击的脚本和参数名称所组成的文本写入本地文件系统的文件中。
不过,AppScan 不可能访问 SQL Server 文件系统,因此必须由用户来验证测试是否成功。
AppScan 测试完成之后,请实际访问 SQL Server 机器,或通过远程连接来访问。
在 C 驱动器的根目录下,请检查是否有名称为“”的文件(也就是
C:)。
如果有,它会包含一份列表,列出所有易受攻击的脚本和参数。
这个变体的技术描述:
MS SQL 有一个能够运行 Shell 命令的内建函数。从查询环境安装一个 Shell 操作参数,便可以调用这个函数。
这个函数的利用方式如下:
假设基于 Web 的应用程序使用下列查询:
SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='$category' ORDER BY PRICE
其中 $category 是用户提供的类别描述(可能使用选择框)。
提供 ' p_cmdshell 'echo some text >> C:' -- 字符串(作为
$category)之后,形成的查询如下:
SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='' p_cmdshell
'echo some text >> C:' --' ORDER BY PRICE
现在,这个查询可以分为三个部分:
[1] 第一个 SQL 查询:SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=''
[2] 第二个 SQL 查询,执行 Shell 命令:p_cmdshell 'echo some text >>
C:'
1 [3] MS SQL 注释:--' ORDER BY PRICE
如您所能见到,恶意的输入将查询的语意改成:查询、执行 Shell 命令,以及注释。
Web 应用程序通常在后端使用数据库,以与企业数据仓库交互。查询数据库事实上的标准语言是 SQL(各大数据库供应商都有自己的不同版本)。Web 应用程序通常会获取用户输入(取自 HTTP 请求),将它并入 SQL 查询中,然后发送到后端数据库。接着应用程序便处理查询结果,有时会向用户显示结果。
如果应用程序对用户(攻击者)的输入处理不够小心,攻击者便可以利用这种操作方式。在此情况下,攻击者可以注入恶意的数据,当该数据并入 SQL 查询中时,就将查询的原始语法更改得面目全非。例如,如果应用程序使用用户的输入(如用户名和密码)来查询用户帐户的数据库表,以认证用户,而攻击者能够将恶意数据注入查询的用户名部分(和/或密码部分),查询便可能更改成完全不同的数据复制查询,可能是修改数据库的查询,或在数据库服务器上运行 Shell 命令的查询。
一般而言,攻击者会分步实现这个目标。他会先学习 SQL 查询的结构,然后使用该知识来阻挠查询(通过注入更改查询语法的数据),使执行的查询不同于预期。假设相关查询是:
SELECT COUNT(*) FROM accounts WHERE username='$user' AND password='$pass'
其中 $user 和 $pass 是用户输入(从调用构造查询的脚本的 HTTP 请求收集而来 -
可能是来自 GET 请求查询参数,也可能是来自 POST 请求主体参数)。此查询的一般用法,其值为 $user=john、$password=secret123。形成的查询如下:SELECT COUNT(*) FROM
accounts WHERE username='john' AND password='secret123'
如果数据库中没有这个用户密码配对,预期的查询结果便是 0,如果此类配对存在(也就是数据库中有名称为“john”的用户,且其密码为“secret123”),结果便是 >0。这是应用程序的基本认证机制。但攻击者可以用下列方式来更改此查询:
攻击者可以提供单引号字符(')所组成的输入,使数据库发出错误消息,其中通常包含关于 SQL 查询的有价值的信息。攻击者只需在发送的请求中包含用户值 ',并在密码中包含任何值(如 foobar)。结果便是下列(格式错误)的 SQL 查询:SELECT COUNT(*) FROM
accounts WHERE username=''' AND password='foobar'
这可能会产生以下错误消息(取决于后端所使用的特定数据库):查询表达式 'username
= ''' AND password = 'foobar'' 中发生语法错误(遗漏运算符)。
这时攻击者便得知查询是根据表达式 username='$user' AND password='$pass' 来构建的。利用手边的 SQL 查询时需要这一关键信息。攻击者了解查询的格式后,下一步只需使用:
user = ' or 1=1 or ''=' password = foobar
生成的查询如下:
SELECT COUNT(*) FROM accounts WHERE username='' or 1=1 or ''='' AND
password='foobar'
这表示查询(在 SQL 数据库中)对于“accounts”表的每项记录都会返回 TRUE,因为
1=1 表达式永远为真。因此,查询会返回“accounts”中的记录数量,于是用户(攻击者)也会被视为有效。这个探测方法有若干变体,例如,发送 '; or '(您应该记住,几乎所有供应 2 商都有他们自己唯一的 SQL“版本”。具体地说,发送 ' having 1=1,也会生成错误消息,此消息会泄露有关列名称的信息。在某些情况下,用户输入不会并入字符串上下文(用单引号括住),而是并入数字上下文,换言之,就是依现状嵌入。因此,在这种情况下,可以使用输入字符串 1 having 1=1。
* 盲目 SQL 注入技术:
降低 SQL 注入攻击风险的一般方式,是禁止详细的 SQL 错误消息,攻击者通常便利用这些消息(如上述示例所说明),轻易找出容易遭受“SQL 注入”的脚本。
这个(以遮盖获取安全)解决方案可以利用称为“盲目 SQL 注入”的技术来略过,黑客不需要依赖返回 SQL 错误消息,便能找出容易遭受“SQL 注入”的脚本。
这项技术需要发送易受攻击的参数(被嵌入在 SQL 查询中的参数)已被修改的请求,将参数修改成,使响应指出是否在 SQL 查询上下文中使用数据。这项修改包括搭配原始字符串来使用 AND Boolean 表达式,使它一时得出 true,一时得出 false。在一种情况中,最终结果应该与原始结果相同(例如:登录成功),在另一情况中,结果应该不同(例如:登录失败)。在某些少见的情况中,得出 true 的 OR 表达式也很有用。
如果原始数据是数字,可以耍较简单的花招。原始数据(如 123)可以在一个请求中替换为
0+123,在另一个请求中替换为 456+123。第一个请求的结果应该与原始结果相同,第二个请求的结果应该不同(因为得出的数字是 579)。在某些情况中,我们仍需要上面所说明的攻击版本(使用 AND 和 OR),但并不转义字符串上下文。
盲目 SQL 注入背后的概念是,即使未直接收到数据库的数据(以错误消息或泄漏信息的形式),也可能抽取数据库中的数据,每次一个位,或以恶意方式修改查询。观念在于,应用程序行为(结果与原始结果相同,或结果与原始结果不同)可以提供关于所求值(已修改)之查询的单位元相关信息,也就是说,攻击者有可能规划出以应用程序行为(相同/不同于原始行为)的形式来影响其求值(单位元)的 SQL Boolean
受影响产品:
基于 MS SQL 的 Web 应用程序及各种类型的产品。
参考资料
SQL注入常见问题:/FAQs/SQLInjectionFAQ/tabid/56/
清理CGI脚本中的用户提供数据:/advisories/
1.2修复方法
一般
若干问题的补救方法在于对用户输入进行清理。
通过验证用户输入未包含危险字符,便可能防止恶意的用户导致应用程序执行计划外的任务,例如:启动任意 SQL 查询、嵌入将在客户端执行的 Javascript 代码、运行各种操作系统命令,等等。
建议过滤出所有以下字符:
[1] |(竖线符号)
[2] & (& 符号)
3 [3];(分号)
[4] $(美元符号)
[5] %(百分比符号)
[6] @(at 符号)
[7] '(单引号)
[8] "(引号)
[9] '(反斜杠转义单引号)
[10] "(反斜杠转义引号)
[11] <>(尖括号)
[12] ()(括号)
[13] +(加号)
[14] CR(回车符,ASCII 0x0d)
[15] LF(换行,ASCII 0x0a)
[16] ,(逗号)
[17] (反斜杠)
以下部分描述各种问题、问题的修订建议以及可能触发这些问题的危险字符:
SQL 注入和 SQL 盲注:
A. 确保用户输入的值和类型(如 Integer、Date 等)有效,且符合应用程序预期。
B. 利用存储过程,将数据访问抽象化,让用户不直接访问表或视图。当使用存储过程时,请利用 ADO 命令对象来实施它们,以强化变量类型。
C. 清理输入以排除上下文更改符号,例如:
[1] '(单引号)
[2] "(引号)
[3] '(反斜线转义单引号)
[4] "(反斜杠转义引号)
[5] )(结束括号)
[6] ;(分号)
跨站点脚本编制:
A. 清理用户输入,并过滤出 JavaScript 代码。我们建议您过滤下列字符:
[1] <>(尖括号)
[2] "(引号)
[3] '(单引号)
[4] %(百分比符号)
[5] ;(分号)
[6] ()(括号)
[7] &(& 符号)
[8] +(加号)
B. 如果要修订 <%00script> 变体,请参阅 MS 文章 821349
C. 对于 UTF-7 攻击: [-] 可能的话,建议您施行特定字符集编码(使用 'Content-Type'
头或 标记)。
HTTP 响应分割:清理用户输入(至少是稍后嵌入在 HTTP 响应中的输入)。
请确保输入未包含恶意的字符,例如:
[1] CR(回车符,ASCII 0x0d)
4 [2] LF(换行,ASCII 0x0a)远程命令执行:清理输入以排除对执行操作系统命令有意义的符号,例如:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
执行 shell 命令:
A. 绝不将未检查的用户输入传递给 eval()、open()、sysopen()、system() 之类的 Perl 命令。
B. 确保输入未包含恶意的字符,例如:
[1] $(美元符号)
[2] %(百分比符号)
[3] @(at 符号)
XPath 注入:清理输入以排除上下文更改符号,例如:
[1] '(单引号)
[2] "(引号) 等
LDAP 注入:
A. 使用正面验证。字母数字过滤(A..,0..9)适合大部分 LDAP 查询。
B. 应该过滤出或进行转义的特殊 LDAP 字符:
[1] 在字符串开头的空格或“#”字符
[2] 在字符串结尾的空格字符
[3] ,(逗号)
[4] +(加号)
[5] "(引号)
[6] (反斜杠)
[7] <>(尖括号)
[8] ;(分号)
[9] ()(括号)
MX 注入:
应该过滤出特殊 MX 字符:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)记录伪造:
应该过滤出特殊记录字符:
[1] CR(回车符,ASCII 0x0d)
[2] LF(换行,ASCII 0x0a)
[3] BS(退格,ASCII 0x08)
ORM 注入:
A. 确保用户输入的值和类型(如 Integer、Date 等)有效,且符合应用程序预期。B. 利用存储过程,将数据访问抽象化,让用户不直接访问表或视图。
C. 使用参数化查询 API
D. 清理输入以排除上下文更改符号,例如: (*):
[1] '(单引号)
[2] "(引号)
[3] '(反斜线转义单引号)
[4] "(反斜杠转义引号)
5
[5] )(结束括号)
[6] ;(分号)
(*) 这适用于 SQL。高级查询语言可能需要不同的清理机制。
J2EE
** 预编译语句:
以下是保护应用程序免遭 SQL 注入(即恶意篡改 SQL 参数)的三种可行方法。 使用以下方法,而非动态构建 SQL 语句:
[1] PreparedStatement,通过预编译并且存储在 PreparedStatement 对象池中。
PreparedStatement 定义 setter 方法,以注册与受支持的 JDBC SQL 数据类型兼容的输入参数。 例如,setString 应该用于 VARCHAR 或 LONGVARCHAR 类型的输入参数(请参阅 Java
API,以获取进一步的详细信息)。 通过这种方法来设置输入参数,可防止攻击者通过注入错误字符(如单引号)来操纵 SQL 语句。
如何在 J2EE 中使用 PreparedStatement 的示例:
// 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)(datasourceName)).getConnection(dbUserName, dbPassword);
} else {
try {
// using the DriverManager to get a JDBC connection
e(jdbcDriverClassPath);
myConnection = nection(jdbcURL, dbUserName,
dbPassword);
} catch (ClassNotFoundException e) {
...
}
}
...
try {
PreparedStatement myStatement = eStatement("select *
from users where username = ?");
ing(1, userNameField);
ResultSet rs = eQuery();
...
6 ();
} catch (SQLException sqlException) {
...
} finally {
();
();
}
[2] CallableStatement,扩展 PreparedStatement 以执行数据库 SQL 存储过程。 该类继承 PreparedStatement 的输入 setter 方法(请参阅上面的 [1])。
以下示例假定已创建该数据库存储过程:
CREATE PROCEDURE select_user (@username varchar(20))
AS SELECT * FROM USERS WHERE USERNAME = @username;
如何在 J2EE 中使用 CallableStatement 以执行以上存储过程的示例:
// 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)(datasourceName)).getConnection(dbUserName, dbPassword);
} else {
try {
// using the DriverManager to get a JDBC connection
e(jdbcDriverClassPath);
myConnection = nection(jdbcURL, dbUserName,
dbPassword);
} catch (ClassNotFoundException e) {
...
}
}
...
try {
PreparedStatement myStatement = eCall("{?= call
select_user ?,?}");
ing(1, userNameField);
erOutParameter(1, R);
ResultSet rs = eQuery();
...
7 ();
} catch (SQLException sqlException) {
...
} finally {
();
();
}
[3] 实体 Bean,代表持久存储机制中的 EJB 业务对象。 实体 Bean 有两种类型:bean 管理和容器管理。 当使用 bean 管理的持久性时,开发者负责撰写访问数据库的 SQL 代码(请参阅以上的 [1] 和 [2] 部分)。 当使用容器管理的持久性时,EJB 容器会自动生成 SQL 代码。 因此,容器要负责防止恶意尝试篡改生成的 SQL 代码。
如何在 J2EE 中使用实体 Bean 的示例:
// J2EE EJB Example
try {
// lookup the User home interface
UserHome userHome = (UserHome)();
// find the User remote interface
User = PrimaryKey(new UserKey(userNameField));
...
} catch (Exception e) {
...
}
推荐使用的 JAVA 工具
引用
/j2se/1.4.1/docs/api/java/sql/
/j2se/1.4.1/docs/api/java/sql/
** 输入数据验证:虽然为方便用户而在客户端层上提供数据验证,但仍必须使用 Servlet
在服务器层上执行数据验证。客户端验证本身就不安全,因为这些验证可轻易绕过,例如,通过禁用 Javascript。
一份好的设计通常需要 Web 应用程序框架,以提供服务器端实用程序例程,从而验证以下内容:[1] 必需字段[2] 字段数据类型(缺省情况下,所有 HTTP 请求参数都是“字符串”)[3] 字段长度[4] 字段范围[5] 字段选项[6] 字段模式[7] cookie 值[8] HTTP 响应好的做法是将以上例程作为“验证器”实用程序类中的静态方法实现。以下部分描述验证器类的一个示例。
[1] 必需字段“始终”检查字段不为空,并且其长度要大于零,不包括行距和后面的空格。
8 // Java example to validate required fields
public Class Validator {
...
public static boolean validateRequired(String value) {
boolean isFieldValid = false;
if (value != null && ().length() > 0) {
isFieldValid = true;
}
return isFieldValid;
}
...
}
...
String fieldValue = ameter("fieldName");
if (teRequired(fieldValue)) {
// fieldValue is valid, continue processing request
...
}
[2] 输入的 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 {
nt(value);
isFieldValid = true;
} catch (Exception e) {
isFieldValid = false;
}
return isFieldValid;
}
...
}
...
// check if the HTTP request parameter is of type int
String fieldValue = ameter("fieldName");
if (teInt(fieldValue)) {
// fieldValue is valid, continue processing request
9 ...
}
好的做法是将所有 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 = ameter("fieldName");
if (teInt(fieldValue)) {
// convert fieldValue to an Integer
Integer integerValue = eger(fieldValue);
// store integerValue in a request attribute
ribute("fieldName", integerValue);
}
...
// Use the request attribute for further processing
Integer integerValue = (Integer)ribute("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 (() >= minLength &&
() <= maxLength);
}
10 ...
}
...
String userName = ameter("userName");
if (teRequired(userName)) {
if (teLength(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 = ameter("numberOfChoices");
if (teRequired(fieldValue)) {
if (teInt(fieldValue)) {
int numberOfChoices = nt(fieldValue);
if (teRange(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) {
11 boolean isValidValue = false;
try {
List list = (options);
if (list != null) {
isValidValue = ns(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 = ameter("userSelection");
if (teOption(options, userSelection)) {
// valid user selection, continue processing request
...
}
[6] 字段模式
始终检查用户输入与由功能需求定义的模式是否匹配。例如,如果 userName 字段应仅允许字母数字字符,且不区分大小写,那么请使用以下正则表达式:^[a-zA-Z0-9]*$
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 ;
import axException;
public Class Validator {
...
public static boolean matchPattern(String value, String expression) {
boolean match = false;
if (validateRequired(expression)) {
RE r = new RE(expression);
match = (value);
}
return match;
}
12 ...
}
...
// Verify that the userName request parameter is alpha-numeric
String userName = ameter("userName");
if (attern(userName, "^[a-zA-Z0-9]*$")) {
// userName is valid, continue processing request
...
}
Java 1.4 引进了一种新的正则表达式程序包()。以下是使用新的 Java 1.4
正则表达式程序包的 attern 修订版:
// Example to validate that a given value matches a specified pattern
// using the Java 1.4 regular expression package
import n;
import r;
public Class Validator {
...
public static boolean matchPattern(String value, String expression) {
boolean match = false;
if (validateRequired(expression)) {
match = s(expression, value);
}
return match;
}
...
}
[7] cookie 值使用 对象来验证 cookie 值。适用于 cookie
值的相同的验证规则(如上所述)取决于应用程序需求(如验证必需值、验证长度等)。
验证必需 cookie 值的示例:
// Example to validate a required cookie value
// First retrieve all available cookies submitted in the HTTP request
Cookie[] cookies = kies();
if (cookies != null) {
// find the "user" cookie
for (int i=0; i<; ++i) {
if (cookies[i].getName().equals("user")) {
// validate the cookie value
if (teRequired(cookies[i].getValue()) {
// valid cookie value, continue processing request
...
13 }
}
}
}
[8] HTTP 响应
[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(());
for (int i=0; i<(); ++i) {
switch ((i)) {
case '<':
("<");
break;
case '>':
(">");
break;
case '"':
(""");
break;
case ''':
("'");
break;
case '%':
("%");
break;
case ';':
(";");
break;
case '(':
("(");
break;
case ')':
(")");
14 break;
case '&':
("&");
break;
case '+':
("+");
break;
default:
((i));
break;
}
return result;
}
...
}
...
// Filter the HTTP response using
PrintWriter out = ter();
// set output response
((response));
();
Java Servlet API 2.3 引进了“过滤器”,它支持拦截和转换 HTTP 请求或响应。
以下示例使用 来用“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 = ter();
ResponseWrapper wrapper = new
ResponseWrapper((HttpServletResponse)response);
er(request, wrapper);
CharArrayWriter caw = new CharArrayWriter();
((ng()));
tentType("text/html");
tentLength(ng().length());
15 (ng());
();
}
...
public class CharResponseWrapper extends HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return ng();
}
public CharResponseWrapper(HttpServletResponse response){
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter(){
return new PrintWriter(output);
}
}
}
}
[8-2] 保护 cookie
在 cookie 中存储敏感数据时,确保使用 ure(布尔标志)在 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");
ure(true);
kie(cookie);
推荐使用的 JAVA 工具用于服务器端验证的两个主要 Java 框架是:
[1] Jakarta Commons Validator(与 Struts 1.1 集成)Jakarta Commons Validator 实施所有以上数据验证需求,是强大的框架。这些规则配置在定义表单字段的输入验证规则的
XML 文件中。在缺省情况下,Struts 支持在使用 Struts“bean:write”标记撰写的所有数据上,过滤 [8] HTTP 响应中输出的危险字符。可通过设置“filter=false”标志来禁用该过滤。
Struts 定义以下基本输入验证器,但也可定义定制的验证器: required:如果字段包含空格以外的任何字符,便告成功。
mask:如果值与掩码属性给定的正则表达式相匹配,便告成功。
range:如果值在 min 和 max 属性给定的值的范围内((value >= min) & (value <= max)), 16 便告成功。
maxLength:如果字段长度小于或等于 max 属性,便告成功。
minLength:如果字段长度大于或等于 min 属性,便告成功。
byte、short、integer、long、float、double:如果可将值转换为对应的基本类型,便告成功。
date:如果值代表有效日期,便告成功。可能会提供日期模式。
creditCard:如果值可以是有效的信用卡号码,便告成功。
email:如果值可能是有效的电子邮件地址,便告成功。
...
classname="hecks" method="validateRequired" msg="ed">
classname="hecks" method="validateMask" msg="d">
...
...
[2] JavaServer Faces 技术
“JavaServer Faces 技术”是一组代表 UI 组件、管理组件状态、处理事件和输入验证的
17 Java API(JSR 127)。
JavaServer Faces API 实现以下基本验证器,但可定义定制的验证器:
validate_doublerange:在组件上注册 DoubleRangeValidator
validate_length:在组件上注册 LengthValidator
validate_longrange:在组件上注册 LongRangeValidator
validate_required:在组件上注册 RequiredValidator
validate_stringrange:在组件上注册 StringRangeValidator
validator:在组件上注册定制的 Validator
JavaServer Faces API 定义以下 UIInput 和 UIOutput 处理器(标记):
input_date:接受以 实例格式化的
output_date:显示以 实例格式化的
input_datetime:接受以 me 实例格式化的
output_datetime:显示以 me 实例格式化的
input_number:接受以 Format 格式化的数字数据类型( 或基本类型)
output_number:接受以 Format 格式化的数字数据类型( 或基本类型)
input_text:接受单行文本字符串。
output_text:显示单行文本字符串。
input_time:接受以 rmat 时间实例格式化的
output_time:显示以 rmat 时间实例格式化的
input_hidden:允许页面作者在页面中包括隐藏变量
input_secret:接受不含空格的单行文本,并在输入时,将其显示为一组星号
input_textarea:接受多行文本 output_errors:显示整个页面的错误消息或与指定的客户端标识相关联的错误消息 output_label:将嵌套的组件作为指定输入字段的标签显示
output_message:显示本地化消息
<%@ taglib uri="/jsf/html" prefix="h" %>
<%@ taglib uri="/jsf/core" prefix="f" %>
...
class="an" scope="session" /> 18 引用 Java API 1.3 - /j2se/1.3/docs/api/ Java API 1.4 - /j2se/1.4/docs/api/ Java Servlet API 2.3 - /products/servlet/2.3/javadoc/ Java 正则表达式程序包 - /regexp/ Jakarta 验证器 - /commons/validator/ JavaServer Faces 技术 - /j2ee/javaserverfaces/** 错误处理: 许多 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 = "ed"; 19 public static final String ERROR_USERNAME_ALPHANUMERIC = "umeric"; public static final String ERROR_USERNAME_DUPLICATE = "ate"; public static final String ERROR_USERNAME_INVALID = "d"; ... } - 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) { = key; = values; } // Returns the error key public String getKey() { return ; } // Returns the placeholder values public Object[] getValues() { return ; } private String key = null; private Object[] values = null; } - Errors:封装错误的集合 20 // 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)(property); if (propertyErrors == null) { propertyErrors = new ArrayList(); (property, propertyErrors); } (error); } // Returns true if there are any errors public boolean hasErrors() { return ( > 0); } // Returns the Errors for the specified property public ArrayList getErrors(String property) { return (ArrayList)(property); } private HashMap errors = new HashMap(); } 以下是使用上述框架类来处理“user_name”字段验证错误的示例: // Example to process validation errors of the "user_name" field. Errors errors = new Errors(); String userName = ameter("user_name"); // (a) Required validation rule if (!teRequired(userName)) { or("user_name", new Error(_USERNAME_REQUIRED)); } // (b) Alpha-numeric validation rule else if (!attern(userName, "^[a-zA-Z0-9]*$")) { or("user_name", new Error(_USERNAME_ALPHANUMERIC)); } 21 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 (fDuplicate(userName)) { or("user_name", new Error(_USERNAME_DUPLICATE)); } } catch (RemoteException e) { // log the error ("Could not validate user for specified userName: " + userName); or("user_name", new Error(_USERNAME_DUPLICATE); } } // set the errors object in a request attribute called "errors" ribute("errors", errors); ... [2] 报告错误 有两种方法可报告 web 层应用程序错误: (a) Servlet 错误机制 (b) JSP 错误机制 [2-a] Servlet 错误机制 Servlet 可通过以下方式报告错误: - 转发给输入 JSP(已将错误存储在请求属性中),或 - 使用 HTTP 错误代码参数来调用 ror,或 - 抛出异常 好的做法是处理所有已知应用程序错误(如 [1] 部分所述),将这些错误存储在请求属性中,然后转发给输入 JSP。输入 JSP 应显示错误消息,并提示用户重新输入数据。以下示例阐明转发给输入 JSP()的方式: // Example to forward to the following user validation errors RequestDispatcher rd = getServletContext().getRequestDispatcher("/user/"); if (rd != null) { 22 d(request, response); } 如果 Servlet 无法转发给已知的 JSP 页面,那么第二个选项是使用 ror 方法,将 _INTERNAL_SERVER_ERROR(状态码 500)作为参数,来报告错误。请参阅 rvletResponse 的 Javadoc,以获取有关各种 HTTP 状态码的更多详细信息。返回 HTTP 错误的示例: // Example to return a HTTP error code RequestDispatcher rd = getServletContext().getRequestDispatcher("/user/"); if (rd == null) { // messages is a resource bundle with all message keys and values ror(_INTERNAL_SERVER_ERROR, sage(_USERNAME_INVALID)); } 作为最后的手段,Servlet 可以抛出异常,且该异常必须是以下其中一类的子类: - RuntimeException - ServletException - IOException [2-b] JSP 错误机制 JSP 页面通过定义 errorPage 伪指令来提供机制,以处理运行时异常,如以下示例所示: <%@ page errorPage="/errors/" %> 未捕获的 JSP 异常被转发给指定的 errorPage,并且原始异常设置在名称为 eption 的请求参数中。错误页面必须包括 isErrorPage 伪指令,如下所示: <%@ page isErrorPage="true" %> isErrorPage 伪指令导致“exception”变量初始化为所抛出的异常对象。 [3] 呈现错误 J2SE Internationalization API 提供使应用程序资源外部化以及将消息格式化的实用程序类,其中包括: (a) 资源束 (b) 消息格式化 [3-a] 资源束 资源束通过将本地化数据从使用该数据的源代码中分离来支持国际化。每一资源束都会为特定的语言环境存储键/值对的映射。 tyResourceBundle 将内容存储在外部属性文件中,对其进行使用或扩展都很常见,如以下示例所示: ################################################ 23 # ties ################################################ # required user name error message ed=User name field is required # invalid user name format umeric=User name must be alphanumeric # duplicate user name error message ate=User name {0} already exists, please choose another one ... 可定义多种资源,以支持不同的语言环境(因此名为资源束)。例如,可定义 ErrorMessages_ties 以支持该束系列的法语成员。如果请求的语言环境的资源成员不存在,那么会使用缺省成员。在以上示例中,缺省资源是 ties。应用程序(JSP 或 Servlet)会根据用户的语言环境从适当的资源检索内容。 [3-b] 消息格式化 J2SE 标准类 eFormat 提供使用替换占位符来创建消息的常规方法。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 = ameter("user_name"); Object[] args = new Object[1]; args[0] = userName; String message = (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); } 24 // 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 = dle("ErrorMessages", locale); // Get localized error message String errorMessage = ing(errorKey); if (args != null) { // Format the message using the specified placeholders args return (errorMessage, args); } else { return errorMessage; } } // default environment locale private Locale defaultLocale = aultLocale(); } ... // Get the user's locale Locale userLocale = ale(); // Check if there were any validation errors Errors errors = (Errors)ribute("errors"); if (errors != null && ors()) { // iterate through errors and output error messages corresponding to the "user_name" property ArrayList userNameErrors = ors("user_name"); ListIterator iterator = or(); while (t()) { // Get the next error object Error error = (Error)(); String errorMessage = orMessage((), userLocale); (errorMessage + "rn"); } } 建议定义定制 JSP 标记(如 displayErrors),以迭代处理并呈现错误消息,如以上示例所示。 [4] 错误映射 通常情况下,“Servlet 容器”会返回与响应状态码或异常相对应的缺省错误页面。可以使 25 用定制错误页面来指定状态码或异常与 Web 资源之间的映射。好的做法是开发不会泄露内部错误状态的静态错误页面(缺省情况下,大部分 Servlet 容器都会报告内部错误消息)。该映射配置在“Web 部署描述符()”中,如以下示例所指定: ... ... 推荐使用的 JAVA 工具用于服务器端验证的两个主要 Java 框架是: [1] Jakarta Commons Validator(与 Struts 1.1 集成)Jakarta Commons Validator是 Java 框架,定义如上所述的错误处理机制。验证规则配置在 XML 文件中,该文件定义了表单字段的输入验证规则以及对应的验证错误密钥。Struts 提供国际化支持以使用资源束和消息格式化来构建本地化应用程序。 ... classname="hecks" method="validateRequired" msg="ed"> classname="hecks" method="validateMask" msg="d"> ...
26
...
...
Struts JSP 标记库定义了有条件地显示一组累计错误消息的“errors”标记,如下列示例所示:
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/" prefix="html" %>
<%@ taglib uri="/WEB-INF/" prefix="bean" %>
|
|
---|---|
|
|
27
[2] JavaServer Faces 技术
“JavaServer Faces 技术”是一组代表 UI 组件、管理组件状态、处理事件、验证输入和支持国际化的 Java API(JSR 127)。
JavaServer Faces API 定义“output_errors”UIOutput 处理器,该处理器显示整个页面的错误消息,或与指定的客户端标识相关联的错误消息。
<%@ taglib uri="/jsf/html" prefix="h" %>
<%@ taglib uri="/jsf/core" prefix="f" %>
...
class="an" scope="session" /> 二、会话标识未更新 2.1概况及技术描述 严重性:高 类 型:应用程序级别测试 WASC威胁分类:授权类型:会话定置 CVE引用:不适用 安全风险:可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务。 可能原因: Web 应用程序编程或配置不安全 28 技术描述: 根据 WASC: “会话固定”是一种攻击技术,会强制用户的会话标识变成显式值。 固定会话标识值的技术有许多种,会随着目标 Web 站点的功能而不同。从利用“跨站点脚本编制”到向 Web 站点密集发出先前生成的 HTTP 请求,都在这些技术范围内。用户的会话标识固定之后,攻击者会等待用户登录,然后利用预定义的会话标识值来假定用户的联机身份。 一般而言,对于标识值的会话管理系统有两种类型。第一种类型是“宽容”系统,可让 Web 浏览器指定任何标识。第二种类型是“严格”系统,只接受服务器端生成的值。当使用宽容系统时,不需要联系 Web 站点,便可以维护任何会话标识。在严格系统中,攻击者需要维护“陷阱会话”并且必须定期联系 Web 站点,才能防止闲置超时。 对于会话固定,倘若没有活动保护,使用会话来识别已认证的用户的任何 Web 站点都可能受到攻击。使用会话标识的 Web 站点通常都是基于 cookie 的站点,但也会使用 URL 和隐藏的表单字段。不幸的是,基于 cookie 的会话最容易受到攻击。 目前已识别的大多数攻击方法都是针对 cookie 的固定。 相对于在用户登录 Web 站点之后,再窃取用户的会话标识,会话固定提供的机会多得多。 在用户登录之前,攻击的活动部分便已启动。 会话固定攻击过程通常由三个步骤组成: 1) 安装会话 攻击者针对目标 Web 站点设下“陷阱会话”,并获取这个会话的标识,攻击者也可以选择攻击中所用的任意会话标识。在某些情况下,必须反复联系 Web 站点,才能维护确定好的陷阱会话值。 2) 固定会话 攻击者将陷阱会话值引进用户的浏览器中,固定用户的会话标识。 3) 进入会话 用户登录目标 Web 站点之后,当使用固定会话标识值时,攻击者便可加以接管。” ---------------------------------------------- 如果会话管理系统接受 URL 参数形式的会话标识,下列请求便可以强迫会话标识采用 URL 参数值。 代码片段: example/?PHPSESSID=1234 根据 WASC: “利用客户端脚本发出新的会话标识 cookie 值 ------------------------------------------------------------------------------------------- 域中任何 Web 站点的“跨站点脚本编制”漏洞都可用来修改当前 cookie 值。 代码片段: 29 example/ 另一个类似的示例(使用 META 标记注入): example/ 利用 HTTP 响应头发出 cookie ----------------------------------------------------------------------- 攻击者强迫目标 Web 站点或域中的任何其他站点发出会话标识 cookie。许多方法都可以做到这一点: - 闯进域中的某 Web 服务器(例如:维护不良的 WAP 服务器)- 毒害某用户的 DNS 服务器,实质将攻击者的 Web 服务器添加到域中 - 在域内安装恶意的 Web 服务器(例如:在 Windows 2000 域的工作站上,所有工作站也都在该 DNS 域中) - 利用 HTTP 响应分割攻击”---------------------------------------------- 登录过程前后会话标识的比较,显示它们并未更新,这表示有可能伪装用户。初步得知会话标识值后,远程攻击者有可能得以充当已登录的合法用户。 利用“跨站点脚本编制”漏洞可以获取会话标识值,导致受害者的浏览器在联系易受攻击的站点时使用预定的会话标识;启用“固定会话”也可以获取会话标识值,导致站点在受害者的浏览器中显示预定的会话标识。 受影响产品: 该问题可能会影响各种类型的产品。 参考资料 1、“基于 Web 的应用程序中的会话定置漏洞”,作者:Mitja Kolsek - Acros Security 2、PHP 手册、会话处理功能、会话与安全性 2.2修订方法 一般 1、始终生成新的会话,供用户成功认证时登录。 2、防止用户操纵会话标识。 3、请勿接受用户浏览器登录时所提供的会话标识 30 三、潜在文件上载 3.1概况及技术描述 严重性:高 类 型:应用程序级别测试 WASC威胁分类:逻辑攻击类型:功能滥用 CVE引用:不适用 安全风险: 可能会在 Web 服务器上运行远程命令。这通常意味着完全破坏服务器及其内容 可能会在 Web 服务器上上载、修改或删除 Web 页面、脚本和文件 可能原因: Web 应用程序编程或配置不安全 技术描述: AppScan 在站点上检测到潜在的文件上载。 这个能力想要让用户将本地文件系统的文件复制到 Web 服务器,它提供一种将信息轻松自在添加到站点的方法。 不过,倘若未采取适当措施来确保这个能力不致滥用,攻击者可能会利用这个不安全的文件上载过程。站点有可能因为现有文件遭到覆盖而损坏,甚至有可能上载脚本档或二进制文件,再愚弄服务器运行它们,从而执行任意代码。 样本利用: 考虑以下表单: [原始的 HTML 表单]
...
...
[受操纵的 HTML 表单]
在上载过程完成之后,黑客可以浏览至 "",Web 服务器会运行这份脚本。
31 受影响产品
该问题可能会影响各种类型的产品。
3.2修复方法
一般
在文件上载过程中,限制用户能力和许可权:
[1] 确保上载脚本只能控制上载的文件名和位置。
[2] 不上载脚本文件,如 asp、aspx、php、jsp 等。只允许上载静态内容。
[3] 只允许上载预期的文件类型。例如,如果您预期纯文本文件,便只允许 .txt 扩展名。
[4] 验证上载的文件内容。如果您预期纯文本文件,请确保它不含二进制字符或动态脚本部分。
四、跨站攻击
4.1 概况及技术描述
严重性:中
类 型:应用程序级别测试
WASC威胁分类:授权类型:授权不充分
CVE引用:不适用
安全风险:
可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务
可能原因:
应用程序使用的认证方法不充分
技术描述:
“跨站点伪造请求 (CSRF)”攻击可让黑客以受害者的名义在易受攻击的站点上运行操作。当易受攻击的站点未适当验证请求来源时,便可能出现这个攻击。
这个漏洞的严重性取决于受影响的应用程序的功能,例如,对搜索页面的 CSRF 攻击,严重性低于对转帐页面或概要更新页面的 CSRF 攻击。
这项攻击的执行方式,是强迫受害者的浏览器向易受攻击的站点发出 HTTP 请求。如果用户目前已登录受害者站点,请求会自动使用用户的凭证(如会话 Cookie、用户的 IP 地址,以及其他浏览器认证方法)。攻击者利用这个方法来伪造受害者的身份,再代替他来提交操作。换句话来说,易受攻击的站点未采取适当措施来验证用户实际是否想执行特定操作。
强迫受害者发送非预期的请求,方法有许多种:
- 通过电子邮件向受害者发送易受攻击应用程序的恶意链接。 - 在黑客的 Web 页面上,放 32 置一个易受攻击的 Web 站点的热链接(如图像或帧)。 - 在公共论坛中,张贴易受攻击站点的链接。
- 利用站点(或另一个站点)的“跨站点脚本编制”或“链接注入”漏洞,将浏览器自动重定向到易受攻击的站点。
如果攻击者利用易受攻击的站点本身的“链接注入”漏洞,可以增加用户通过站点认证的可能性,进而增加攻击成功的可能性。
例如,攻击者可以利用上述任何选项来诱惑受害者查看含有下列条目的页面:
style='visibility:hidden'>
这会使受害者的浏览器自动请求 URL 及浏览器的当前凭证。
如果这个银行业站点易受到 CSRF 攻击,它会根据应用程序逻辑,从受害者的帐户中,将
1000 美元转账到 John 的银行帐户。
“跨站点伪造请求”攻击也称为 CSRF(发音为 C-Serf)、XSRF、“跨站点伪造引用”、“单键攻击”以及“会话骑乘”。
您可以利用下列方式来验证您的应用程序是否易受到 CSRF 攻击:
[1] 检查易受攻击的链接/请求是否未包括攻击者难以猜中的参数
[2] 检查易受攻击的链接/请求是否会执行只应自愿执行的操作
含有用户在不知不觉中提交的请求所能直接访问的敏感操作的应用程序,被视为很容易遭受
CSRF 攻击。CSRF 也可能出现在登录页面和注销页面上。由于攻击者可以伪造来自受害者的连续注销请求,因此 CSRF 可能导致服务拒绝。在登录页面上,CSRF 可以允许攻击者使用包含攻击者用户名和密码的伪造请求来将客户机登录到攻击者的账户中。登录 CSRF 攻击会带有严重的后果,这取决于其他站点行为。例如,如果站点保留了用户操作的历史记录(例如搜索历史记录),那么攻击者将能够在易受攻击的站点上查看受害者之前执行的操作。
受影响产品
该问题可能会影响各种类型的产品。
参考资料:
1、跨站点伪造请求/wiki/Cross-site_request_forgery
2、跨站点伪造请求培训/ibmdl/pub/software/dw/richmedia/rational/08/appscan_demos/csrf-cbt/#recorded_advisory
4.2修复方法
一般
如果要避免 CSRF 攻击,每个请求都应该包含唯一标识,它是攻击者所无法猜测的参数。
建议的选项之一是添加取自会话 cookie 的会话标识,使它成为一个参数。服务器必须检查这个参数是否符合会话 cookie,若不符合,便废弃请求。
攻击者无法猜测这个参数的原因是应用于 cookie 的“同源策略”,因此,攻击者无法伪造一个虚假的请求,让服务器误以为真。
33 攻击者难以猜测且无法访问的任何秘密(也就是无法从其他域访问),都可用来替换会话标识。
这可以防止攻击者设计看似有效的请求。
五、启用不安全的HTTP方法
5.1概况及技术描述
严重性:中
类 型:基础结构测试
WASC威胁分类:客户端攻击类型:内容电子欺骗
CVE引用:不适用
安全风险:
可能会在 Web 服务器上上载、修改或删除 Web 页面、脚本和文件
可能原因:
可能会在 Web 服务器上上载、修改或删除 Web 页面、脚本和文件
技术描述:
似乎 Web 服务器配置成允许下列其中一个(或多个)HTTP 方法(动词):
- DELETE
- SEARCH
- COPY
- MOVE
- PROPFIND
- PROPPATCH
- MKCOL
- LOCK
- UNLOCK
这些方法可能表示在服务器上启用了 WebDAV,可能允许未授权的用户对其进行利用。 受影响产品:
该问题可能会影响各种类型的产品
5.2修复方法
一般
如果服务器不需要支持 WebDAV,请务必禁用它,或禁止不必要的 HTTP 方法。
34
发布者:admin,转转请注明出处:http://www.yc00.com/web/1687691651a32094.html
评论列表(0条)