Skip to content

Commit 348bb1d

Browse files
committed
comlete
1 parent 23da5b2 commit 348bb1d

File tree

1 file changed

+59
-2
lines changed

1 file changed

+59
-2
lines changed

blogs/jsoup8.md

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Jsoup代码解读之八-防御XSS攻击
22
--------
33
![hacker][1]
44

5-
## 一般原理
5+
## 防御XSS攻击的一般原理
66

77
cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御。
88

@@ -27,7 +27,7 @@ cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入
2727

2828
对于上述的两个步骤,1、3都已经分别在parser和输出中完成,现在只剩下步骤 2:过滤高风险标签等。
2929

30-
Jsoup给出的答案是白名单。
30+
Jsoup给出的答案是白名单。下面是`Whitelist`的部分代码。
3131

3232
```java
3333
public class Whitelist {
@@ -39,5 +39,62 @@ public class Whitelist {
3939
}
4040
```
4141

42+
这里定义了标签名/属性名/属性值的白名单。
43+
44+
`Cleaner`是过滤的执行者。不出所料,Cleaner内部定义了`CleaningVisitor`来进行标签的过滤。CleaningVisitor的过滤过程并不改变原始DOM树的值,而是将符合条件的属性,加入到`Element destination`里去。
45+
46+
private final class CleaningVisitor implements NodeVisitor {
47+
private int numDiscarded = 0;
48+
private final Element root;
49+
private Element destination; // current element to append nodes to
50+
51+
private CleaningVisitor(Element root, Element destination) {
52+
this.root = root;
53+
this.destination = destination;
54+
}
55+
56+
public void head(Node source, int depth) {
57+
if (source instanceof Element) {
58+
Element sourceEl = (Element) source;
59+
60+
if (whitelist.isSafeTag(sourceEl.tagName())) { // safe, clone and copy safe attrs
61+
ElementMeta meta = createSafeElement(sourceEl);
62+
Element destChild = meta.el;
63+
destination.appendChild(destChild);
64+
65+
numDiscarded += meta.numAttribsDiscarded;
66+
destination = destChild;
67+
} else if (source != root) { // not a safe tag, so don't add. don't count root against discarded.
68+
numDiscarded++;
69+
}
70+
} else if (source instanceof TextNode) {
71+
TextNode sourceText = (TextNode) source;
72+
TextNode destText = new TextNode(sourceText.getWholeText(), source.baseUri());
73+
destination.appendChild(destText);
74+
} else { // else, we don't care about comments, xml proc instructions, etc
75+
numDiscarded++;
76+
}
77+
}
78+
79+
public void tail(Node source, int depth) {
80+
if (source instanceof Element && whitelist.isSafeTag(source.nodeName())) {
81+
destination = destination.parent(); // would have descended, so pop destination stack
82+
}
83+
}
84+
}
85+
86+
## 结束语
87+
88+
至此,Jsoup的全部模块都已经写完了。Jsoup源码并不多,只有14000多行,但是实现非常精巧,在读代码的过程中,除了相关知识,还验证几个很重要的思想:
89+
90+
* 最好的代码抽象,是对现实概念的映射。
91+
92+
这句话在看《代码大全》的时候印象很深刻。在Jsoup里,只要有相关知识,每个类的作用都能第一时间明白其作用,这点在看nodes模块时感觉更强烈。
93+
94+
* 不要过度抽象
95+
96+
在Jsoup里,只用到了两个接口,一个是`NodeVisitor`,一个是`Connection`,其他都是用抽象类或者直接用实现类代替。记得有次面试的时候被问到我们开发中每逢一个功能,都要先定义一个接口的做法是否必要?现在的答案是没有必要,过度的抽象反而会降低代码质量。
97+
98+
另外,Jsoup的代码内聚性都很高,每个类的功能基本都定义在类的内部,这是一个典型的充血模型。同时有大量的facade使用,而避免了Factory、Configure等类的出现,个人感觉这点是非常好的。
4299

43100
[1]: http://static.oschina.net/uploads/space/2013/0831/071752_RBZc_190591.png

0 commit comments

Comments
 (0)