# What Is a Port Scan? How to Tell If It Signals an Attack, Check Your Logs, Defend Against It, and How It Differs From a Vulnerability Assessment

> What a port scan is, for people running servers or AWS workloads: why public IPs are probed constantly, telling scans, password guessing and intrusions apart, checking ss, ufw, VPC Flow Logs and GuardDuty, limiting exposure, and how it differs from a vulnerability assessment.

- Published: 2026-10-26
- Author: 友田 陽大
- Tags: セキュリティ, 脆弱性診断, AWS, インフラ, ネットワーク, ポートスキャン
- URL: https://tomodahinata.com/en/blog/port-scan-detection-prevention-vulnerability-assessment-guide
- Category: 実践ネットワーク攻撃と防御
- Pillar guide: https://tomodahinata.com/en/blog/network-penetration-testing-methodology-attack-defense-guide

## Key points

- A port scan is probing from the outside to find which ports are open and which services are running on them. Public addresses are probed all the time: in NICTER's 2025 observations, each monitored IP address received about 2.5 million packets over the year
- Being scanned is not itself an intrusion. What to look at is what responds, and signs of intrusion: a successful login from an unknown source, a 200 response to /.env, or your own server scanning others
- GuardDuty's Recon:EC2/PortProbeUnprotectedPort means 'a port you left open is being probed'; Recon:EC2/Portscan means 'your own instance is scanning others'. The latter should make you suspect a compromise
- You cannot stop scans, but you can narrow what responds. Deny inbound by default, never open databases or caches to 0.0.0.0/0, and make SSH key-only with restricted sources, or use Session Manager. Changing the port number only reduces noise, and a WAF only sees HTTP(S)
- A port scan answers 'what is open'; a vulnerability scan, 'are there known vulnerabilities'; a vulnerability assessment, 'has someone with expertise examined it for vulnerabilities'; and a penetration test, 'can an attacker actually achieve their goal'

---

**A port scan is probing a server from the outside to find which of its ports (the numbered entry points that accept connections) are open and which service is running on each.** IP addresses exposed to the internet are probed practically every day. In observations by Japan's National Institute of Information and Communications Technology (NICT), each monitored IP address received about 2.5 million packets in 2025, the most since observation began. **Being scanned is not, by itself, an intrusion.** What matters is what responds, and how it is protected.

Three things to do right now:

1. **Check what is responding.** Run `sudo ss -tulpn` on the server, and list the rules in your cloud configuration that are open to `0.0.0.0/0` (the steps are in [Check your exposure from the outside](#check-your-exposure-from-the-outside)).
2. **Close what does not need to be public.** Keep databases, caches and admin pages off the internet. Make SSH key-only and restrict where it can be reached from.
3. **At least check for signs of intrusion.** Look for successful SSH logins from unfamiliar sources (`Accepted` lines) and for 200 responses to requests for `/.env`. If you have GuardDuty enabled, `Recon:EC2/Portscan` (your server is scanning others) is also a reason to suspect a compromise.

This article is for people who run servers, VPSs, AWS environments or web services. How nmap decides a port's state is covered in [the article on how port scanning (nmap) works](/blog/network-reconnaissance-port-scanning-nmap-service-detection-defense-guide).

---

## What is a port scan? What is being probed

[SP 800-115](https://csrc.nist.gov/pubs/sp/800/115/final), the testing guide from the US National Institute of Standards and Technology (NIST), calls port scanning "network port and service identification": the work of identifying open ports, services such as HTTP, and the applications running them. The string a service returns when you connect (the banner) can sometimes reveal the application's version and even the OS.

### What is a TCP scan?

**A TCP scan works out, from how the target responds to TCP's connection handshake, whether a port is open, closed, or filtered (blocked, so its state cannot be determined).** The two most common are the SYN scan, which stops just before the connection is established (nmap's `-sS`; on Unix it requires root), and the connect scan, which has the OS complete the connection (`-sT`; no root needed, but more likely to show up in the target's logs) ([nmap's official reference](https://nmap.org/book/man-port-scanning-techniques.html)). For how to read the responses, see [the nmap article](/blog/network-reconnaissance-port-scanning-nmap-service-detection-defense-guide).

UDP (DNS on 53, SNMP on 161/162 and so on) is scanned with `-sU`. A port's state depends on where you scan from: the same port can look `open` from inside the same network and `filtered` across the internet. **You only know what is visible from outside by scanning from outside.**

## Why you get scanned every day: what observation data shows about constant probing

**Exposed devices are probed constantly, even when nobody in particular is targeting them.**

| Observation | Figure | Source |
|---|---|---|
| NICTER darknet observation (2025) | 2,504,680 packets per IP address per year, the most since observation began (2,427,977 in 2024) | [NICTER Observation Report 2025](https://csl.nict.go.jp/report/NICTER_report_2025.pdf) |
| Same, share of research scanning | About 55.0% of all packets (about 60.2% in 2024) | Same as above |
| National Police Agency sensors (2025) | 9,605.7 suspicious accesses per IP address per day (up 0.9% year on year) | [National Police Agency, 2025 report](https://www.npa.go.jp/publications/statistics/cybersecurity/data/R7/R07_cyber_jousei.pdf) |
| National Police Agency sensors (January to June 2026) | 13,687 per IP address per day (up 50.7% year on year) | [National Police Agency, first half of 2026](https://www.npa.go.jp/publications/statistics/cybersecurity/data/R8kami/R08_kami_cyber_jousei.pdf) |

A darknet is a block of IP addresses that is reachable but not in use. Nothing should be sent to it, so what arrives is scans, backscatter from DDoS attacks and the like. NICTER notes that this total is not the number of attacks against Japan as a whole, and about 55.0% of it was research scanning. Because traffic aimed at large numbers of unspecified devices makes up more than half of what it observed, Japan's National Police Agency says this reflects a reality in which devices and services exposed on the internet "operate on the premise that third parties are constantly probing them."

The source country or IP address may also not match where the sender actually is, for example because a compromised relay was used (National Police Agency). **Narrowing what responds on your side is more reliable than chasing scan sources.**

## What is in your logs? Telling scans, password guessing and signs of intrusion apart

**Most "suspicious access" is scanning or password guessing. What needs urgent action is signs of intrusion.**

| What you see | How it looks in the logs | What it means | What to do first |
|---|---|---|---|
| Port scan | Many destination ports from the same source in a short time (`DPT=` in block logs, `REJECT` in flow logs) | Someone is checking what is open | Check your open ports and close the ones you do not need |
| Password guessing against SSH or RDP | For SSH, long runs of `Failed password for invalid user …` or `Invalid user …` | Common usernames and passwords are being tried | Switch to key authentication and restrict sources |
| Web probing | Requests for `/wp-login.php` or `/.env` in the access log | Someone is looking for hidden admin pages or forgotten files | Check the status code of those requests. 404 or 403 means nothing was handed over. Also check that your admin page cannot be opened by anyone |
| Signs of intrusion | `Accepted …` (successful login) from an unfamiliar source, a 200 for requests to `/.env` or `/.git/`, a GuardDuty finding that your server is attacking others | You may already be compromised, or secrets may have leaked | Put investigation and isolation first. Treat the keys and passwords in that file as leaked and reissue them, and move the file out of the publicly served location |

Web probing corresponds to MITRE ATT&CK's "Wordlist Scanning (T1595.003)" and is a different thing from a port scan. VPC Flow Logs do not record URLs, so it never shows up there; look in your web server's or ALB's access logs instead (for an ALB, the `elb_status_code` and `target_status_code` fields). To check your own domain from outside:

```bash
# Replace example.com with your own domain. Prints the status code and the body size in bytes
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' https://example.com/.env
```

404 or 403 means nothing was handed over. A 200 that is the same size as your home page may just mean the site returns the same page for every URL, so open the response and check what it contains.

### Reading GuardDuty finding names

In [GuardDuty](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-ec2.html), the severity and meaning change depending on whether your instance is the target or the one doing it.

| Finding | Meaning | Default severity | Response |
|---|---|---|---|
| `Recon:EC2/PortProbeUnprotectedPort` | A port not blocked by any security group, ACL or host firewall is being probed by a known scanner | Low (High for 9200 / 9300). Not raised for 80 and 443 | Review whether it needs to be public. For 22 or 3389, restrict it to your organization's IP addresses |
| `UnauthorizedAccess:EC2/SSHBruteForce` (there is an RDP version too) | Brute force | Low when your instance is the target, High when it is the one doing it | If your instance is the one doing it, assume it is compromised |
| `Recon:EC2/Portscan` | **Your instance** is port-scanning external hosts | Medium | If it is not your own scanner, a compromise is likely |
| `Impact:EC2/PortSweep` | **Your instance** is probing the same port across many IP addresses | High | If unexpected, suspect a compromise |

EC2 findings sourced from flow logs do not cover IPv6 traffic, and SSH brute-force detection only looks at port 22. How to suppress findings raised by your own scanner is covered in [the article on GuardDuty suppression rules](/blog/aws-guardduty-suppression-rules-trusted-ip-threat-lists-tuning-guide).

When you suspect a compromise, [AWS's procedure](https://docs.aws.amazon.com/guardduty/latest/ug/compromised-ec2.html) is to check for malware and move the instance to an isolation security group that does not allow `0.0.0.0/0` (this does not cut connections that are already being tracked). If you cannot stop the activity, replace the instance. For the whole process, see [the incident response article](/blog/incident-response-nist-800-61r3-csirt-runbook-playbook-production-guide).

## How to check your logs for port scans

### Linux server: what is listening

```bash
# -t TCP / -u UDP / -l listening sockets only / -p process / -n don't resolve names
sudo ss -tulpn
```

Look at the listening address. `0.0.0.0` (all IPv4), `::` (all IPv6) and `*` (all interfaces) are reachable from outside unless a firewall stops them. `127.0.0.1` and `::1` are reachable only from inside the server. PostgreSQL listens only on localhost by default, and Redis's default configuration is `bind 127.0.0.1 -::1`, so if either shows `0.0.0.0`, confirm that the change was intentional.

If you use Docker, be careful. According to [the official documentation](https://docs.docker.com/engine/network/packet-filtering-firewalls/), traffic to published container ports is routed before ufw's rules apply, so ufw does not protect it. If a port only needs to be used from the host itself, publish it on a localhost address (as in the example on [the official port publishing page](https://docs.docker.com/engine/network/port-publishing/)).

```bash
# Publish so the port is reachable only from the Docker host (IPv4 and IPv6 localhost)
docker run -p 127.0.0.1:8080:80 -p '[::1]:8080:80' nginx
```

The same page warns, however, that in Docker versions before 28.0.0, devices on the same L2 segment (for example, machines on the same switch) can reach ports published to localhost.

### Firewall block logs (ufw)

On Ubuntu, ufw is **disabled out of the box**, so as shipped it writes no block logs either. Enabling it can drop your current SSH session, so if you are connected over SSH, allow SSH first.

```bash
sudo ufw status verbose     # "Status: inactive" means it is disabled
# Allow SSH first (198.51.100.7 stands in for your own source IP)
sudo ufw allow proto tcp from 198.51.100.7 to any port 22
# If your source IP changes, open it with rate limiting instead
# sudo ufw limit 22/tcp
sudo ufw enable
sudo ufw status verbose     # Confirm the rules you allowed are listed
```

With logging on, at the default level `low`, blocked packets are logged with a `[UFW BLOCK]` prefix, subject to rate limiting.

```bash
sudo ufw logging on
# Today's block log (recorded as kernel log messages)
sudo journalctl -k --since today --grep "UFW BLOCK"
# On systems that use rsyslog, it is also written to a dedicated file
sudo grep "UFW BLOCK" /var/log/ufw.log
```

A line looks like this (the values are illustrative, and some fields are omitted):

```text
[UFW BLOCK] IN=eth0 OUT= SRC=198.51.100.23 DST=203.0.113.10 PROTO=TCP SPT=40213 DPT=3389 SYN
```

Many lines from the same `SRC` (source) with different `DPT` (destination port) values in a short time mean a scan. A block line records traffic that arrived but was not let in; in itself, it is evidence that the block worked.

### SSH logs: attempts and successful logins

Where SSH authentication logs are written to a file, it is `/var/log/auth.log` on Debian and Ubuntu systems and `/var/log/secure` on RHEL and Fedora systems. From Debian 12 onward, rsyslog is not installed by default, so if there is no file, read the systemd journal instead (on Ubuntu the unit is named `ssh.service`).

```bash
# Failed attempts (mostly password brute forcing)
sudo grep -E "Failed|Invalid user" /var/log/auth.log | tail -n 20
# Successful logins. Check that none come from unfamiliar sources
sudo grep "Accepted" /var/log/auth.log
# Where there is no log file (Ubuntu)
sudo journalctl -u ssh.service --since yesterday --grep "Accepted"
```

OpenSSH records a successful login with a line beginning `Accepted publickey for deploy from 192.0.2.50 port 50022 ssh2` (the username and IP are examples). However many `Failed` lines there are, they are records of failure. **What deserves attention is an `Accepted` from a source you cannot account for.**

### AWS: REJECT in VPC Flow Logs, and GuardDuty

[VPC Flow Logs](https://docs.aws.amazon.com/vpc/latest/userguide/flow-log-records.html) record the IP traffic going to and from network interfaces, and **nothing is recorded unless you create them yourself**. They are collected outside the path of your traffic, so they do not slow it down. Below are the fields of the default format and [AWS's official example](https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-records-examples.html) (a record of rejected traffic to RDP on port 3389):

```text
version account-id interface-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status
2 123456789010 eni-1235b8ca123456789 172.31.9.69 172.31.9.12 49761 3389 6 20 4249 1418530010 1418530070 REJECT OK
```

`REJECT` covers, among other things, traffic that a security group or network ACL did not allow (`6` is TCP). If you send flow logs to CloudWatch Logs, Logs Insights can list the sources that hit the largest variety of rejected destination ports. I assembled the query below from the syntax in the official documentation; it has not been run against a real log group.

```text
filter action = "REJECT"
| stats count(*) as attempts, countDistinct(dstPort) as ports by srcAddr
| sort ports desc
| limit 20
```

By default, records are aggregated over an interval of up to 10 minutes (on Nitro-based instances, always 1 minute or less), and delivery to CloudWatch Logs typically takes about 5 minutes on a best-effort basis. They are not suited to real-time monitoring.

GuardDuty analyzes flow log data through its own independent stream, so you do not need to create flow logs for GuardDuty's sake. It does not show you those logs, though (for setup, see [the GuardDuty setup article](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide); for cost, [the cost article](/blog/aws-guardduty-cost-optimization-pricing-finops-guide)). Web probing such as `/wp-login.php` shows up in ALB access logs, which are disabled by default.

## Can you prevent port scans? What you can control is what responds

**You cannot stop scans from arriving.** What you can control is what responds when they do.

- [ ] **Deny inbound by default.** A newly created AWS security group has no inbound rules, so add only the ports you need. Lightsail, however, is managed with a per-instance firewall, and even OS-only images such as Ubuntu have SSH (22) and HTTP (80) open to all IP addresses right after creation. ufw is disabled out of the box, so enable it as shown above. GCP's default network comes with rules that allow SSH (22) and RDP (3389) from `0.0.0.0/0`, so delete or narrow them.
- [ ] **Never open databases or caches to `0.0.0.0/0`.** PostgreSQL (5432), MySQL (3306), Redis (6379), MongoDB (27017) and Elasticsearch/OpenSearch (9200 / 9300) only need to be reachable from your application servers. Redis's official documentation warns that an outside attacker can delete all of your data with a single `FLUSHALL` command. On RDS, turn off `PubliclyAccessible`.
- [ ] **Do not open SSH or RDP to the whole world.** Use key-only authentication and restrict sources, or switch to [Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html), which needs no inbound port (on GCP, IAP TCP forwarding).
- [ ] **Do not put admin pages where anyone can open them.** WordPress's official handbook also gives an example of allowing `wp-login.php` only from specific IP addresses.
- [ ] **Do not forget IPv6.** Review rules that allow `::/0` the same way. On Lightsail, the IPv4 and IPv6 firewalls are configured separately.
- [ ] **Keep whatever you leave open up to date.** If the banner reveals the version, attackers will look for known vulnerabilities in that version.
- [ ] **Rate-limit password attempts.** ufw's `limit` rule denies an IP address that tries to connect 6 or more times within 30 seconds. fail2ban blocks IP addresses with many failures for a set time, but says itself that it cannot remove the risk of weak authentication. From OpenSSH 9.8 onward, a feature that temporarily refuses sources that keep failing (PerSourcePenalties) is enabled by default.

Before changing SSH settings, check the values actually in effect. Upstream OpenSSH defaults to `PasswordAuthentication yes`, and distributions and cloud images sometimes ship a different value. On Ubuntu, files in `/etc/ssh/sshd_config.d/*.conf` are read before the main configuration, in lexical order of their names. For most keywords OpenSSH uses the first value it reads, so **if a file that comes earlier in name order sets the same keyword, your setting is ignored.** On cloud images, when cloud-init is configured to allow password authentication, it writes `PasswordAuthentication yes` to `50-cloud-init.conf`.

```bash
# Check the values in effect and which drop-in files exist
sudo sshd -T | grep -i -E "passwordauthentication|kbdinteractiveauthentication|permitrootlogin"
ls /etc/ssh/sshd_config.d/
```

```text
# /etc/ssh/sshd_config.d/00-disable-password.conf (pick a name that sorts before the existing files)
PasswordAuthentication no
KbdInteractiveAuthentication no
```

```bash
# -t checks the syntax only
sudo sshd -t
# -T shows the values in effect. Both should print no
sudo sshd -T | grep -i -E "passwordauthentication|kbdinteractiveauthentication"
# Restart (on Ubuntu)
sudo systemctl restart ssh.service
```

A mistake here can lock you out, so keep your current session open and confirm from another terminal that you can still log in with your key.

### Common misconceptions: changing the port, a WAF, blocking IPs

| Measure | Effect | Limits |
|---|---|---|
| Move SSH off port 22 | Less log noise | The replacement port gets scanned too (in NICTER's 2025 observations, 2222/TCP was the 10th most-targeted destination port of attack-related packets, excluding research scanning). GuardDuty's SSH brute-force finding also stops firing |
| Put a WAF in front | Can block HTTP(S) requests that match conditions (rules) you specify | AWS WAF sees the HTTP(S) requests that reach CloudFront, an ALB and similar resources. Connections to SSH or database ports are outside its scope; those are the job of security groups and firewalls. Attacks its rules cannot tell apart also get through |
| Block the scanning IP addresses | Traffic from those addresses stops | There are vast numbers of sources (NICTER attributed 17,348 IP addresses to known research organizations alone), and some are compromised relays |

These are covered in more depth in [the WAF defense-in-depth article](/blog/waf-defense-in-depth-aws-waf-cloud-armor-owasp-guide) and [the article on authentication vulnerabilities](/blog/authentication-vulnerabilities-brute-force-2fa-bypass-attack-guide).

## Check your exposure from the outside

### Set the scope first: scan only what you own

**Only port-scan assets you manage, or a scope you have written permission for.** [The official nmap guide](https://nmap.org/book/legal-issues.html) recommends getting written permission in advance, and notes that unauthorized scanning may breach your provider's terms of service.

Under [AWS's policy](https://aws.amazon.com/security/penetration-testing/), you can run security assessments, port scanning included, against permitted services such as EC2, RDS, CloudFront, Lambda and Lightsail without prior approval. DoS (including simulated DoS) and port, protocol or request flooding are prohibited, however. On any other VPS, check the provider's terms.

In Japan, Article 2, paragraph 4 of the [Act on Prohibition of Unauthorized Computer Access](https://laws.e-gov.go.jp/law/411AC0000000128) defines an act of unauthorized access as entering another person's ID and password, or information or commands that get around access controls, so as to make restricted functions usable. The definition is narrow, but that does not make it acceptable to probe other people's systems ([the article on ethical hacking and Japanese law](/blog/ethical-hacker-law-japan-unauthorized-access-act-active-cyber-defense-disclosure-guide)).

### Scan with nmap from an outside network

Scanning from inside the server makes ports look open even when they cannot be reached from outside. Scan only your own IP, from **outside the server**, such as your home connection.

```bash
# Your server's IP (203.0.113.10 is an example). With root, SYN-scan every port
sudo nmap -Pn -sS -p- 203.0.113.10 -oN outside-tcp.txt
# On a machine without root, use a connect scan
nmap -Pn -sT -p- 203.0.113.10 -oN outside-tcp.txt
# UDP needs root. Only the 100 most commonly used ports
sudo nmap -Pn -sU --top-ports 100 203.0.113.10
# If the server has an IPv6 address, scan it separately (2001:db8::10 is an example)
sudo nmap -6 -Pn -sS -p- 2001:db8::10
```

For production servers, I recommend the SYN scan. The official nmap guide recommends a SYN scan over a connect scan when you want to reduce the impact on fragile systems, because a connect scan completes the connection and can occasionally bring a service down.

`-Pn` skips the check for whether the host is up and always scans the IP you give it. Without further options, nmap scans only the 1,000 most commonly used ports, so add `-p-` to cover them all. Confirm for each `open` port that it really needs to be public; ideally, the ports you do not publish show as `filtered`.

If you want to check without sending any packets, use Shodan. It is a search engine for internet-connected devices: search for something like `ip:203.0.113.10` and you will see the services Shodan has found. Those results come from whenever Shodan last looked, though, so confirm the current state with nmap.

### List it from your cloud configuration: AWS CLI

```bash
# Inbound rules that allow 0.0.0.0/0 or ::/0 (run in each region)
aws ec2 describe-security-group-rules \
  --query 'SecurityGroupRules[?IsEgress==`false` && (CidrIpv4==`"0.0.0.0/0"` || CidrIpv6==`"::/0"`)].[GroupId,IpProtocol,FromPort,ToPort,CidrIpv4,CidrIpv6]' \
  --output table
# For Lightsail, check each instance (my-instance is an example)
aws lightsail get-instance-port-states --instance-name my-instance
```

I checked the security group query against sample data shaped like the official output example; it has not been run against a real account. A row whose `IpProtocol` is `-1` opens every protocol. In the Lightsail output, `cidrs` (IPv4) and `ipv6Cidrs` (IPv6) show the sources allowed to connect. For ongoing monitoring, Amazon Inspector checks every 12 hours which ports on your EC2 instances can be reached, and [Security Hub CSPM](https://docs.aws.amazon.com/securityhub/latest/userguide/ec2-controls.html) detects port 22 (EC2.13) or 3389 (EC2.14) open to everyone, and unrestricted access to high-risk ports (EC2.19).

## Port scan vs. vulnerability scan vs. vulnerability assessment vs. penetration test

**A port scan only answers "what is open". It does not tell you whether those services have weaknesses, or whether the application has flaws.** The table below follows NIST SP 800-115 and the definitions in METI's Information Security Service Standards (published in IPA's list of conforming services, on the pages for [vulnerability assessment services](https://www.ipa.go.jp/security/ug65p90000019fc0-att/20261009_2.pdf) and [penetration testing services](https://www.ipa.go.jp/security/ug65p90000019fc0-att/20261009_6.pdf)).

| Type | Question it answers | What it misses, and caveats |
|---|---|---|
| Port scan | Which ports are open and which services are running | Whether those services have weaknesses. Flaws inside the application |
| Vulnerability scan | Whether there are known vulnerabilities such as outdated versions, missing patches or misconfigurations | Many false positives. Weaknesses that only appear when attacks are combined |
| Vulnerability assessment | People with a defined level of vulnerability expertise examine web apps, platforms and smartphone apps for vulnerabilities | Anything outside the agreed scope |
| Penetration test | Whether, using the same techniques as an attacker, someone can get around security controls and achieve an objective | Anything outside the chosen scenarios. Testing can break systems |

- **A port scan is part of a vulnerability scan.** A vulnerability scan also identifies hosts and open ports before it assesses vulnerabilities.
- **A penetration test sits on top of a vulnerability assessment.** In IPA's list, it is positioned as an option of vulnerability assessment services.
- **Test from the inside too.** An external scan is stopped by perimeter devices and only sees the ports they let through. NIST SP 800-115 says internal testing often finds more vulnerabilities, and that both perspectives matter.

What follows is my own view. A manual web app assessment can verify authorization (who may see which data), which is hard to judge with tools alone. But tool-only assessments are sold under the same "vulnerability assessment" name, so check in the quote whether authorization testing is included. What goes into a web app assessment is covered in [the article on vulnerability assessment methods](/blog/web-application-vulnerability-assessment-owasp-zap-sast-dast-guide), and network penetration testing in [the network pentest article](/blog/network-penetration-testing-methodology-attack-defense-guide).

## When to call in a specialist, and what an audit checks

| Situation | What to ask for |
|---|---|
| A finding says your instance is the one doing it, or there is an `Accepted` you cannot account for | Incident response, before any assessment |
| You cannot work out for yourself what is exposed, or fully read your security groups | A cloud configuration review |
| You handle personal data, payments, or data belonging to multiple client companies | A web app vulnerability assessment that includes authorization testing (authorization flaws are invisible to a port scan) |
| You only have a production environment | Start with methods that do not touch production ([the article on assessing with only production](/blog/security-audit-production-only-no-staging-guide)) |

The [security audit](/security-audit) on this site is a review that reads your web service's source code and its AWS, GCP and Vercel configuration. It covers authentication and authorization, input and data handling, secrets, known vulnerabilities in dependencies, and deployment and cloud configuration. Scope and depth are agreed at the first consultation and put in writing, and configuration is checked with read-only access, without changing anything. It does not guarantee that you are "completely safe"; it targets known serious risks within the agreed scope. It is not an "information security audit service" in the sense of METI's standards (an independent verification, against a standard, of how controls are put in place and operated).

On a lumber-distribution B2B SaaS that won the METI Minister's Award, I ran four rounds of self-conducted audits, including penetration testing, and confirmed zero missing-authorization findings across all 221 endpoints using 15 real roles. That was application-layer testing; a port scan can only see as far as the entry points.

For typical costs and how to read a quote, see [the article on vulnerability assessment pricing](/blog/web-application-vulnerability-assessment-cost-pricing-guide).
