/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2013 The ZAP Development team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.parosproxy.paros.core.scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import org.parosproxy.paros.network.HttpHeaderField;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
/**
* A {@code Variant} for HTTP headers, allowing to attack the values of the headers.
* <p>
* Some headers are ignored as attacking them would have unwanted side effects (for example, changing or removing a
* Proxy-Authorization header or Content-Length header).
*
* @since 2.2.0
* @author andy
* @see Variant
* @see VariantCookie
*/
public class VariantHeader implements Variant {
//might still be publicly used.
@Deprecated
public static final String[] injectableHeaders = {
HttpRequestHeader.USER_AGENT,
HttpRequestHeader.REFERER,
HttpRequestHeader.HOST
};
//headers converted to lowercase to make comparison easier later.
private static final String [] injectablesTempArray = {
HttpRequestHeader.CONTENT_LENGTH.toLowerCase(Locale.ROOT), //scanning this would likely break the entire request
HttpRequestHeader.PRAGMA.toLowerCase(Locale.ROOT), //unlikely to be picked up/used by the app itself.
HttpRequestHeader.CACHE_CONTROL.toLowerCase(Locale.ROOT), //unlikely to be picked up/used by the app itself.
HttpRequestHeader.COOKIE.toLowerCase(Locale.ROOT), //The Cookie header has its own variant that controls whether it is scanned. Better not to scan it as a header.
HttpRequestHeader.AUTHORIZATION.toLowerCase(Locale.ROOT), //scanning this would break authorisation
HttpRequestHeader.PROXY_AUTHORIZATION.toLowerCase(Locale.ROOT), //scanning this would break authorisation
HttpRequestHeader.CONNECTION.toLowerCase(Locale.ROOT), //scanning this would likely break the entire request
HttpRequestHeader.PROXY_CONNECTION.toLowerCase(Locale.ROOT), //scanning this would likely break the entire request
HttpRequestHeader.IF_MODIFIED_SINCE.toLowerCase(Locale.ROOT), //unlikely to be picked up/used by the app itself.
HttpRequestHeader.IF_NONE_MATCH.toLowerCase(Locale.ROOT), //unlikely to be picked up/used by the app itself.
HttpRequestHeader.X_CSRF_TOKEN.toLowerCase(Locale.ROOT), //scanning this would break authorisation
HttpRequestHeader.X_CSRFTOKEN.toLowerCase(Locale.ROOT), //scanning this would break authorisation
HttpRequestHeader.X_XSRF_TOKEN.toLowerCase(Locale.ROOT), //scanning this would break authorisation
HttpRequestHeader.X_ZAP_SCAN_ID.toLowerCase(Locale.ROOT), //inserted by ZAP, so no need to scan it.
HttpRequestHeader.X_ZAP_REQUESTID.toLowerCase(Locale.ROOT), //inserted by ZAP, so no need to scan it.
HttpRequestHeader.X_SECURITY_PROXY.toLowerCase(Locale.ROOT), //unlikely to be picked up/used by the app itself.
};
//a hashset of (lowercase) headers that we can look up quickly and easily
private static final HashSet <String> NON_INJECTABLE_HEADERS = new HashSet<String>(Arrays.asList(injectablesTempArray));
/**
* The list of parameters (that is, headers) extracted from the request header of the message, never {@code null}.
*/
private List<NameValuePair> params = Collections.emptyList();
/**
* @throws IllegalArgumentException if {@code message} is {@code null}.
*/
@Override
public void setMessage(HttpMessage message) {
if (message == null) {
throw new IllegalArgumentException("Parameter message must not be null.");
}
ArrayList<NameValuePair> extractedParameters = new ArrayList<>();
List<HttpHeaderField> httpHeaders = message.getRequestHeader().getHeaders();
for (HttpHeaderField header : httpHeaders) {
if (! NON_INJECTABLE_HEADERS.contains(header.getName().toLowerCase(Locale.ROOT))) {
extractedParameters.add(
new NameValuePair(
NameValuePair.TYPE_HEADER,
header.getName(),
header.getValue(),
extractedParameters.size()));
}
}
if (extractedParameters.isEmpty()) {
params = Collections.emptyList();
} else {
extractedParameters.trimToSize();
params = Collections.unmodifiableList(extractedParameters);
}
}
/**
* Gets the list of parameters (that is, headers) extracted from the request header of the message.
*
* @return an unmodifiable {@code List} containing the extracted parameters, never {@code null}.
*/
@Override
public List<NameValuePair> getParamList() {
return params;
}
/**
*
* @param msg
* @param originalPair
* @param name
* @param value
* @return
*/
@Override
public String setParameter(HttpMessage msg, NameValuePair originalPair, String name, String value) {
msg.getRequestHeader().setHeader(originalPair.getName(), value);
if (value == null) {
return "";
}
return originalPair.getName() + ": " + value;
}
/**
*
* @param msg
* @param originalPair
* @param name
* @param value
* @return
*/
@Override
public String setEscapedParameter(HttpMessage msg, NameValuePair originalPair, String name, String value) {
return setParameter(msg, originalPair, name, value);
}
}