转自:https://my.oschina/u/2416209/blog/983585
很多企业对代码的安全性有着很高的要求,目前使用的较为多的扫描代码安全漏洞的工具为Fortify 5.1。以下为一些漏洞的解决方案,欢迎指正。
1.Password Management: Empty Password
1)简介:为密码变量指定空字符串绝非一个好方法。如果使用 empty password 成功通过其他系统的验证,那么相应帐户的安全性很可能会被减弱,原因是其接受了 empty password。如果在为变量指定一个合法的值之前,empty password 仅仅是一个占位符,那么它将给任何不熟悉代码的人造成困惑,而且还可能导致出现意外控制流路径方面的问题。
例 1: 以下代码尝试使用empty password 连接数据库。
...
DriverManager.getConnection(url, ""scott"", """");
...
如果例 1 中的代码成功执行,则表明数据库用户帐户“scott”配置了一个 empty password,攻击者可以轻松地猜测到这一点。更危险的是,程序一旦发布,更新帐户以使用非 empty password 时,需要对代码进行更改。
例 2: 以下代码可将密码变量初始化为空字符串,并尝试在存储的值中读取密码,且将其与用户提供的值进行比较。
...
String storedPassword = """";
String temp;
if ((temp = readPassword()) != null) {
storedPassword = temp;
}
if(storedPassword.equals(userPassword))
// Access protected resources
...
}
...
如果 readPassword() 因数据库错误或其他问题而未能取得存储的密码,攻击者只需向 userPassword 提供一个空字符串,就能轻松绕过密码检查。
在移动世界中,由于设备丢失的几率较高,因此密码管理是一个非常棘手的问题。
例 3:以下代码可将用户名和密码变量初始化为空字符串,如果服务器之前未拒绝这些变量当前提出的请求,代码就可从 Android WebView 存储读取凭证,并使用用户名和密码设置身份验证,从而查看受保护页面。
...
webview.setWebViewClient(new WebViewClient() {
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
String username = """";
String password = """";
if (handler.useHttpAuthUsernamePassword()) {
String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
username = credentials[0];
password = credentials[1];
}
handler.proceed(username, password);
}
});
...
与例 2 相似,如果 useHttpAuthUsernamePassword() 返回 false,攻击者就可以通过提供 empty password 查看受保护页面。
2)解决方案:由于该漏洞是根据password的关键字扫描出的结果,所以规避该类问题,最好的办法就是不使用password当变量。
修改前:
String password = "";
try {
password = AESUtils.encryptForJS(rawPassword);
} catch (Exception e1) {
logger.error(e1);
}
修改后:
String pwd= "";
try {
pwd= AESUtils.encryptForJS(rawPassword);
} catch (Exception e1) {
logger.error(e1);
}
2.Unreleased Resource: Sockets
1)简介:程序可能无法释放某个套接字。资源泄露至少有两种常见的原因:
- 错误状况及其他异常情况。
- 未明确程序的哪一部份负责释放资源。
大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。
例 1:下面的方法绝不会关闭它所打开的套接字。在繁忙的环境中,这会导致 JVM 用尽它所有的套接字。
private void echoSocket(String host, int port) throws UnknownHostException, SocketException, IOException
{
Socket sock = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
while ((String socketData = reader.readLine()) != null) { System.out.println(socketData); } }
例 2:正常情况下,以下修复代码会正常关闭套接字以及任何相关联的数据流。但如果在读取输入或将数据输出到屏幕时出现异常,则不会关闭套接字对象。如果这种情况经常出现,系统将会耗尽所有套接字,无法处理更多连接。
private void echoSocket(String host, int port) throws UnknownHostException, SocketException, IOException
{
Socket sock = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
while ((String socketData = reader.readLine()) != null) { System.out.println(socketData); } sock.close(); }
2)解决方案:释放资源。
修改前:
private ClientHandle getClientHandle() throws IOException,
InterruptedException {
Socket socket = null;
for (int retry = 4; retry-- != 0;)
try {
socket = new Socket(ip, port);
break;
} catch (Exception exception) {
Thread.sleep(10L);
}
if (socket == null) {
throw new InterruptedException("������æ!");
} else {
ClientHandle handle = new ClientHandle(socket);
return handle;
}
}
修改后:
private ClientHandle getClientHandle() throws IOException,
InterruptedException {
Socket socket = null;
for (int retry = 4; retry-- != 0;)
try {
socket = new Socket(ip, port);
break;
} catch (Exception exception) {
Thread.sleep(10L);
}
if (socket == null) {
throw new InterruptedException("������æ!");
} else {
ClientHandle handle = null;
try {
handle = new ClientHandle(socket);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(socket != null) {
socket.close();
}
}
return handle;
}
}
3.Null Dereference
1)简介:当违反程序员的一个或多个假设时,通常会出现 null 指针异常。如果程序明确将对象设置为 null,但稍后却间接引用该对象,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将变量初始化为 null。
大部分空指针问题只会引起一般的软件可靠性问题,但如果攻击者能够故意触发空指针间接引用,攻击者就有可能利用引发的异常绕过安全逻辑,或致使应用程序泄漏调试信息,这些信息对于规划随后的攻击十分有用。
示例:在下列代码中,程序员将变量 foo 明确设置为 null。稍后,程序员间接引用 foo,而未检查对象是否为 null 值。
Foo foo = null;
...
foo.setBar(val);
...
}
2)解决方案:使用对象前,需要进行判空。
修改前:
if (uploadFolder.startsWith(File.separator))
uploadFolder.replaceFirst(File.separator, "");
修改后:
if (uploadFolder!=null&&uploadFolder.startsWith(File.separator))
uploadFolder.replaceFirst(File.separator, "");
4.Insecure Randomness
1)简介:在对安全性要求较高的环境中,使用一个能产生可预测数值的函数作为随机数据源,会产生 Insecure Randomness 错误。
电脑是一种具有确定性的机器,因此不可能产生真正的随机性。伪随机数生成器 (PRNG) 近似于随机算法,始于一个能计算后续数值的种子。
PRNG 包括两种类型:统计学的 PRNG 和密码学的 PRNG。统计学的 PRNG 可提供有用的统计资料,但其输出结果很容易预测,因此数据流容易复制。若安全性取决于生成数值的不可预测性,则此类型不适用。密码学的 PRNG 通过可产生较难预测的输出结果来应对这一问题。为了使加密数值更为安全,必须使攻击者根本无法、或极不可能将它与真实的随机数加以区分。通常情况下,如果并未声明 PRNG 算法带有加密保护,那么它有可能就是一个统计学的 PRNG,不应在对安全性要求较高的环境中使用,其中随着它的使用可能会导致严重的漏洞(如易于猜测的密码、可预测的加密密钥、会话劫持攻击和 DNS 欺骗)。
示例: 下面的代码可利用统计学的 PRNG 为购买产品后仍在有效期内的收据创建一个 URL。
String GenerateReceiptURL(String baseUrl) {
Random ranGen = new Random();
ranGen.setSeed((new Date()).getTime());
return (baseUrl + ranGen.nextInt(400000000) + "".html"");
}
这段代码使用 Random.nextInt() 函数为它所生成的收据页面生成独特的标识符。因为 Random.nextInt() 是一个统计学的 PRNG,攻击者很容易猜到由它所生成的字符串。尽管收据系统的底层设计也存在错误,但如果使用了一个不生成可预测收据标识符的随机数生成器(如密码学的 PRNG),会更安全一些。
2)解决方案:修改获取随机数的方式。
修改前:
Random r = new Random();
i = r.nextInt(15);
修改后:
SecureRandom s = new SecureRandom();
i= s.nextInt(15);
5.XML External Entity Injection
1)简介:XML External Entities 攻击可利用能够在处理时动态构建文档的 XML 功能。XML 实体可动态包含来自给定资源的数据。外部实体允许 XML 文档包含来自外部 URI 的数据。除非另行配置,否则外部实体会迫使 XML 解析器访问由 URI 指定的资源,例如位于本地计算机或远程系统上的某个文件。这一行为会将应用程序暴露给 XML External Entity (XXE) 攻击,从而用于拒绝本地系统的服务,获取对本地计算机上文件未经授权的访问权限,扫描远程计算机,并拒绝远程系统的服务。
下面的 XML 文档介绍了 XXE 攻击的示例。
<?xml version=""1.0"" encoding=""ISO-8859-1""?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM ""file:///dev/random"" >]><foo>&xxe;</foo>
如果 XML 解析器尝试使用 /dev/random 文件中的内容来替代实体,则此示例会使服务器(使用 UNIX 系统)崩溃。
2)解决方案:应对 XML 解析器进行安全配置,使它不允许将外部实体包含在传入的 XML 文档中。
为了避免 XXE injections,应为 XML 代理、解析器或读取器设置下面的属性:
factory.setFeature(""http://xml/sax/features/external-general-entities"", false);
factory.setFeature(""http://xml/sax/features/external-parameter-entities"", false);
如果不需要 inline DOCTYPE 声明,可使用以下属性将其完全禁用:
factory.setFeature(""http://apache/xml/features/disallow-doctype-decl"", true);
要保护 TransformerFactory,应设置下列属性:
TransformerFactory transFact = TransformerFactory.newInstance();
transFact.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, """");
Transformer trans = transFact.newTransformer(xsltSource);
trans.transform(xmlSource, result);
修改前:
TransformerFactory tFac = TransformerFactory.newInstance();
Transformer t = tFac.newTransformer(xslSource);
修改后:
TransformerFactory tFac = TransformerFactory.newInstance();
tFac.setFeature("http://xml/sax/features/external-general-entities", false);
tFac.setFeature("http://xml/sax/features/external-parameter-entities", false);
Transformer t = tFac.newTransformer(xslSource);
6.Unreleased Resource: Streams
1)简介:"程序可能无法成功释放某一项系统资源。资源泄露至少有两种常见的原因:
- 错误状况及其他异常情况。
- 未明确程序的哪一部份负责释放资源。
大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。
示例:下面的方法绝不会关闭它所打开的文件句柄。FileInputStream 中的 finalize() 方法最终会调用 close(),但是不能确定何时会调用 finalize() 方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。
private void processFile(String fName) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(fName);
int sz;
byte[] byteArray = new byte[BLOCK_SIZE];
while ((sz = fis.read(byteArray)) != -1) {
processBytes(byteArray, sz);
}
}
2)解决方案:及时释放资源。
修改前:
OutputStream out = null;
try {
out = new FileOutputStream(outputZipFileName);
out.write(b);
} catch (Exception e) {
log.error(e);
}
修改后:
OutputStream out = null;
try {
out = new FileOutputStream(outputZipFileName);
out.write(b);
} catch (Exception e) {
log.error(e);
} finally {
if(out != null) {
//out.flush();
out.close();
}
}
8.Portability Flaw: Locale Dependent Comparison
1)简介:对可能与区域设置相关的数据进行比较时,应指定相应的区域设置。
示例 1:以下示例尝试执行验证,以确定用户输入是否包含 <script> 标签。
...
public String tagProcessor(String tag){
if (tag.toUpperCase().equals(""SCRIPT"")){
return null;
}
//does not contain SCRIPT tag, keep processing input
...
}
...
关于上述代码的问题是:在使用不带区域设置的 java.lang.String.toUpperCase() 时,其将使用默认的区域设置规则。使用土耳其区域设置 ""title"".toUpperCase() 时将返回 ""T\u0130TLE"",其中 ""\u0130"" 是 ""LATIN CAPITAL LETTER I WITH DOT ABOVE"" 字符。这会导致生成意外结果,例如,在示例 1 中,会导致此验证无法捕获 ""script"" 一词,从而可能造成跨站脚本攻击漏洞。
2)解决方案:确保指定默认区域设置,或者指定可以接受这些字符(如 toUpperCase())并带有 API 的区域设置。
修改前:
if("date".equals(paramtype.toLowerCase()))
修改后:
if("date".equals(paramtype.toLowerCase(Locale.CHINESE)))
9.Header Manipulation
1)简介:以下情况中会出现 Header Manipulation 漏洞:
1. 数据通过一个不可信赖的数据源进入 Web 应用程序,最常见的是 HTTP 请求。
2. 数据包含在一个 HTTP 响应头文件里,未经验证就发送给了 Web 用户。
如同许多软件安全漏洞一样,Header Manipulation 只是通向终端的一个途径,它本身并不是终端。从本质上看,这些漏洞是显而易见的:一个攻击者将恶意数据传送到易受攻击的应用程序,且该应用程序将数据包含在 HTTP 响应头文件中。
其中最常见的一种 Header Manipulation 攻击是 HTTP Response Splitting。为了成功地实施 HTTP Response Splitting 盗取,应用程序必须允许将那些包含 CR(回车,由 %0d 或 \r 指定)和 LF(换行,由 %0a 或 \n 指定)的字符输入到头文件中。攻击者利用这些字符不仅可以控制应用程序要发送的响应剩余头文件和正文,还可以创建完全受其控制的其他响应。
如今的许多现代应用程序服务器可以防止 HTTP 头文件感染恶意字符。例如,如果尝试使用被禁用的字符设置头文件,最新版本的 Apache Tomcat 会抛IllegalArgumentException。如果您的应用程序服务器能够防止设置带有换行符的头文件,则其具备对 HTTP Response Splitting 的防御能力。然而,单纯地过滤换行符可能无法保证应用程序不受 Cookie Manipulation 或 Open Redirects 的攻击,因此必须在设置带有用户输入的 HTTP 头文件时采取措施。
示例:下列代码片段会从 HTTP 请求中读取网络日志项的作者名字 author,并将其置于一个 HTTP 响应的 cookie 头文件中。
String author = request.getParameter(AUTHOR_PARAM);
...
Cookie cookie = new Cookie(""author"", author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);
假设在请求中提交了一个字符串,该字符串由标准的字母数字字符组成,如“Jane Smith”,那么包含该 cookie 的 HTTP 响应可能表现为以下形式:
HTTP/1.1 200 OK
...
Set-Cookie: author=Jane Smith
...
然而,因为 cookie 值来源于未经校验的用户输入,所以仅当提交给 AUTHOR_PARAM 的值不包含任何 CR 和 LF 字符时,响应才会保留这种形式。如果攻击者提交的是一个恶意字符串,比如“Wiley Hacker\r\nHTTP/1.1 200 OK\r\n...”,那么 HTTP 响应就会被分割成以下形式的两个响应:
HTTP/1.1 200 OK
...
Set-Cookie: author=Wiley Hacker
HTTP/1.1 200 OK
...
显然,第二个响应已完全由攻击者控制,攻击者可以用所需的头文件和正文内容构建该响应。攻击者可以构建任意 HTTP 响应,从而发起多种形式的攻击,包括:cross-user defacement、网络和浏览器缓存中毒、cross-site scripting 和 page hijacking。
用户信息涂改:攻击者可以向一个易受攻击的服务器发出一个请求,导致服务器创建两个响应,其中第二个响应可能会被曲解为对其他请求的响应,而这一请求很可能是与服务器共享相同 TCP 连接的另一用户发出的。这种攻击可以通过以下方式实现:攻击者诱骗用户,让他们自己提交恶意请求;或在远程情况下,攻击者与用户共享同一个连接到服务器(如共享代理服务器)的 TCP 连接。最理想的情况是,攻击者只能通过这种做法让用户相信自己的应用程序已经遭受了黑客攻击,进而对应用程序的安全性失去信心。最糟糕的情况是,攻击者可能提供经特殊技术处理的内容,这些内容旨在模仿应用程序的执行方式,但会重定向用户的私人信息(如帐号和密码),将这些信息发送给攻击者。
缓存中毒: 如果多用户 Web 缓存或者单用户浏览器缓存将恶意构建的响应缓存起来,该响应的破坏力会更大。如果响应缓存在共享的 Web 缓存(如在代理服务器中常见的缓存)中,那么使用该缓存的所有用户都会不断收到恶意内容,直到清除该缓存项为止。同样,如果响应缓存在单个用户的浏览器中,那么在清除该缓存项以前,该用户会不断收到恶意内容。然而,影响仅局限于本地浏览器的用户。
跨站脚本攻击:一旦攻击者控制了应用程序传送的响应,就可以选择多种恶意内容来传播给用户。Cross-Site Scripting 是最常见的攻击形式,这种攻击在响应中包含了恶意的 JavaScript 或其他代码,并在用户的浏览器中执行。基于 XSS 的攻击手段花样百出,几乎是无穷无尽的,但通常它们都会包含传输给攻击者的私人数据(如 Cookie 或者其他会话信息)。在攻击者的控制下,指引受害者进入恶意的网络内容;或者利用易受攻击的站点,对用户的机器进行其他恶意操作。对于易受攻击的应用程序用户,最常见且最危险的攻击就是使用 JavaScript 将会话和 authentication 信息返回给攻击者,而后攻击者就可以完全控制受害者的帐号了。
网页劫持:除了利用一个易受攻击的应用程序向用户传输恶意内容,还可以利用相同的根漏洞,将服务器生成的供用户使用的敏感内容重定向,转而供攻击者使用。攻击者通过提交一个会导致两个响应的请求,即服务器做出的预期响应和攻击者创建的响应,致使某个中间节点(如共享的代理服务器)误导服务器所生成的响应,将本来应传送给用户的响应错误地传给攻击者。因为攻击者创建的请求产生了两个响应,第一个被解析为针对攻击者请求做出的响应,第二个则被忽略。当用户通过同一 TCP 连接发出合法请求时,攻击者的请求已经在此处等候,并被解析为针对受害者这一请求的响应。这时,攻击者将第二个请求发送给服务器,代理服务器利用针对受害者(用户)的、由该服务器产生的这一请求对服务器做出响应,因此,针对受害者的这一响应中会包含所有头文件或正文中的敏感信息。
Cookie Manipulation:当与类似 Cross-Site Request Forgery 的攻击相结合时,攻击者就可以篡改、添加、甚至覆盖合法用户的 cookie。
打开重定向:如果允许未验证的输入来控制重定向机制所使用的 URL,可能会有利于攻击者发动钓鱼攻击。
2)解决方案:过滤掉特殊字符。
注意事项:将文件名转为utf-8格式后,含有特殊字符“%”。所以需将文件名转为ISO-8859-1格式后替换特殊字符串,谷歌和火狐浏览器可采用下列方式修改,IE待验证。
修改前:
filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
response.setHeader("Content-Disposition", "attachment; filename="+filename);
修改后:
filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
String regex = "[`~!@#$%^&*()\\+\\=\\{}|:\"?><【】\\/r\\/n]";
Pattern pa = Patternpile(regex);
Matcher ma = pa.matcher(filename);
if(ma.find()){
filename = ma.replaceAll("");
}
response.setHeader("Content-Disposition", "attachment; filename="+filename);
10.Code Correctness: Double-Checked Locking
1)简介:许多才智卓越的人都试图使用 double-checked locking 方法来提高性能,并为此付出了大量的时间,但是无一成功。
例 1:乍一看,下列代码似乎既能避免不必要的同步又能保证线程的安全性。
if (fitz == null) {
synchronized (this) {
if (fitz == null) {
fitz = new Fitzer();
}
}
}
return fitz;
程序员希望保证仅分配一个 Fitzer() 对象,但又不希望每次调用该代码时都进行一次同步。这就是所谓的 double-checked locking 方法。
令人遗憾的是,它并不起作用,并且可以分配多个 Fitzer() 对象。
2)解决方案:其实同步所花费的代价比想象中的要少。许多情况下,最好的方法就是采用最简单的解决方法。
修改前:
if(cfg == null){
synchronized (ConfigUtil.class) {
if (cfg == null){
cfg = new ConfigUtil();
}
}
}
return cfg;
修改后:
synchronized (ConfigUtil.class) {
if (cfg == null){
cfg = new ConfigUtil();
}
return cfg;
}
11.Poor Error Handling: Return Inside Finally
1)简介:finally 块中的返回指令会导致从 try 块中抛出的异常丢失。
例 1:在下列代码中,第二次调用 doMagic 方法,同时将参数 true 传递给该方法会导致抛出 MagicException 异常,该异常将不会传递给调用者。finally 块中的返回指令会导致异常的丢弃。
public class MagicTrick {
public static class MagicException extends Exception { }
public static void main(String[] args) {
System.out.println(""Watch as this magical code makes an "" +
""exception disappear before your very eyes!"");
System.out.println(""First, the kind of exception handling "" +
""you're used to:"");
try {
doMagic(false);
} catch (MagicException e) {
// An exception will be caught here
e.printStackTrace();
}
System.out.println(""Now, the magic:"");
try {
doMagic(true);
} catch (MagicException e) {
// No exception caught here, the finally block ate it
e.printStackTrace();
}
System.out.println(""tada!"");
}
public static void doMagic(boolean returnFromFinally)
throws MagicException {
try {
throw new MagicException();
}
finally {
if (returnFromFinally) {
return;
}
}
}
}
2)解决方案:将返回指令移到 finally 块之外。如果必须要 finally 块返回一个值,可以简单地将该返回值赋给一个本地变量,然后在 finally 块执行完毕后返回该变量。
修改前:
int iRun=1;
try{
dba.runSql(fullSql,new Object[]{LoginIp,LoginUser,LoginUserName,Log});
}catch (SQLException sqlexception) {
iRun=0;
}finally {
return iRun;
}
修改后:
int iRun=1;
int iRun1 = 0;
try{
dba.runSql(fullSql,new Object[]{LoginIp,LoginUser,LoginUserName,Log});
}catch (SQLException sqlexception) {
iRun=0;
}finally {
iRun1 = iRun;
}
return iRun1;
12.Privacy Violation: Autocomplete
1)简介:启用自动完成功能后,某些浏览器会保留会话中的用户输入,以便随后使用该计算机的用户查看之前提交的信息。
2)解决方案:对于表单或敏感输入,显式禁用自动完成功能。通过禁用自动完成功能,之前输入的信息不会在用户输入时以明文形式显示。这也会禁用大多数主要浏览器的“记住密码”功能。
例 1:在 HTML 表单中,通过在 form 标签上将 autocomplete 属性的值显式设置为 off,禁用所有输入字段的自动完成功能。
<form method=""post"" autocomplete=""off"">
Address: <input name=""address"" />
Password: <input name=""password"" type=""password"" />
</form>
例 2:或者,通过在相应的标签上将 autocomplete 属性的值显式设置为 off,禁用特定输入字段的自动完成功能。
<form method=""post"">
Address: <input name=""address"" />
Password: <input name=""password"" type=""password"" autocomplete=""off""/>
</form>
请注意,autocomplete 属性的默认值为 on。因此,处理敏感输入时请不要忽略该属性。
修改前:
<div class="ui-form-item loginPassword">
<input id="password" type="password" placeholder="请输入密码">
</div>
修改后:
<div class="ui-form-item loginPassword">
<input id="password" type="password" placeholder="请输入密码"autocomplete="off" >
</div>
13.Header Manipulation: Cookies
1)简介:该类问题其实属于Header Manipulation其中的一种。只是将验证的对象换成
Cookie cok = new Cookie(ckName, null)中的ckName。
2)解决方案:将对象中的特殊字符去除。
修改前:
Cookie cok = new Cookie(ck.getName(), null);
cok.setMaxAge(0);
response.addCookie(cok);
修改后:
String regex = "[`~!@#$%^&*()\\+\\=\\{}|:\"?><【】\\/r\\/n]";
Pattern pa = Patternpile(regex);
Cookie[] cks = request.getCookies();
String ckName = ck.getName();
Matcher ma = pa.matcher(ckName);
if(ma.find()){
ckName = ma.replaceAll("");
}
Cookie cok = new Cookie(ckName, null);
cok.setMaxAge(0);
response.addCookie(cok);
}
更多推荐
Fortify 5.1漏洞整改方案
发布评论