@@ -2,7 +2,7 @@ Jsoup代码解读之八-防御XSS攻击
2
2
--------
3
3
![ hacker] [ 1 ]
4
4
5
- ## 一般原理
5
+ ## 防御XSS攻击的一般原理
6
6
7
7
cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御。
8
8
@@ -27,7 +27,7 @@ cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入
27
27
28
28
对于上述的两个步骤,1、3都已经分别在parser和输出中完成,现在只剩下步骤 2:过滤高风险标签等。
29
29
30
- Jsoup给出的答案是白名单。
30
+ Jsoup给出的答案是白名单。下面是 ` Whitelist ` 的部分代码。
31
31
32
32
``` java
33
33
public class Whitelist {
@@ -39,5 +39,62 @@ public class Whitelist {
39
39
}
40
40
```
41
41
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等类的出现,个人感觉这点是非常好的。
42
99
43
100
[ 1 ] : http://static.oschina.net/uploads/space/2013/0831/071752_RBZc_190591.png
0 commit comments