View Javadoc
1   package com.jsql.util;
2   
3   import com.jsql.model.InjectionModel;
4   import org.apache.commons.lang3.StringUtils;
5   import org.apache.logging.log4j.LogManager;
6   import org.apache.logging.log4j.Logger;
7   import org.jsoup.Jsoup;
8   
9   import java.net.http.HttpRequest.Builder;
10  import java.util.AbstractMap.SimpleEntry;
11  import java.util.*;
12  import java.util.stream.Stream;
13  
14  public class CsrfUtil {
15      
16      /**
17       * Log4j logger sent to view.
18       */
19      private static final Logger LOGGER = LogManager.getRootLogger();
20  
21      private SimpleEntry<String, String> tokenCsrf = null;
22      
23      private static final String SET_COOKIE_RESPONSE = "set-cookie";
24      private static final String INPUT_ATTR_VALUE = "value";
25      private static final String MSG_ENABLE_CSRF = "Try with option CSRF processing enabled in preferences";
26  
27      private final InjectionModel injectionModel;
28      
29      public CsrfUtil(InjectionModel injectionModel) {
30          this.injectionModel = injectionModel;
31      }
32  
33      public void parseForCsrfToken(String pageSource, Map<String, String> headers) {
34          this.parseCsrfFromCookie(headers);
35          this.parseCsrfFromHtml(pageSource);
36      }
37  
38      private void parseCsrfFromHtml(String pageSource) {
39          // Change immutable list to mutable for adding user tag
40          List<String> tags = new ArrayList<>(
41              Arrays.asList(
42                  "[name=_csrf]",
43                  "[name=_token]",
44                  "[name=csrf-token]",
45                  "[name=_csrf_header]",
46                  "[name=csrf_token]",
47                  "[name=csrfToken]",
48                  "[name=user_token]",
49                  "[name=csrfmiddlewaretoken]",
50                  "[name=form_build_id]"
51              )
52          );
53          
54          if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isCsrfUserTag()) {
55              tags.add(
56                  String.format(
57                      "[name='%s']",
58                      this.injectionModel.getMediatorUtils().getPreferencesUtil().csrfUserTag()
59                  )
60              );
61          }
62          
63          Optional<SimpleEntry<String, String>> optionalTokenCsrf = Jsoup.parse(pageSource)
64              .select("input")
65              .select(String.join(",", tags))
66              .stream()
67              .findFirst()
68              .map(input ->
69                  new SimpleEntry<>(
70                      input.attr("name"),
71                      input.attr(CsrfUtil.INPUT_ATTR_VALUE)
72                  )
73              );
74          
75          if (optionalTokenCsrf.isPresent()) {
76              SimpleEntry<String, String> tokenCsrfFound = optionalTokenCsrf.get();
77              LOGGER.log(
78                  LogLevelUtil.CONSOLE_INFORM,
79                  "Found Csrf token from HTML body: {}={}",
80                  tokenCsrfFound::getKey,
81                  tokenCsrfFound::getValue
82              );
83              if (
84                  !this.injectionModel.getMediatorUtils().getPreferencesUtil().isNotProcessingCookies()
85                  && this.injectionModel.getMediatorUtils().getPreferencesUtil().isProcessingCsrf()
86              ) {
87                  this.tokenCsrf = tokenCsrfFound;
88                  LOGGER.log(
89                      LogLevelUtil.CONSOLE_SUCCESS,
90                      "Csrf token added to query and header: {}",
91                      tokenCsrfFound::getValue
92                  );
93              } else {
94                  LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CsrfUtil.MSG_ENABLE_CSRF);
95              }
96          }
97      }
98  
99      private void parseCsrfFromCookie(Map<String, String> mapResponse) {
100         Optional<SimpleEntry<String, String>> optionalCookieCsrf = Optional.empty();
101         
102         if (mapResponse.containsKey(CsrfUtil.SET_COOKIE_RESPONSE)) {
103             // Spring: Cookie XSRF-TOKEN => Header X-XSRF-TOKEN, GET/POST parameter _csrf
104             // Laravel, Zend, Symfony
105             String[] cookieValues = StringUtils.split(mapResponse.get(CsrfUtil.SET_COOKIE_RESPONSE), ";");
106             optionalCookieCsrf = Stream.of(cookieValues)
107                 .filter(cookie -> cookie.trim().toLowerCase().startsWith("xsrf-token"))
108                 .map(cookie -> {
109                     String[] cookieEntry = StringUtils.split(cookie, "=");
110                     return new SimpleEntry<>(
111                         cookieEntry[0].trim(),
112                         cookieEntry[1].trim()
113                     );
114                 })
115                 .findFirst();
116         }
117         
118         if (optionalCookieCsrf.isPresent()) {
119             SimpleEntry<String, String> cookieCsrf = optionalCookieCsrf.get();
120             LOGGER.log(
121                 LogLevelUtil.CONSOLE_ERROR,
122                 "Found CSRF token from Cookie: {}={}",
123                 cookieCsrf::getKey,
124                 cookieCsrf::getValue
125             );
126             SimpleEntry<String, String> headerCsrf = new SimpleEntry<>(
127                 cookieCsrf.getKey(),
128                 cookieCsrf.getValue()
129             );
130             if (
131                 !this.injectionModel.getMediatorUtils().getPreferencesUtil().isNotProcessingCookies()
132                 && this.injectionModel.getMediatorUtils().getPreferencesUtil().isProcessingCsrf()
133             ) {
134                 this.tokenCsrf = headerCsrf;
135             } else {
136                 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CsrfUtil.MSG_ENABLE_CSRF);
137             }
138         }
139     }
140 
141     public void addHeaderToken(Builder httpRequest) {
142         if (this.tokenCsrf == null) {
143              return;
144         }
145 
146         httpRequest.setHeader("X-XSRF-TOKEN", this.tokenCsrf.getValue());
147         httpRequest.setHeader("X-CSRF-TOKEN", this.tokenCsrf.getValue());
148         
149         if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isCsrfUserTag()) {
150             httpRequest.setHeader(
151                 this.injectionModel.getMediatorUtils().getPreferencesUtil().csrfUserTagOutput(),
152                 this.tokenCsrf.getValue()
153             );
154         }
155     }
156 
157     public void addRequestToken(StringBuilder httpRequest) {
158         if (this.tokenCsrf == null) {
159             return;
160         }
161         httpRequest.append(
162             String.format(
163                 "%s=%s&",
164                 this.tokenCsrf.getKey(),
165                 this.tokenCsrf.getValue()
166             )
167         );
168         httpRequest.append(
169             String.format(
170                 "_csrf=%s&",
171                 this.tokenCsrf.getValue()
172             )
173         );
174         if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isCsrfUserTag()) {
175             httpRequest.append(
176                 String.format(
177                     "%s=%s&",
178                     this.injectionModel.getMediatorUtils().getPreferencesUtil().csrfUserTagOutput(),
179                     this.tokenCsrf.getValue()
180                 )
181             );
182         }
183     }
184     
185     public String addQueryStringToken(String urlInjection) {
186         String urlInjectionFixed = urlInjection;
187         if (this.tokenCsrf == null) {
188             return urlInjectionFixed;
189         }
190         urlInjectionFixed += String.format(
191             "&%s=%s",
192             this.tokenCsrf.getKey(),
193             this.tokenCsrf.getValue()
194         );
195         urlInjectionFixed += String.format(
196             "&_csrf=%s",
197             this.tokenCsrf.getValue()
198         );
199         if (this.injectionModel.getMediatorUtils().getPreferencesUtil().isCsrfUserTag()) {
200             urlInjectionFixed += String.format(
201                 "&%s=%s",
202                 this.injectionModel.getMediatorUtils().getPreferencesUtil().csrfUserTagOutput(),
203                 this.tokenCsrf.getValue()
204             );
205         }
206         return urlInjectionFixed;
207     }
208     
209     
210     // Getter / Setter
211 
212     public boolean isCsrf() {
213         return this.tokenCsrf != null;
214     }
215 
216     public SimpleEntry<String, String> getTokenCsrf() {
217         return this.tokenCsrf;
218     }
219 
220     public void setTokenCsrf(SimpleEntry<String, String> tokenCsrf) {
221         this.tokenCsrf = tokenCsrf;
222     }
223 }