mirror of
https://github.com/zxing/zxing.git
synced 2025-02-02 05:41:08 -08:00
Revamp protection against flood of requests; minor related tweaks
This commit is contained in:
parent
05093ed3d2
commit
65d2b163eb
|
@ -447,6 +447,7 @@ public final class DecodeServlet extends HttpServlet {
|
||||||
if (dispatcher == null) {
|
if (dispatcher == null) {
|
||||||
log.warning("Can't obtain RequestDispatcher");
|
log.warning("Can't obtain RequestDispatcher");
|
||||||
} else {
|
} else {
|
||||||
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
dispatcher.forward(request, response);
|
dispatcher.forward(request, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,13 @@ import javax.servlet.annotation.WebFilter;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Iterator;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,40 +46,49 @@ public final class DoSFilter implements Filter {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(DoSFilter.class.getName());
|
private static final Logger log = Logger.getLogger(DoSFilter.class.getName());
|
||||||
|
|
||||||
private static final int MAX_ACCESSES_PER_IP_PER_TIME = 100;
|
private static final int MAX_ACCESSES_PER_IP_PER_TIME = 50;
|
||||||
private static final int MAX_RECENT_ACCESS_MAP_SIZE = 100_000;
|
private static final long MAX_ACCESSES_TIME_MS = TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
|
||||||
|
private static final int MAX_RECENT_ACCESS_MAP_SIZE = 10_000;
|
||||||
|
|
||||||
private final Map<String,AtomicInteger> numRecentAccesses;
|
private final Map<String,AtomicLong> numRecentAccesses;
|
||||||
private final Set<String> bannedIPAddresses;
|
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
public DoSFilter() {
|
public DoSFilter() {
|
||||||
numRecentAccesses = Collections.synchronizedMap(new LinkedHashMap<String,AtomicInteger>() {
|
numRecentAccesses = new LinkedHashMap<String,AtomicLong>() {
|
||||||
@Override
|
@Override
|
||||||
protected boolean removeEldestEntry(Map.Entry<String,AtomicInteger> eldest) {
|
protected boolean removeEldestEntry(Map.Entry<String,AtomicLong> eldest) {
|
||||||
return size() > MAX_RECENT_ACCESS_MAP_SIZE;
|
return size() > MAX_RECENT_ACCESS_MAP_SIZE;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
bannedIPAddresses = Collections.synchronizedSet(new HashSet<String>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) {
|
public void init(FilterConfig filterConfig) {
|
||||||
timer = new Timer("DoSFilter reset timer");
|
timer = new Timer("DoSFilter");
|
||||||
timer.scheduleAtFixedRate(
|
timer.scheduleAtFixedRate(
|
||||||
new TimerTask() {
|
new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
numRecentAccesses.clear();
|
synchronized (numRecentAccesses) {
|
||||||
|
// Periodically reduce allowed accesses per IP
|
||||||
|
Iterator<Map.Entry<String,AtomicLong>> accessIt = numRecentAccesses.entrySet().iterator();
|
||||||
|
while (accessIt.hasNext()) {
|
||||||
|
Map.Entry<String,AtomicLong> entry = accessIt.next();
|
||||||
|
AtomicLong count = entry.getValue();
|
||||||
|
// If number of accesses is below the threshold, remove it entirely
|
||||||
|
if (count.get() <= MAX_ACCESSES_PER_IP_PER_TIME) {
|
||||||
|
accessIt.remove();
|
||||||
|
} else {
|
||||||
|
// Else it exceeded the max, so log it (again)
|
||||||
|
log.warning("Possible DoS attack from " + entry.getKey() + " (" + count + " outstanding)");
|
||||||
|
// Reduce count of accesses held against the IP
|
||||||
|
count.getAndAdd(-MAX_ACCESSES_PER_IP_PER_TIME);
|
||||||
}
|
}
|
||||||
}, 0L, TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES));
|
|
||||||
timer.scheduleAtFixedRate(
|
|
||||||
new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
bannedIPAddresses.clear();
|
|
||||||
}
|
}
|
||||||
}, 0L, TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES));
|
log.info("Tracking accesses from " + numRecentAccesses.size() + " IPs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, MAX_ACCESSES_TIME_MS, MAX_ACCESSES_TIME_MS);
|
||||||
timer.scheduleAtFixedRate(
|
timer.scheduleAtFixedRate(
|
||||||
new TimerTask() {
|
new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -108,26 +115,19 @@ public final class DoSFilter implements Filter {
|
||||||
if (remoteIPAddress == null) {
|
if (remoteIPAddress == null) {
|
||||||
remoteIPAddress = request.getRemoteAddr();
|
remoteIPAddress = request.getRemoteAddr();
|
||||||
}
|
}
|
||||||
if (remoteIPAddress == null || bannedIPAddresses.contains(remoteIPAddress)) {
|
if (remoteIPAddress == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (getCount(remoteIPAddress) > MAX_ACCESSES_PER_IP_PER_TIME) {
|
AtomicLong count;
|
||||||
log.warning("Possible DoS attack from " + remoteIPAddress);
|
synchronized (numRecentAccesses) {
|
||||||
bannedIPAddresses.add(remoteIPAddress);
|
count = numRecentAccesses.get(remoteIPAddress);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCount(String remoteIPAddress) {
|
|
||||||
AtomicInteger count = numRecentAccesses.get(remoteIPAddress);
|
|
||||||
if (count == null) {
|
if (count == null) {
|
||||||
numRecentAccesses.put(remoteIPAddress, new AtomicInteger(1));
|
count = new AtomicLong();
|
||||||
return 1;
|
numRecentAccesses.put(remoteIPAddress, count);
|
||||||
} else {
|
|
||||||
return count.incrementAndGet();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return count.incrementAndGet() > MAX_ACCESSES_PER_IP_PER_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
|
|
@ -55,7 +55,7 @@ JAVA_OPTS="-Djava.security.egd=file:/dev/urandom -Djava.awt.headless=true -Xmx32
|
||||||
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
|
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
|
||||||
|
|
||||||
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
|
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
|
||||||
prefix="localhost_access_log" suffix=".txt" rotatable="false"
|
prefix="localhost_access_log" suffix=".txt"
|
||||||
pattern="%h %l %u %t "%r" %s %b" />
|
pattern="%h %l %u %t "%r" %s %b" />
|
||||||
|
|
||||||
</Host>
|
</Host>
|
||||||
|
|
Loading…
Reference in a new issue