var url=document.URL;
document.write(url.substring(url.indexOf("msg=")+4,url.length);
该段脚本分析URL,读取msg参数的值,并将其写入页面。如果攻击者设计一个恶意的URL,并以JavaScript代码作为msg参数,那么Web浏览器就会像显示HTTP相应那样实行该代码,应用程序将受到基于DOM的XSS攻击。
**例1**:以下代码示例解压zip文件。
```java
...
File sourceFile = new File(sourceName);
ZipFile zipFile = null;
try {
zipFile = new ZipFile(sourceFile,"UTF-8");
} catch (IOException exception) {
exception.printStackTrace();
System.out.println("解压文件不存在!");
}
Enumeration e = zipFile.getEntries();
while(e.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry)e.nextElement();
System.out.println(`zipEntry.getName()`);
File f = new File(targetFile,`zipEntry.getName()`);
f.getParentFile().mkdirs();
f.createNewFile();
InputStream is = zipFile.getInputStream(zipEntry);
FileOutputStream fos = new FileOutputStream(f);
int length = 0;
byte[] b = new byte[1024];
while((length=is.read(b, 0, 1024))!=-1) {
fos.write(b, 0, length);
}
is.close();
fos.close();
}
if (zipFile != null) {
zipFile.close();
}
```
代码示例未验证`zipEntry.getName()`,如果zip文件放在`/tmp/`目次中,zip条目为`../etc/hosts`,且应用程序在须要的权限下运行,则会导致系统的hosts文件被覆盖。
**例2**:以下代码利用`org.zeroturnaround.zip.ZipUtil`解压zip文件。
```java
public void unZip(String zipPath,String targetPath) {
...
ZipUtil.unpack(new File(zipPath), new File(targetPath));
...
}
```
代码示例中如果利用了`zt-zip 1.13`之前版本。攻击者可借助带有目次遍历名称的zip文件利用该漏洞写入任意文件。
修复发起
防止ZIP条目覆盖导致路径遍历可以通过判定zipEntry路径是否在指定路径内,大概利用一些最新的解压jar包来解压文件。
**例1**:下面的validateFileDir函数来限制zip条目文件路径只在答应的目次内。
```java
//限制文件在答应的目次内
public static String validateFileDir(String fileName, String permitDirectory) throws IOException{
File file= new File(fileName);
String canonicalFilePath= checkFile.getCanonicalPath();
File permitDir = new File(permitDirectory);
String canonicalPermitDir = permitDir.getCanonicalPath();
if (canonicalFilePath.startsWith(canonicalPermitDir)){
return canonicalFilePath;
}else{
throw new IllegalStateException("文件不在答应的目次内");
}
}
```
可以解压zip文件过程中利用`validateFileDir`函数来限制zip条目文件只在答应目次内。
**例2**:利用当前zt-zip 1.13以及之后的版原来举行解压文件。
中危:拒绝服务:正则表达式
正则表达式引擎分成两类:一类称为DFA(确定性有限状态自动机),另一类称为NFA(非确定性有限状态自动机)。Java利用的是NFA正则引擎,利用正则式和文本比较,每遇到一个字符,就把它跟正则式比较,匹配就记下来,然后接着往下比较。一旦不匹配,就会后退直到回到上一次匹配的地方。而不可信赖数据被传递至应用程序并作为正则表达式利用,可能导致线程过分利用 CPU 资源,从而导致拒绝服务攻击。
**比方**:在以下代码片段中, Spring MVC可以将 HTTP哀求参数绑定到 User属性:
@RequestMapping("/login" )
public String login(User user) {
}
其中,User 类定义为:
public class User {
private String username;
private String address;
private int age;
private boolean admin;
}
当Spring MVC未设置为禁止绑定敏感属性,则攻击者可能会通过发送以下使普通用户变为管理员。
name=张三&address=北京&age=22&admin=true
修复发起:
当程序将非将HTTP哀求参数直接绑定给对象时,应该要控制绑定到对象的属性,防止暴露敏感属性。
**例1**:在以下代码片段中,在 Spring MVC(3.0版本至最新)禁止绑定敏感属性。
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"admin"});
}
@RequestMapping("/login" )
public String login(User user) {
@RequestMapping(value="/add/user", method=RequestMethod.POST, consumes="text/html")
public void addEmployee(@RequestBody User user){
}
public class User {
private String username;
private String address;
@JsonIgnore
private boolean admin;
private int age;
}
同理,Jackson还可以利用`@JsonIgnoreProperties、@JsonIgnoreTyp和 @JsonInclude`等注解告诉框架忽略这些属性,利用JAXB利用`@XmlAccessorType、@XmlAttribute、@XmlElement和 @XmlTransient`等注解告诉框架忽略这些属性,然后利用`@XmlAttribute和@XmlElement`等注解选择应绑定的字段。
**例4**:在以下代码片段中,Jackson利用`@XmlAttribute`选择要绑定的字段。
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class User {
private String username;
private String address;
@JsonIgnore
private boolean admin;
private int age;
@XmlAttribute
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@XmlAttribute
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
// write the files to the disk, but only if file is not insanely big
if (entry.getSize() > MAX) {
throw new IllegalStateException("File to be unzipped is huge.");
}
if (entry.getSize() == -1) {
throw new IllegalStateException("File to be unzipped might be huge.");
}
FileOutputStream fos = new FileOutputStream(entry.getName());
bop = new BufferedOutputStream(fos, SIZE);
while ((count = zis.read(data, 0, SIZE)) != -1) {
bop.write(data, 0, count);
} 低危:数据跨越信托边界
<!--select user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
select * from db_user where user_name=${username}
</select>
如果攻击者能够替代username中的任意字符串,它们可以利用下面的关于username的字符串举行SQL注入。
`validuser' OR '1'='1`
当其注入到命令时,命令就会酿成:
`select * from db_user where user_name ='validuser' OR '1'='1'`
<!--select user information by name-->
<select id="queryByUserName" resultMap="userResultMap" parameterType="String">
select * from db_user where user_name=#{username}
</select> 中危:公式注入
int bytesToRead = 1024;
byte[] byteArray = new byte[bytesToRead];
streamFileInput = new FileInputStream("C:\\file.txt");
streamFileInput.read(byteArray);
修复发起:
public static void main (String args[]) {
for (int i = 0; i < 10; i++) {
Random random = new Random(123456);
int number = random.nextInt(21);
}
}
修复发起:
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) {
}
} 中危:空暗码
程序中利用了空的暗码值,系统安全性将会受到威胁。
**比方**:下列代码中接纳了空的暗码。
public Connection getConnection(){
String url = "localhost";
String name = "admin";
String password = "";
}
修复发起:
程序中不应利用空的暗码值,程序所需暗码应从设置文件中获取加密的暗码值。
**比方**:下列代码中从设置文件中获取经过加密的暗码值并解密利用。
public Connection getConnection(){
String url = EncryptUtil.decrypt(PropertiesUtil.get("connection.url"));
String name = EncryptUtil.decrypt(PropertiesUtil.get("connection.username"));
String password = EncryptUtil.decrypt(PropertiesUtil.get("connection.password"));
} 中危:硬编码暗码
程序中接纳硬编码方式处理暗码,一方面会降低系统安全性,另一方面不易于程序维护。
**比方**:下列代码中接纳硬编码方式处理暗码。
public class ConnectionConfig{
String url = "localhost";
String name = "admin";
String password = "123456";
}
修复发起:
加密大于块的数据时,应该制止利用ECB模式,因为ECB模式下雷同的明文块总是会得到雷同的密文,有回放攻击的风险。CBC模式可以制止回放攻击,但是其效率较低,而且在和SSL一起利用时会造成严重风险。故可以改用`CCM(Counter with CBC-MAC)`模式,如果更留意性能,在可用的情况下则利用`GCM(Galois/Counter)`模式。
**比方**:以下代码将AES暗码用于GCM模式。
GCMParameterSpec s = new GCMParameterSpec(tLen, src);
Cipher cipher = Cipher.getInstance("AES/OFB8/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, createSecretKey(seed), s); 低危:不安全的随机数
function getToken (){
var token = Math.random();
return token;
}
修复发起:
JavaScript在安全性要求较高的环境中生成随机数,常规的发起是利用 Mozilla API 中的`window.crypto.getRandomValues()`函数,但这种方法受限于浏览器的兼容性,只在较新的版本(Chrome>=11.0, Firefox>=21, Edge, IE>=11, Safari>=3.1)可以利用。如果考虑到旧版本浏览器,则应在javascript之外处理PRNG功能。
**比方**:缺陷描述中的例子可以改写为。
function getToken (){
var array = new Uint32Array(1);
window.crypto.getRandomValues(array);
var token = array[0];
return token;
} 低危:表明中的暗码
**比方**:下面代码片段通过当前用户id下载相应图片。
```java
...
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = (User)principal;
File f = new File("/user/"+user.getId()+".png");
download(f);
...
```
长期性框架(如 Hibernate或JPA)通常会自动更新数据库实体,当系统答应哀求参数可以直接自动填充数据库长期实体时,攻击者将能够通过提供附加的哀求参数向数据库中注入非预期的值。
**比方**:在下面的示例中:Order、User都是 Hibernate长期类。
```java
public class Order {
String orderId;
List goods;
User User;
...
}
public class User {
String userId;
String username;
String password;
...
}
```
OrderController是处理该哀求的Spring控制器类:
```java
@Controller
public class OrderController {
...
@RequestMapping("/updateOrder")
public String updateOrder(Order order) {
...
session.save(order);
}
}
```
当系统答应哀求自动绑定到数据库实体时,攻击者可以通过在该哀求中添加如下哀求参数来更新其他用户的暗码:`https://www.shopwebsite.com/myOrder/updateOrder?order.user.userId=1234&order.user.password=newpassword`
修复发起
程序对非受信的用户输入数据举行净化,不要直接填充给数据库实体。 中危:非静态内部类实现序列化接口[Java]
非静态内部类(包括普通内部类、Local内部类和匿名内部类)的实例持有一个外部类实例的隐式引用。非静态内部类实现序列化接口,当其被序列化时会出现运行时异常或泄露外部类中的信息。
下面代码示例中:内部类间接实现序列化接口
```java
public class SuperSer implements Serializable{
//...
}
public class OuterSer{
private int rank;
class InnerSer extends SuperSer{
protected String name;
}
}
```
**例1**:下面代码示例中:内部类实现或间接实现序列化接口,外部类没有实现序列化,当内部类被序列化时,程序会报出NotSerializableException
```java
public class OuterSer{
private int rank;
class InnerSer implements Serializable{
protected String name;
}
}
```
修复发起
内部类不要实现或间接实现Serializable接口,或将内部类声明为静态的。
**例1**:下面代码示例中:内部类没有实现序列化接口。
```java
public class OuterSer implements Serializable {
private int rank;
class InnerSer{
protected String name;
}
}
```
**例2**:下面代码示例中:将内部类声明为静态内部类。
```java
public class OuterSer implements Serializable {
private int rank;
static class InnerSer implements Serializable{
protected String name;
}
}
```
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Perform servlet tasks.
// Create a new thread to handle background processing.
Runnable r = new Runnable() {
public void run() {
// Process and store request statistics.
...
}
};
new Thread(r).start();
}
修复发起:
发起利用框架(如EJB、Spring等)提供的线程管理功能来替代直接利用操作线程。
低危:JavaEE程序:遗留的调试代码
应用程序中的测试代码会创建一些意想不到的入口,这些入口可能会被攻击者作为“后门”举行利用
**比方**:JAVA EE程序中的`main()`方法。
public static void main(String[] args) {
...
}
修复发起:
class GrantAccess {
public static void displayAccountStatus() {
System.out.println("Account details for admin: XX");
}
}
class GrantUserAccess extends GrantAccess {
public static void displayAccountStatus() {
System.out.println("Account details for user: XX");
}
}
public class StatMethod {
public static void choose(String username) {
GrantAccess admin = new GrantAccess();
GrantAccess user = new GrantUserAccess();
if (username.equals("admin")) {
admin.displayAccountStatus();
} else {
user.displayAccountStatus();
}
}
public static void main(String[] args) {
choose("user"); // Account details for admin: XX
choose("admin"); // Account details for admin: XX
}
}
class GrantAccess {
public void displayAccountStatus() {
System.out.println("Account details for admin: XX");
}
}
class GrantUserAccess extends GrantAccess {
@Override
public void displayAccountStatus() {
System.out.println("Account details for user: XX");
}
}
public class StatMethod {
public static void choose(String username) {
GrantAccess admin = new GrantAccess();
GrantAccess user = new GrantUserAccess();
if (username.equals("admin")) {
admin.displayAccountStatus();
} else {
user.displayAccountStatus();
}
}
public static void main(String[] args) {
choose("user"); // Account details for user: XX
choose("admin"); // Account details for admin: XX
}
}
var req = new XMLHttpRequest();
req.open("OST", "/transferFunds", true);
body = "to_account=Bill&amount=10000";
req.send(body);
**例2**:下面是通过表单发送哀求。
<form method="GET" action="/transferFunds ">
cash: <input type="text" name="cash">
to: <input type=" text " name=“to">
<input type="submit" name="action" value="TransferFunds">
</form>
修复发起: