Skip to content

Commit 89cb9d8

Browse files
committed
fix #13
1 parent fc1be1b commit 89cb9d8

File tree

17 files changed

+439
-162
lines changed

17 files changed

+439
-162
lines changed

src/main/java/org/joychou/config/SafeDomainConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77

88

99
/**
10-
* 为了不要每次调用都解析safedomain的xml,所以将解析动作放在bean里
10+
* 为了不要每次调用都解析safedomain的xml,所以将解析动作放在Bean里
1111
*/
1212
@Configuration
1313
public class SafeDomainConfig {
1414

1515
private static final Logger LOGGER = LoggerFactory.getLogger(SafeDomainConfig.class);
1616

17-
@Bean
17+
@Bean // @Bean代表将safeDomainParserf方法返回的对象装配到SpringIOC容器中
1818
public SafeDomainParser safeDomainParser() {
1919
try {
2020
LOGGER.info("SafeDomainParser bean inject successfully!!!");
@@ -26,3 +26,4 @@ public SafeDomainParser safeDomainParser() {
2626
}
2727

2828
}
29+

src/main/java/org/joychou/config/SafeDomainParser.java

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ public class SafeDomainParser {
1818

1919
public SafeDomainParser(){
2020

21-
String safeTag = "safedomain";
22-
String domainSafeTag = "domain";
23-
String safeDomainClassPath = "url" + File.separator + "safe_domain.xml";
21+
String rootTag = "domains";
22+
String safeDomainTag = "safedomains";
23+
String blockDomainTag = "blockdomains";
24+
String finalTag = "domain";
25+
String safeDomainClassPath = "url" + File.separator + "url_safe_domain.xml";
2426
ArrayList<String> safeDomains = new ArrayList<>();
27+
ArrayList<String> blockDomains = new ArrayList<>();
2528

2629
try {
2730
// 读取resources目录下的文件
@@ -32,23 +35,104 @@ public SafeDomainParser(){
3235
DocumentBuilder db = dbf.newDocumentBuilder();
3336
Document doc = db.parse(file); // parse xml
3437

35-
NodeList rootNode = doc.getElementsByTagName(safeTag);
38+
NodeList rootNode = doc.getElementsByTagName(rootTag); // 解析根节点domains
3639
Node domainsNode = rootNode.item(0);
3740
NodeList child = domainsNode.getChildNodes();
3841

3942
for (int i = 0; i < child.getLength(); i++){
4043
Node node = child.item(i);
41-
if (node.getNodeName().equals(domainSafeTag)) {
42-
safeDomains.add(node.getTextContent());
44+
// 解析safeDomains节点
45+
if (node.getNodeName().equals(safeDomainTag)) {
46+
NodeList tagChild = node.getChildNodes();
47+
for (int j = 0; j < tagChild.getLength(); j++) {
48+
Node finalTagNode = tagChild.item(j);
49+
// 解析safeDomains节点里的domain节点
50+
if (finalTagNode.getNodeName().equals(finalTag)) {
51+
safeDomains.add(finalTagNode.getTextContent());
52+
}
53+
}
54+
}else if (node.getNodeName().equals(blockDomainTag)) {
55+
NodeList finalTagNode = node.getChildNodes();
56+
for (int j = 0; j < finalTagNode.getLength(); j++) {
57+
Node tagNode = finalTagNode.item(j);
58+
// 解析blockDomains节点里的domain节点
59+
if (tagNode.getNodeName().equals(finalTag)) {
60+
blockDomains.add(tagNode.getTextContent());
61+
}
62+
}
4363
}
4464
}
45-
4665
}catch (Exception e){
4766
logger.error(e.toString());
4867
}
4968

5069
WebConfig wc = new WebConfig();
5170
wc.setSafeDomains(safeDomains);
71+
wc.setBlockDomains(blockDomains);
72+
73+
// 解析SSRF配置
74+
String ssrfRootTag = "ssrfsafeconfig";
75+
String ssrfSafeDomainTag = "safedomains";
76+
String ssrfBlockDomainTag = "blockdomains";
77+
String ssrfBlockIpsTag = "blockips";
78+
String ssrfFinalTag = "domain";
79+
String ssrfIpFinalTag = "ip";
80+
String ssrfSafeDomainClassPath = "url" + File.separator + "ssrf_safe_domain.xml";
81+
82+
ArrayList<String> ssrfSafeDomains = new ArrayList<>();
83+
ArrayList<String> ssrfBlockDomains = new ArrayList<>();
84+
ArrayList<String> ssrfBlockIps = new ArrayList<>();
85+
86+
try {
87+
// 读取resources目录下的文件
88+
ClassPathResource resource = new ClassPathResource(ssrfSafeDomainClassPath);
89+
File file = resource.getFile();
90+
91+
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
92+
DocumentBuilder db = dbf.newDocumentBuilder();
93+
Document doc = db.parse(file); // parse xml
94+
95+
NodeList rootNode = doc.getElementsByTagName(ssrfRootTag); // 解析根节点
96+
Node domainsNode = rootNode.item(0);
97+
NodeList child = domainsNode.getChildNodes();
98+
99+
for (int i = 0; i < child.getLength(); i++){
100+
Node node = child.item(i);
101+
// 解析safeDomains节点
102+
if (node.getNodeName().equals(ssrfSafeDomainTag)) {
103+
NodeList tagChild = node.getChildNodes();
104+
for (int j = 0; j < tagChild.getLength(); j++) {
105+
Node tagFinalNode = tagChild.item(j);
106+
if (tagFinalNode.getNodeName().equals(ssrfFinalTag)) {
107+
ssrfSafeDomains.add(tagFinalNode.getTextContent());
108+
}
109+
}
110+
}else if (node.getNodeName().equals(ssrfBlockDomainTag)) {
111+
NodeList tagChild = node.getChildNodes();
112+
for (int j = 0; j < tagChild.getLength(); j++) {
113+
Node tagFinalNode = tagChild.item(j);
114+
if (tagFinalNode.getNodeName().equals(ssrfFinalTag)) {
115+
ssrfBlockDomains.add(tagFinalNode.getTextContent());
116+
}
117+
}
118+
}else if(node.getNodeName().equals(ssrfBlockIpsTag)){
119+
NodeList tagChild = node.getChildNodes();
120+
for (int j = 0; j < tagChild.getLength(); j++) {
121+
Node tagFinalNode = tagChild.item(j);
122+
// 解析 blockIps 节点里的 ip 节点
123+
if (tagFinalNode.getNodeName().equals(ssrfIpFinalTag)) {
124+
ssrfBlockIps.add(tagFinalNode.getTextContent());
125+
}
126+
}
127+
}
128+
}
129+
}catch (Exception e){
130+
logger.error(e.toString());
131+
}
132+
133+
wc.setSsrfBlockDomains(ssrfBlockDomains);
134+
wc.setSsrfBlockIps(ssrfBlockIps);
135+
wc.setSsrfSafeDomains(ssrfSafeDomains);
52136
}
53137
}
54138

src/main/java/org/joychou/config/WebConfig.java

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
* @author JoyChou @2019-07-24
1313
*/
14-
@Component
14+
@Component // 注解@Component表明WebConfig类将被SpringIoC容器扫描装配,并且Bean名称为webConfig
1515
public class WebConfig {
1616

1717
private static String[] callbacks;
@@ -22,7 +22,10 @@ public class WebConfig {
2222
private static Boolean referSecEnabled = false;
2323
private static String businessCallback;
2424
private static ArrayList<String> safeDomains= new ArrayList<>();
25-
25+
private static ArrayList<String> blockDomains= new ArrayList<>();
26+
private static ArrayList<String> ssrfSafeDomains = new ArrayList<>();
27+
private static ArrayList<String> ssrfBlockDomains= new ArrayList<>();
28+
private static ArrayList<String> ssrfBlockIps = new ArrayList<>();
2629
/**
2730
* application.properties里object自动转jsonp的referer校验开关
2831
* @param jsonpReferCheckEnabled jsonp校验开关
@@ -45,19 +48,6 @@ public static String[] getJsonpCallbacks(){
4548
}
4649

4750

48-
/**
49-
* application.properties里object自动转jsonp的referer白名单域名
50-
* @param jsonpRefererHost 白名单域名,仅支持一级域名
51-
*/
52-
@Value("${joychou.security.jsonp.referer.host}")
53-
public void setJsonpReferWhitelist(String[] jsonpRefererHost){
54-
WebConfig.jsonpRefererHost = jsonpRefererHost;
55-
}
56-
public static String[] getJsonpReferWhitelist(){
57-
return jsonpRefererHost;
58-
}
59-
60-
6151
@Value("${joychou.security.referer.enabled}")
6252
public void setReferSecEnabled(Boolean referSecEnabled){
6353
WebConfig.referSecEnabled = referSecEnabled;
@@ -95,10 +85,42 @@ public static String getBusinessCallback(){
9585
}
9686

9787

98-
public void setSafeDomains(ArrayList<String> safeDomains){
88+
void setSafeDomains(ArrayList<String> safeDomains){
9989
WebConfig.safeDomains = safeDomains;
10090
}
10191
public static ArrayList<String> getSafeDomains(){
10292
return safeDomains;
10393
}
94+
95+
96+
void setBlockDomains(ArrayList<String> blockDomains){
97+
WebConfig.blockDomains = blockDomains;
98+
}
99+
public static ArrayList<String> getBlockDomains(){
100+
return blockDomains;
101+
}
102+
103+
104+
void setSsrfSafeDomains(ArrayList<String> ssrfSafeDomains){
105+
WebConfig.ssrfSafeDomains = ssrfSafeDomains;
106+
}
107+
public static ArrayList<String> getSsrfSafeDomains(){
108+
return ssrfSafeDomains;
109+
}
110+
111+
112+
void setSsrfBlockDomains(ArrayList<String> ssrfBlockDomains){
113+
WebConfig.ssrfBlockDomains = ssrfBlockDomains;
114+
}
115+
public static ArrayList<String> getSsrfBlockDomainsDomains(){
116+
return ssrfBlockDomains;
117+
}
118+
119+
120+
void setSsrfBlockIps(ArrayList<String> ssrfBlockIps){
121+
WebConfig.ssrfBlockIps = ssrfBlockIps;
122+
}
123+
public static ArrayList<String> getSsrfBlockIps(){
124+
return ssrfBlockIps;
125+
}
104126
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.joychou.controller;
2+
3+
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.util.AntPathMatcher;
7+
import org.springframework.util.PathMatcher;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
import javax.servlet.http.HttpServletRequest;
12+
13+
/**
14+
* The difference between getRequestURI and getServletPath.
15+
* 由于Spring Security的<code>antMatchers("/css/**", "/js/**")</code>未使用getRequestURI,所以登录不会被绕过。
16+
*
17+
* Details: https://joychou.org/web/security-of-getRequestURI.html
18+
*
19+
* Poc:
20+
* http://localhost:8080/css/%2e%2e/exclued/vuln
21+
* http://localhost:8080/css/..;/exclued/vuln
22+
* http://localhost:8080/css/..;bypasswaf/exclued/vuln
23+
*
24+
* @author JoyChou @2020-03-28
25+
*/
26+
27+
@RestController
28+
@RequestMapping("uri")
29+
public class GetRequestURI {
30+
31+
private final Logger logger= LoggerFactory.getLogger(this.getClass());
32+
33+
@RequestMapping(value = "/exclued/vuln")
34+
public String exclued(HttpServletRequest request) {
35+
36+
String[] excluedPath = {"/css/**", "/js/**"};
37+
String uri = request.getRequestURI(); // Security: request.getServletPath()
38+
PathMatcher matcher = new AntPathMatcher();
39+
40+
logger.info("getRequestURI: " + uri);
41+
logger.info("getServletPath: " + request.getServletPath());
42+
43+
for (String path: excluedPath) {
44+
if ( matcher.match (path, uri) ) {
45+
return "You have bypassed the login page.";
46+
}
47+
}
48+
return "This is a login page >..<";
49+
}
50+
}

src/main/java/org/joychou/controller/SSRF.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public static String ssrf_HttpClient(@RequestParam String url) {
197197
@RequestMapping("/commonsHttpClient/sec")
198198
@ResponseBody
199199
public static String commonsHttpClient(@RequestParam String url) {
200-
if (!SecurityUtil.checkSSRFWithoutRedirect(url)) {
200+
if (!SecurityUtil.checkSSRFByWhitehosts(url)) {
201201
return "Bad man. I got u.";
202202
}
203203

@@ -285,7 +285,7 @@ public static String IOUtils(@RequestParam String url) {
285285
public static String ImageIOSec(@RequestParam String url) {
286286
try {
287287
URL u = new URL(url);
288-
if (!SecurityUtil.checkSSRF(url)) {
288+
if (!SecurityUtil.checkSSRFWithoutRedirect(url)) {
289289
logger.error("[-] SSRF check failed. Original Url: "+ url);
290290
return "SSRF check failed.";
291291
}

0 commit comments

Comments
 (0)