Analysis of CSRF principles and Struts2 token verification Defense Strategy
Struts2 token not only effectively prevents repeated form submission, but also supports CSRF verification.
The CSRF attack principle is as follows:
CSRF attack schematic
In fact, B may also be a benign website, but it is only hijacked by the hacker XSS. The user is really wronged: I have not got a mess of websites, why is it still a trick?
Struts2 token Verification Principle:
Struts2 token verification schematic
According to the CSRF attack schematic, although. jsp returns the token value to the browser, But B cannot get the specific value of the token, or B can only get A token by requesting, however, the purpose of forging user requests is lost because the session is inconsistent. Therefore, B. action no longer processes the forged request. The CSRF process is truncated, as shown in:
Struts2 token truncated the CSRF attack schematic
If you understand the principles, let's work. It is found that many non-form submission requests are successfully intercepted, for example, the original ajax requests, href requests on the page, and dwr requests cannot work normally. -- remove the token verification of the actions in these requests to work normally, however, this gives hackers a good opportunity.
So how can I transfer struts2 token to other requests except the form?
Let's continue to learn more about the struts2 token verification principles.
Take the latest version 2.3.20 as an example. Add an action in struts. xml In fact, the org. apache. struts2.interceptor. TokenInterceptor interceptor is configured. View its doIntercept source code:
protected String doIntercept(ActionInvocation invocation) throws Exception {if (log.isDebugEnabled()) {log.debug(Intercepting invocation to check for valid transaction token.);}//see WW-2902: we need to use the real HttpSession here, as opposed to the map//that wraps the session, because a new wrap is created on every requestHttpSession session = ServletActionContext.getRequest().getSession(true);synchronized (session) {if (!TokenHelper.validToken()) {return handleInvalidToken(invocation);}}return handleValidToken(invocation);}
It calls the validToken method of org. apache. struts2.util. TokenHelper. Its source code is as follows:
public static boolean validToken() {String tokenName = getTokenName();if (tokenName == null) {if (LOG.isDebugEnabled()) {LOG.debug(no token name found -> Invalid token );}return false;}String token = getToken(tokenName);if (token == null) {if (LOG.isDebugEnabled()) {LOG.debug(no token found for token name +tokenName+ -> Invalid token );}return false;}Map session = ActionContext.getContext().getSession();String sessionToken = (String) session.get(tokenName);if (!token.equals(sessionToken)) {if (LOG.isWarnEnabled()) {LOG.warn(LocalizedTextUtil.findText(TokenHelper.class, struts.internal.invalid.token, ActionContext.getContext().getLocale(), Form token {0} does not match the session token {1}., new Object[]{token, sessionToken}));}return false;}// remove the token so it won't be used againsession.remove(tokenName);return true;}
It first calls its own getTokenName () to get the tokenName value, and then uses its own getToken (tokenName) to get the value of the token parameter requested by the client, finally, compare it with the value saved in the session.
View the getTokenName () source code:
public static String getTokenName() {Map params = ActionContext.getContext().getParameters();if (!params.containsKey(TOKEN_NAME_FIELD)) {if (LOG.isWarnEnabled()) {LOG.warn(Could not find token name in params.);}return null;}String[] tokenNames = (String[]) params.get(TOKEN_NAME_FIELD);String tokenName;if ((tokenNames == null) || (tokenNames.length < 1)) {if (LOG.isWarnEnabled()) {LOG.warn(Got a null or empty token name.);}return null;}tokenName = tokenNames[0];return tokenName;}
TOKEN_NAME_FIELD is a static variable of org. apache. struts2.util. TokenHelper with the value of struts. token. name. The function of the getTokenName method is to get the value of struts. token. name passed by the client. After the validToken obtains this value, it continues to call getToken based on this value. The getToken source code:
public static String getToken(String tokenName) {if (tokenName == null ) {return null;}Map params = ActionContext.getContext().getParameters();String[] tokens = (String[]) params.get(tokenName);String token;if ((tokens == null) || (tokens.length < 1)) {if (LOG.isWarnEnabled()) {LOG.warn(Could not find token mapped to token name + tokenName);}return null;}token = tokens[0];return token;}
It is based on the value of the struts. token. name parameter, for example, token (this value can be configured in the token tag of jsp, such , The default value is token), get the value of the token parameter sent from the client, and finally return it to the validToken method.
The struts action with token verification captured by chrome's developer tool confirms the above statement:
The value of struts. token. name intercepted
I have understood the principles, and the following things seem to have gone through.
$. Ajax supports struts2 token:
<script>var strutsToken =
;var token = { struts.token.name: token, token: strutsToken};$.ajax({ url: '/endpoint', data: token, dataType: 'jsonp', cache: true, success: function() { console.log('success'); }, error: function() { console.log('failure'); }});</script>
The href request on the page supports struts2 token:
Title = Edit Meta> View Details