本篇介绍了以下代码安全问题
说明:本篇所有介绍的安全问题在审计时通常为中危及以上
1、跨站请求伪造
2、弱验证
3、组件间通信XSS
4、有风险的反射型XSS
5、有风险的存储型XSS
6、不安全的随机数
7、空密码
8、硬编码密码
9、弱加密
10、弱加密:密钥长度不足
1、跨站请求伪造
详细信息
跨站请求伪造(CSRF)是伪造客户端请求的一种攻击,攻击者可以在用户不知情的情况下代表经过身份验证的用户执行操作。
例如:以下代码片段实现远程服务,有可能遭受跨站请求伪造攻击。
public interface MyService extends RemoteService {
public String myMethod(String s);
}
public class MyServiceImpl extends MyService {
public String myMethod(String s){
...
}
}
修复建议
GWT中包含默认的CSRF保护,通过生成会话cookie值的MD5散列值并将使用生成的散列值作为XSRF token,从会话身份验证cookie派生出XSRF token。
例如:以下代码片段添加@XsrfProtect来意味着将对该接口的所有方法执行CSRF token验证。
@XsrfProtect
public interface MyService extends RemoteService {
public String myMethod(String s);
}
public class MyServiceImpl extends MyService {
public String myMethod(String s){
...
}
}
这样,服务器端程序响应用户请求前先验证token,判断请求的合法性。而对于token,越难被猜出攻击者攻击成功的概率就越低。
2、弱验证
详细信息
依靠HTML、XML或其他类型编码验证用户输入可能导致浏览器执行恶意代码。
在以下情况下会出现XSS漏洞:
1.数据通过一个不可信赖的数据源进入Web应用程序。在组件间通信XSS的情况下,不可信赖的数据是从驻留在其他组件所接收的数据。在Android应用中,指运行在同一设备中的应用程序。对于Reflected XSS,不可信赖的源通常为Web请求,而对于Persisted(或Stored) XSS,该源通常为数据库或者其他后端存储数据。
2.未检验包含在动态内容中的数据,便将其传送给了Web用户。
传送到Web浏览器的恶意内容通常采用JavaScript代码片段的形式,但也可能会包含一些HTML、Flash或者其他任意一种可以被浏览器执行的代码,基于XSS的攻击手段多样,但通常它们都会包含传输给攻击者的私人数据(如Cookie或者其他会话信息)。在攻击者的控制下,指引被攻击者进入二义的网络内容,或者利用易受攻击的站点,对用户的机器进行其他恶意操作。
例1:下面的JSP代码片段可从HTTP请求中读取人员的ID eid,并通过标签将其显示给用户。
标签设置escapeXml="true"时,可以避免一部分跨站脚本攻击。但是如果eid里有包含恶意代码,那么Web浏览器就会像显示HTTP响应那样执行该代码,应用程序将受到XSS攻击。
例2:以下代码在Android WebView中启用了JavaScript(默认情况下,JavaScript为禁用状态),并根据从Android Intent接收到的值加载页面。
...
WebView webview = (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true);
String url = this.getIntent().getExtras().getString(""url"");
webview.loadUrl(url);
...
如果url的值以“javascript:”开头,则接下来的JavaScript代码将在WebView中的Web页面上下文内部执行。如例1所示,如果应用程序之外的数据源将危险数据存储在一个数据库或其他数据存储器中,随后这些危险数据可能被当作可信赖的数据回写到应用程序中,并存储在动态内容中。
修复建议
为了避免XSS攻击,建议采用以下方式进行防御:
(1)对用户的输入进行合理验证(如年龄只能是数字),对特殊字符(如、'、"以及
(2)根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。
例如:采用OWASP ESAPI对数据输出HTML上下文中不同位置,编码方法如下。
//HTML encode
ESAPI.encoder().encodeForHTML("inputData");
//HTML attribute encode
ESAPI.encoder().encodeForHTMLAttribute("inputData ");
//JavaScript encode
ESAPI.encoder().encodeForJavaScript("inputData ");
//CSS encode
ESAPI.encoder().encodeForCSS(""inputData ");
//URL encode
ESAPI.encoder().encodeForURL("inputData ");
(3)设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:
...
response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max- age=seconds; HttpOnly");
...
3、组件间通信XSS
详细信息
在以下情况中向一个Web浏览器发送恶意构造的代码会导致该浏览器执行恶意代码,也就是跨站脚本漏洞:
(1)数据通过不可信赖的数据源进入Web应用程序。组件间通信的XSS一般情况下不可信赖的数据源指的是从驻留在其他组件所接收的数据。例如Android应用中,指从运行在同一设备中的应用程序获取的数据。对于反射型的XSS,不可信赖的源通常为Web请求,而对于存储型的XSS,该源通常为数据库或者其他后端存储数据。
(2)未检验包含在动态内容中的数据,便将其传送给了Web用户。
例1:以下代码在Android WebView中启用了JavaScript(默认情况下,JavaScript为禁用状态),并根据从Android Intent接收到的值加载页面。
...
WebView view = (WebView) findViewById(R.id.view);
webview.getSettings().setJavaScriptEnabled(true);
String url = this.getIntent().getExtras().getString("url");
webview.loadUrl(url);
...
如果url的值以“javascript:”开头,则接下来的JavaScript代码将在WebView中的Web页面上下文内部执行,例如”javascript:alert(/xss/)”。如果应用程序之外的数据源将危险数据存储在一个数据库或其他数据存储器中,随后这些危险数据可能被当作可信赖的数据回写到应用程序中,并存储在动态内容中。
例2:下面JSP代码片段的功能是从HTTP请求中读取用户的ID(uid),并将其显示给用户。
<% String uid = request.getParameter("uid"); %>
...
User ID: <%= uid %>
如果uid里有包含恶意代码,那么Web浏览器就会像显示HTTP响应那样执行该代码,应用程序将受到反射型XSS攻击。
例3:下面JSP代码片段的功能是根据一个已知用户ID(uid)从数据库中查询出该雇员的姓名,并显示在JSP页面上。
<%...
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from user where id="+uid);
if (rs != null) {
rs.next();
String name = rs.getString("name");
%>
User Name: <%= name %>
如果name的值是由用户提供的,且存入数据库时没有进行合理的校验,那么攻击者就可以利用上面的代码进行存储型XSS攻击。
修复建议
为了避免跨站脚本攻击,建议采用以下方式进行防御:
(1)对用户的输入进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及
(2)根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。
例如:采用OWASP ESAPI对数据输出HTML上下文中不同位置,编码方法如下。
//HTML encode
ESAPI.encoder().encodeForHTML("inputData");
//HTML attribute encode
ESAPI.encoder().encodeForHTMLAttribute("inputData ");
//JavaScript encode
ESAPI.encoder().encodeForJavaScript("inputData ");
//CSS encode
ESAPI.encoder().encodeForCSS(""inputData ");
//URL encode
ESAPI.encoder().encodeForURL("inputData ");
(3)设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:
...
response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max- age=seconds; HttpOnly");
...
4、有风险的反射型XSS
详细信息
应用程序通过Web请求获取不可信赖的数据,在未检验数据是否存在恶意代码的情况下,便将其传送给了Web用户,应用程序将易于受到反射型XSS攻击。
例如:下面JSP代码片段的功能是从HTTP请求中读取雇员的ID(eid),并将其显示给用户。
<% String name= request.getParameter("username"); %>
...
姓名: <%= name%>
如果name里有包含恶意代码,那么Web浏览器就会像显示HTTP响应那样执行该代码,应用程序将受到反射型XSS攻击。
修复建议
为了避免反射型XSS攻击,建议采用以下方式进行防御:
(1)对用户的输入进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及
(2)根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。
例如:采用OWASP ESAPI对数据输出HTML上下文中不同位置,编码方法如下。
//HTML encode
ESAPI.encoder().encodeForHTML("inputData");
//HTML attribute encode
ESAPI.encoder().encodeForHTMLAttribute("inputData");
//JavaScript encode
ESAPI.encoder().encodeForJavaScript("inputData");
//CSS encode
ESAPI.encoder().encodeForCSS("inputData");
//URL encode
ESAPI.encoder().encodeForURL("inputData");
(3)设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:
...
response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max- age=seconds; HttpOnly");
...
5、有风险的存储型XSS
详细信息
应用程序从数据库或其它后端数据存储获取不可信赖的数据,在未检验数据是否存在恶意代码的情况下,便将其传送给了Web用户,应用程序将易于受到存储型XSS攻击。
例如:下面JSP代码片段的功能是根据一个已知雇员ID(eid)从数据库中查询出该雇员的姓名,并显示在JSP页面上。
<% ...
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from users where id =" + id);
String address = null;
if (rs != null) {
rs.next();
address = rs.getString("address");
}
%>
家庭地址: <%= address %>
如果name的值是由用户提供的,且存入数据库时没有进行合理的校验,那么攻击者就可以利用上面的代码进行存储型XSS攻击。
修复建议
为了避免存储型XSS攻击,建议采用以下方式进行防御:
(1)对从数据库或其它后端数据存储获取不可信赖的数据进行合理验证(如年龄只能是数字),对特殊字符(如<、>、'、"以及
(2)根据数据将要置于HTML上下文中的不同位置(HTML标签、HTML属性、JavaScript脚本、CSS、URL),对所有不可信数据进行恰当的输出编码。
例如:采用OWASP ESAPI对数据输出HTML上下文中不同位置,编码方法如下。
//HTML encode
ESAPI.encoder().encodeForHTML(inputData);
//HTML attribute encode
ESAPI.encoder().encodeForHTMLAttribute(inputData);
//JavaScript encode
ESAPI.encoder().encodeForJavaScript(inputData);
//CSS encode
ESAPI.encoder().encodeForCSS(inputData);
//URL encode
ESAPI.encoder().encodeForURL(inputData);
(3)设置HttpOnly属性,避免攻击者利用跨站脚本漏洞进行Cookie劫持攻击。在Java EE中,给Cookie添加HttpOnly的代码如下:
...
response.setHeader("Set-Cookie","cookiename=cookievalue; path=/; Domain=domainvaule; Max- age=seconds; HttpOnly");
...
6、不安全的随机数
详细信息
Java API中提供了java.util.Random类实现PRNG(),该PRNG是可移植和可重复的,如果两个java.util.Random类的实例使用相同的种子,会在所有Java实现中生成相同的数值序列。
例如:下面代码片段中,使用了java.util.Random类,该类对每一个指定的种子值生成同一个序列。
import java.util.Random;
// ...
public static void main (String args[]) {
// ...
for (int i = 0; i < 10; i++) {
Random random = new Random(123456);
int number = random.nextInt(21);
...
}
}
修复建议
在安全性要求较高的应用中,应使用更安全的随机数生成器,如java.security.SecureRandom类。
例如:下面代码片段中,使用java.security.SecureRandom来生成更安全的随机数。
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
// ...
public static void main (String args[]) {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
for (int i = 0; i < 10; i++) {
int number = random.nextInt(21);
...
}
} catch (NoSuchAlgorithmException nsae) {
...
}
}
7、空密码
详细信息
程序中使用了空的密码值,系统安全性将会受到威胁。
修复建议
程序中所需密码应从配置文件中获取经过加密的密码值。
8、硬编码密码
详细信息
程序中采用硬编码方式处理密码,一方面会降低系统安全性,另一方面不易于程序维护。
例如:下列代码中采用硬编码方式处理密码。
public class ConnectionConfig{
String url = "localhost";
String name = "admin";
String password = "123456";
...
}
修复建议
程序中所需密码应从配置文件中获取经过加密的密码值。
9、弱加密
详细信息
在安全性要求较高的系统中,使用不安全的加密算法(如DES、3DES、RC4、RC5等),将无法保证敏感数据的保密性。
例如:下面代码片段中,采用DES对数据进行加密。
BufferedReader bufread2 = null;
InputStreamReader inread2 = null;
try {
inread2 = new InputStreamReader(System.in);
bufread2 = new BufferedReader(inread2);
String str = bufread2.readLine();
/* FLAW: Insecure cryptographic algorithm (DES) */
Cipher des = Cipher.getInstance("DES");
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
des.init(Cipher.ENCRYPT_MODE, key);
byte[] enc_str = des.doFinal(str.getBytes());
IO.writeLine(IO.toHex(enc_str));
} catch(IOException e) {
log_bsnk.warning("Error reading from console");
} finally{
...
}
...
修复建议
在安全性要求较高的系统中,建议应使用安全的加密算法(如AES、RSA)对敏感数据进行加密。
例如:下面代码片段中,使用AES取代DES保证数据完整性。
BufferedReader bufread2 = null;
InputStreamReader inread2 = null;
try {
inread2 = new InputStreamReader(System.in);
bufread2 = new BufferedReader(inread2);
String str = bufread2.readLine();
/* FIX: Secure cryptographic algorithm (AES) */
Cipher aes = Cipher.getInstance("AES");
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey key = kg.generateKey();
aes.init(Cipher.ENCRYPT_MODE, key);
byte[] enc_str = aes.doFinal(str.getBytes());
IO.writeLine(IO.toHex(enc_str));
} catch(IOException e) {
log_gsnk.warning("Error reading from console");
} finally{
...
}
...
10、弱加密:密钥长度不足
详细信息
加密算法中使用的密钥长度较短,会降低系统安全。
例如:下面代码片段中,KeyPairGenerator使用RSA加密算法,长度为1024位。
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
byte[] publicKeyData = publicKey.getEncoded();
byte[] privateKeyData = privateKey.getEncoded();
...
修复建议
对于对称加密算法,建议使用长度大于或等于128位的密钥。对于非对称加密算法(如RSA),建议使用长度大于或等于2048位的密钥。