Network Security and Defense Questions
Securing networks at the infrastructure layer. Covers firewalls, ACLs and rule design, network device hardening and secure configuration, intrusion detection and prevention systems, VPN and remote-access encryption, network protocols and their security properties, and packet-level traffic analysis. The hands-on network-defense layer, distinct from zero-trust architecture strategy.
Write a Snort rule (Snort v2 or v3 syntax acceptable) that alerts on HTTP requests where the URI contains the SQL injection token 'UNION SELECT' (case-insensitive). The rule should match the HTTP URI, be reasonably efficient, and include a comment describing any potential false positives and performance considerations.
Sample Answer
Approach
A Snort rule has a fixed shape: an action and header (protocol, source and destination address and port, direction) followed by options in parentheses describing what to match on and metadata about the match. To match UNION SELECT in the HTTP request URI case-insensitively, use a content match scoped to the HTTP URI buffer, the portion of the request Snort's HTTP normalization extracts as the URI, with the case-insensitive modifier.
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"WEB-ATTACK SQL injection UNION SELECT attempt in URI"; flow:to_server,established; content:"UNION SELECT"; http_uri; nocase; fast_pattern; classtype:web-application-attack; sid:1000101; rev:1; reference:url,owasp.org/www-community/attacks/SQL_Injection;)
Snort 3 sticky-buffer equivalent, same match, with the buffer declared before the content it modifies:
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"WEB-ATTACK SQL injection UNION SELECT attempt in URI"; flow:to_server,established; http_uri; content:"UNION SELECT", nocase, fast_pattern; classtype:web-application-attack; sid:1000101; rev:1;)
Key points
flow:to_server,established;restricts the match to client-to-server traffic on an already-established connection, cutting the traffic the content match has to run against and avoiding matches on retransmitted or out-of-state packets.http_uriscopes the content match to only the normalized URI portion of the request rather than the entire packet, reducing false matches from unrelated header or body content, and lets Snort's HTTP preprocessor handle basic normalization, such as percent-decoding, before the match runs.nocasemakes the match case-insensitive, sounion select,Union Select, andUNION selectall match.fast_patterntells Snort's pattern-matching engine to index this specific content string for the initial fast search across all loaded rules, which matters once you have thousands of rules and traffic to inspect at line rate.sid:1000101uses a local, custom signature ID; IDs below 1,000,000 are reserved for Snort's official ruleset, so a custom rule should use 1,000,000 or above by convention.
False positives and performance
This is a narrow, literal-string match, so it will miss trivially obfuscated variants, such as the two keywords split with a comment or extra whitespace a target database still tolerates, or characters percent-encoded before Snort's normalization catches them. It can also false-positive on legitimate traffic that happens to contain that literal phrase: a security scanner's own test payloads, a submitted bug bounty report, or an internal database administration tool whose URI parameters legitimately include a UNION SELECT query for reporting. Performance-wise this is a cheap rule, a single content match scoped to a narrow buffer with fast_pattern set, so the operational cost is low; the accuracy limitation is what makes this a first, coarse layer rather than a complete defense. It belongs alongside proper input validation and parameterized queries at the application layer, which fix the underlying vulnerability rather than only detecting one way to exploit it.
Edge cases
- A request that splits the two keywords across separate parameters, or uses an inline comment between them, defeats this literal match; a more resilient version would add a regular-expression option tolerant of inserted whitespace or comment sequences, at some added CPU cost per packet.
- Traffic on a non-standard HTTP port not included in
$HTTP_PORTSis not inspected by this rule at all unless that port is added to the variable.
Briefly compare Snort, Suricata, and Zeek (formerly Bro) as IDS/network-monitoring tools. For each tool explain its primary detection approach (signature vs script-based), key strengths (performance, multi-threading, protocol parsing, scripting), typical output formats (e.g., EVE JSON, conn.log), and one common real-world use case where the tool is the preferred choice.
Sample Answer
Direct answer
Snort, Suricata, and Zeek, formerly named Bro, are the three most widely deployed open-source tools for network traffic inspection, but they take genuinely different approaches: Snort and Suricata are primarily signature-matching intrusion detection and prevention engines, while Zeek is primarily a traffic-analysis and logging framework built around a scripting language. Many shops run more than one of them together rather than choosing just one.
Comparison
| Tool | Primary approach | Key strengths | Typical output | Where it is the preferred choice |
|---|---|---|---|---|
| Snort | Signature-based rule matching | Mature, large existing signature ecosystem, official and community rulesets, well understood by most security teams | Alert logs | Standardizing on the most established, widely documented signature format and tooling |
| Suricata | Signature-based rule matching, largely Snort-rule-compatible | Native multi-threading, using multiple CPU cores in a single instance, built-in parsers for many application-layer protocols, rich structured logging | EVE JSON, one JSON object per line, per event type such as alert, DNS, HTTP, and flow | High-throughput environments that need multi-core scaling and structured JSON that feeds a SIEM (security information and event management platform) or log pipeline without custom parsing |
| Zeek | Script-based traffic analysis and logging, not primarily signature matching | Deep, protocol-aware session logging beyond just alerts, and a scripting language for custom detection logic a signature cannot easily express | Multiple structured log files by protocol, most notably conn.log, a per-connection summary of addresses, ports, protocol, duration, bytes each direction, and connection state | Investigations and threat hunting that need a rich historical record of all traffic, not just traffic that already matched a signature |
Log-field detail
Suricata's EVE JSON alert events carry fields such as source and destination IP, the matched signature, and its severity per event; its flow events summarize a completed connection's byte and packet counts. Zeek's conn.log instead gives a per-connection summary line for essentially all traffic it sees, fields such as bytes sent in each direction, connection duration, and connection state, whether or not anything about that connection matched a rule. That is exactly the "not just alerts" strength above: you cannot get a record of what every connection on the network looked like today out of a pure signature engine's alert log, because a signature engine, by design, only logs what it flagged.
Trade-offs and pitfalls
Choosing based only on which tool has more signatures misses that Zeek is solving a different problem, comprehensive logging and custom scripting, rather than competing head-to-head with Snort or Suricata's rule-matching strength. Many mature security operations centers run Suricata or Snort for signature-based alerting and Zeek in parallel for the full-traffic session logs that support deeper investigation and hunting.
Describe an approach to tune an IDS/IPS to reduce false positives while still detecting novel or zero-day attacks. Include steps for baseline profiling, whitelist/blacklist strategies, signature vs anomaly detection trade-offs, use of threat intelligence, feedback loops with SOC analysts, and metrics to evaluate tuning success.
Sample Answer
Direct answer
Reducing false positives without losing the ability to catch novel or zero-day attacks means accepting that signature-based and anomaly-based detection solve different problems, and tuning each one separately rather than treating the whole intrusion detection or prevention system as one dial to turn: signatures get tightened against your own environment's normal traffic, while anomaly-based detection is what actually gives you a shot at catching something with no existing signature at all.
Baseline profiling
Before tuning anything, capture what normal looks like for your environment: which signatures fire routinely against legitimate traffic, candidates for exclusion or scope-narrowing, and what typical traffic volumes, protocols, and destinations look like per host group, the baseline anomaly-based detection needs in order to flag deviations later.
Whitelist and blacklist strategies
Scope known-false-positive-prone signatures narrowly, excluding the specific host or subnet that triggers them for a known-legitimate reason, rather than disabling the signature entirely, so you keep its coverage against everything else. Maintain a blacklist of known-bad indicators, IPs and domains, fed from threat intelligence, to catch known threats immediately and cheaply without waiting on a full signature match.
Signature-based versus anomaly-based trade-offs
Signature-based detection is precise against known patterns, a low false-positive rate once tuned, but structurally cannot catch a genuinely novel attack it has no signature for; that is the definition of a zero-day. Anomaly-based detection, flagging traffic that deviates from an established baseline, can catch previously unseen attack patterns, but starts with a higher false-positive rate, since unusual is not the same as malicious, and it needs a properly built baseline to be useful at all. Running both together, rather than picking one, is what closes the tension in the question: signatures catch known threats cheaply and accurately, anomaly detection is the coverage for the unknown, and each has different tuning knobs.
Use of threat intelligence
Feed current indicators of compromise from threat intelligence into both layers: as blacklist entries for immediate, signature-equivalent blocking, and as context that helps triage an anomaly-based alert faster, since a currently known-active campaign matching the anomaly's pattern is far more actionable than the same anomaly with no external context.
Feedback loops with SOC analysts
Every alert an analyst investigates and closes as a false positive should feed back into a tuning backlog, not just get dismissed individually. Patterns across many closed false positives, the same signature or host group repeatedly, are what actually tell you where to spend tuning effort, rather than tuning based on a single analyst's gut sense of what feels noisy.
Metrics to evaluate tuning success
Track the false-positive rate per signature over time, whether a specific tuning change actually reduced it, and mean time to triage an alert, a proxy for whether analysts are drowning in noise. Separately, run periodic detection-coverage tests, controlled test traffic similar in spirit to the evasion-testing approach used for IDS resilience, to confirm that tuning for false-positive reduction has not accidentally suppressed real detection coverage along the way, since a tuning change measured only on the false-positive side can silently break the true-positive side without anyone noticing until an incident happens.
A concrete tuning example, and how it was measured and communicated
A signature intended to catch a specific web-shell upload pattern was firing repeatedly against a legitimate internal file-upload application that happened to produce similarly shaped requests. Rather than disabling the signature outright, the fix scoped an exclusion to that specific application's IP range and confirmed, by replaying earlier evasion-style test traffic against the now-scoped signature, that it still fired correctly against traffic from anywhere else. The change was measured by comparing the signature's daily alert volume before and after the change, a clear drop against the excluded range and unchanged everywhere else, and was communicated to the security operations center (SOC) team with the specific scope of the exclusion documented, so a future analyst reviewing an absence of alerts from that IP range would know it was a deliberate, scoped decision rather than a silently broken detection.
Trade-offs and pitfalls
Over-tuning by disabling a whole signature to fix one host's false positives is the single most common way tuning quietly erodes detection coverage; scoping the exclusion as narrowly as possible, and tracking every exclusion in a reviewable list, avoids that.
Discuss how TLS 1.3 changes the visible information in passive network monitoring compared to TLS 1.2. Which handshake elements remain observable, which are encrypted earlier in the exchange, and how would you adapt existing detection rules that relied on TLS 1.2 cleartext fields?
Sample Answer
Direct answer
TLS (Transport Layer Security) 1.3 encrypts almost the entire handshake starting right after the ServerHello, including the certificate exchange, so a passive monitor loses the certificate-based signals it relied on under TLS 1.2. What still travels in the clear is essentially just the ClientHello and the negotiation fields in the ServerHello: the SNI (Server Name Indication), the offered and chosen cipher suites, ALPN (Application-Layer Protocol Negotiation), and the key_share values. Detection has to shift from "read the certificate" to fingerprinting and metadata: SNI matching and JA3/JA3S fingerprinting, a technique that hashes the ordered list of fields each side offers during the handshake (cipher suites, extensions, elliptic curves) into a short string that tends to stay stable for a given TLS library and configuration, so it can substitute for the certificate detail the observer can no longer read.
Structured elaboration
| Handshake element | TLS 1.2 visibility to a passive tap | TLS 1.3 visibility to a passive tap |
|---|---|---|
| ClientHello (SNI, cipher list, extensions) | Cleartext | Cleartext, unless Encrypted Client Hello (ECH), an emerging extension, is deployed |
| ServerHello (chosen cipher, key material) | Cleartext | Cleartext, but the cipher list has shrunk to five AEAD suites, so it fingerprints less |
| Certificate message | Cleartext, subject/SAN/issuer are readable | Encrypted |
| CertificateVerify, Finished | Partially cleartext depending on the flight | Encrypted |
| Session resumption tickets | Session ID largely visible | Opaque PSK-based tickets, further hidden behind 0-RTT |
| Application data | Encrypted | Encrypted |
The reason for the shift is where key derivation happens. TLS 1.2 only derives session keys after the full Certificate, ServerKeyExchange, ServerHelloDone flow, so most of the handshake state travels before any encryption exists. TLS 1.3 derives handshake traffic keys as soon as the ServerHello's key_share is processed, so everything from EncryptedExtensions onward, including the certificate, is wrapped before it reaches the wire.
Adapting detection rules that relied on TLS 1.2 cleartext fields:
- Certificate-based rules stop working. A rule that matched a malicious command-and-control certificate's subject or a bank-impersonation SAN can no longer see the certificate at all. Replace it with SNI-based domain reputation and blocklists (works as long as ECH is not in use), and correlate the SNI plus destination IP against Certificate Transparency (CT) logs, the public append-only logs every publicly trusted certificate authority must submit issued certificates to, if you need to know after the fact which certificate a session likely used.
- JA3/JA3S fingerprinting still works, since it is computed from the ordered fields of the ClientHello and ServerHello, both still cleartext. JA3S (the server-side fingerprint) is less discriminating now than under TLS 1.2, because the negotiable parameter space genuinely shrank to five cipher suites, so pair it with extension order and the offered key_share group rather than trusting it alone.
- Rules that inspected content past the certificate message (a malware family's specific extension pattern, for example) have nothing left to match on the wire. Move to encrypted traffic analysis instead: packet size and timing sequences, TLS record size distribution, and JA3-family fingerprints, none of which require decrypting anything.
- Compliance or data-loss-prevention programs that depended on passive certificate inspection now generally need an active TLS-terminating forward proxy, an inline device that becomes the real TLS endpoint for outbound traffic and re-encrypts on to the actual destination, to regain that visibility. That is an architecture change with real trust and privacy trade-offs, not a rule tweak.
- Watch for ECH adoption. Once it is broadly deployed, SNI is wrapped too, and detection has to rely entirely on fingerprinting and behavioral signals with no cleartext domain name left at all.
Worked example
You can reproduce the visibility difference yourself without any special tooling. Capture traffic passively with tcpdump on a monitoring port (not as the client itself, which always has the keys) and open the file in Wireshark or tshark without supplying the server's private key. Filter for tls.handshake.type == 11, the Certificate handshake message type. Against a TLS 1.2 capture of a site, that filter matches, and the decoded pane shows the certificate's subject and issuer. Against a TLS 1.3 capture of the same site, the same filter returns nothing decodable: the record is present on the wire, but Wireshark can only label it as an encrypted handshake record, because the certificate now travels inside a channel the passive observer has no key for.
TLS 1.2 handshake, fields visible to a passive tap
ClientHello -> SNI=example.com, cipher list, extensions [CLEARTEXT]
ServerHello <- chosen cipher, TLS version [CLEARTEXT]
Certificate <- server cert chain (subject, SAN, issuer) [CLEARTEXT]
ServerKeyExchange, ServerHelloDone
ClientKeyExchange, ChangeCipherSpec, Finished
Application Data <-> encrypted
TLS 1.3 handshake, fields visible to the same tap
ClientHello -> SNI=example.com, cipher list, key_share [CLEARTEXT]
ServerHello <- chosen cipher, key_share [CLEARTEXT]
{EncryptedExtensions, Certificate, CertificateVerify, Finished} [ENCRYPTED]
Application Data <-> encrypted
Trade-offs and pitfalls
Do not overstate the change: TLS 1.3 removes certificate-based passive visibility, it does not defeat all passive inspection. Most deployments have not adopted ECH yet, so SNI-based rules and domain reputation lists still function normally. A common mistake is treating JA3 as a reliable allow/deny signal on its own: it fingerprints the TLS library and configuration, not the application, so many unrelated programs sharing a common HTTP client and TLS stack produce identical JA3 values, which makes JA3 far better as a clustering or anomaly-scoring input than as a standalone blocklist.
Write a Python script or clear pseudocode that processes Suricata EVE JSON alert logs (one JSON object per line) and prints the top 10 source IP addresses by alert count, excluding RFC1918 private addresses. The solution should handle very large files via streaming and avoid loading the entire file into memory.
Sample Answer
Approach
Suricata's EVE JSON (Extensible Event Format) log writes one JSON object per line, one line per event, such as alert, DNS lookup, or flow record, depending on configuration. To find the top 10 offending source IPs from potentially very large log files without loading the whole file into memory, stream the file line by line, parse each line independently, keep a running count per source IP in a counter, and only sort and rank at the very end.
import json
import ipaddress
from collections import Counter
RFC1918_NETS = [
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("172.16.0.0/12"),
ipaddress.ip_network("192.168.0.0/16"),
]
def is_rfc1918(ip_str):
try:
ip = ipaddress.ip_address(ip_str)
except ValueError:
return False
return any(ip in net for net in RFC1918_NETS)
def top_source_ips(path, top_n=10):
counts = Counter()
with open(path) as f:
for line in f:
line = line.strip()
if not line:
continue
try:
event = json.loads(line)
except json.JSONDecodeError:
continue
if event.get("event_type") != "alert":
continue
src_ip = event.get("src_ip")
if not src_ip or is_rfc1918(src_ip):
continue
counts[src_ip] += 1
return counts.most_common(top_n)
# Self-contained demo: write a small sample EVE JSON file, then run the function on it.
import tempfile, os
sample_lines = [
{"event_type": "alert", "src_ip": "203.0.113.9"},
{"event_type": "alert", "src_ip": "203.0.113.9"},
{"event_type": "alert", "src_ip": "198.51.100.14"},
{"event_type": "alert", "src_ip": "10.0.5.22"}, # RFC1918, excluded
{"event_type": "dns", "src_ip": "203.0.113.9"}, # not an alert, excluded
{"event_type": "alert", "src_ip": "203.0.113.9"},
{"event_type": "alert", "src_ip": "192.0.2.77"},
]
fd, path = tempfile.mkstemp(suffix=".json")
with os.fdopen(fd, "w") as f:
for rec in sample_lines:
f.write(json.dumps(rec) + "\n")
for ip, count in top_source_ips(path, top_n=10):
print(f"{ip}\t{count}")
os.remove(path)
Output:
203.0.113.9 3
198.51.100.14 1
192.0.2.77 1
Key points
for line in freads the file lazily, one line at a time, so memory use stays proportional to the number of distinct source IPs seen, bounded by the address space actually observed, not to the file size.- Filtering to
event_type == "alert"matters because a real EVE JSON file typically interleaves alert, DNS, flow, and other event types on one stream; this same script works whether the input is a pure alert log or a mixed EVE JSON file. - RFC1918, the reservation of 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 for private networks, is checked explicitly against those three network ranges, deliberately narrower than Python's built-in private-address check, which also excludes loopback, link-local, and other special-use ranges beyond RFC1918. Use the built-in check instead if that broader exclusion is what you actually want.
- A malformed line raising a JSON decode error is skipped rather than crashing the whole run, since one corrupted log line should not take down log processing for the rest of the file.
Complexity
Time is O(n) to stream and count n lines, plus O(u log 10) to extract the top 10 from u unique source IPs using the counter's built-in ranking, which is effectively O(u) for realistic u. Space is O(u), proportional to the number of distinct source IPs seen, not to the number of lines in the file.
Edge cases
- An empty file, or a file with no alert events at all, returns an empty list rather than erroring.
- A source IP that is present but malformed, not a valid IP string, is treated as non-private and would be counted; if that risk matters, validate the address up front with the same address-parsing call and skip invalid values.
- The same streaming pattern applies directly to a firewall log's denied-connection records instead of Suricata alerts, by swapping the event-type filter and field name for that log format; the counting and memory approach does not change.
Unlock Full Question Bank
Get access to all Network Security and Defense interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.