Registered Information Security Specialist Exam, Spring 2024 (Reiwa 6) Afternoon Q1 — JWT alg=none, API Authorization, and Interim WAF Mitigation
· Updated: · Go Komura · Registered Information Security Specialist, Registered Security Specialist, API, API Security, JWT, Authentication, Authorization, WAF, Log4Shell, Information Security, Vulnerability, IPA
Revision history (first version, published Aug 6, 2026)
- First published
Cite this article(DOI (registered archive): 10.5281/zenodo.22170855)
The DOIs below refer to previously archived versions and may not match the current text. Use this page’s URL to reference the current text.
Go Komura (2026). Registered Information Security Specialist Exam, Spring 2024 (Reiwa 6) Afternoon Q1 — JWT alg=none, API Authorization, and Interim WAF Mitigation. KomuraSoft LLC. https://comcomponent.com/en/blog/sc-exam-r6s-pm-q1-api-security/
- DOI (registered archive)
- 10.5281/zenodo.22170855
- DOI (last registered version)
- 10.5281/zenodo.22170856
“We verify the JWT, and yet an attacker can impersonate someone else.” “The JWT is perfectly valid, and yet an attacker can read someone else’s information.” The two sound alike, but you fix them in different places. The first is a problem of token verification; the second is a problem of authorization over the target data.
Question 1 of the afternoon session of the Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination takes an API called from a smartphone as its subject and asks you to tease these different checks apart. The first half covers the JWT, the user ID, the updatable fields, and the authentication code; the second half covers a library vulnerability and a WAF.1
This article builds on the official model answers and the grading commentary, and works through each item in the order “the key point of the answer, then the evidence in the question text, then the design you add in practice.” Read it keeping what you write within the exam’s character limit separate from what a real system actually needs.23
1. The Bottom Line First — Passing One Check Never Lets You Skip the Next
The principle running through this article is this: never treat the success of the previous check as a reason to skip the next trust boundary. A trust boundary is the line that decides on what conditions a value arriving from outside may be trusted.
Holding a valid JWT does not mean you may read someone else’s information. Being able to update your own information does not mean you may change the billing status as well. The authentication code’s expiry and the WAF each demand their own separate checks and operational practices.
A Map of the Answers
The table below gives the key points of the official answers. For the prose answers, check the character limit and the supporting evidence for each question in the relevant chapter.2
| Question and blank | Key point of the answer | Detailed explanation |
|---|---|---|
| Question 1, blank a | Stateless | Chapter 3 |
| Question 2(1), blank b | 500 seconds | Chapter 4 |
| Question 2(2) | Verify that the alg in the JWT header is not NONE | Chapter 5 |
| Question 2(3) | Verify that the user ID in the JWT matches mid | Chapter 6 |
| Question 2(4), blank c | Common module P | Chapter 7 |
| Question 2(5), blank d | Lock the account once consecutive failures exceed the threshold | Chapter 8 |
| Question 3(1) | Record and check accesses to index.html on the test server | Chapter 9 |
| Question 3(2), blanks e and f | Both are Header | Section 10.1 |
| Question 3(3) | A regular expression that matches both uppercase and lowercase | Section 10.2 |
| Question 3(4) | Prevent blocking from false positives and examine the alerts | Section 10.3 |
Start with Chapter 2 for the shape of the question, then read Chapters 3 through 10 in question order to follow the whole thing. How to write the answers is in Chapter 12, and the checklist for design and code review in practice is in Chapter 13.
In the diagram a solid line marks a relation that always holds and a dashed line marks a conditional one (the conditions are given per relation on the detail page). The full list of relations (18 in total, with evidence and certainty) and the definitions of the main concepts are collected on the knowledge map detail page (in Japanese). Data: JSON-LD / Turtle
2. The Shape of the Question — Read the Five Checks Separately
The setting is Company G, which is about to launch a new healthcare service. Users enter meals, weight and similar data from a smartphone app and receive health-risk assessments and meal-plan advice. The system is built on the cloud and combines an API gateway, event-driven processing, and a managed database.
Product and service names in the question text are abstracted. This article does not reproduce the figures and tables from the question booklet; it restates the structure you need in order to understand each question. Passages that give the key points of the official answers are kept distinct from the practical notes added here.1
flowchart TB
accTitle: Overview of the question
accDescr: Read the authentication code, the JWT, the target ID, the updated fields, and execution driven by external input as five separate checks.
A["User request"] --> B["Check the authentication code"]
B --> C["Issue and verify the JWT"]
C --> D["User API"]
D --> E["Authorization for the target mid"]
D --> F["Authorization for updating status"]
D -.-> G["Write external input to the log"]
G --> H["Vulnerable library"]
H --> I["External code execution via JNDI"]
Figure 1: From authentication through API operations to log processing, there is more than one place where a value gets trusted.
2.1. Separate Authentication, Token Verification, and Two Kinds of Authorization
Even after the authentication code has confirmed the person and the JWT has been issued and verified, authorization over the data and over the individual fields still remains. The path that writes external input to the log also needs a boundary that keeps input from being treated as an instruction.
[User ID and password]
|
v
[Check the 4-digit code] ---- no attempt limit ----> brute force
|
v
[Issue a JWT]
|
v
[JWT library] ------- allows alg=none ------> user ID tampering
|
v
[User API]
| |
| +-- passes status straight on -----> property-level authorization flaw
|
+-- trusts mid --------------------------------> object-level authorization flaw
[Write external input to the log]
|
v
[Vulnerable library] ---- JNDI/LDAP/HTTP ------> external code execution
The most important thing here is the following distinction.
| Check | What it asks | How it was broken in this question |
|---|---|---|
| Authentication | Who are you | Brute-forcing the 4-digit code |
| Token verification | Has that identity information been tampered with | alg=none |
| Object-level authorization | May you access that user’s data | Swapping mid |
| Property-level authorization | May you change that field | status=paid |
| The boundary from input to execution | Can external input be interpreted as an instruction | JNDI Lookup |
Passing the previous check is never a reason to skip the next one. A user holding a valid JWT is not necessarily allowed to read another user’s data. A user who can update their own data is not necessarily allowed to change the billing status too.
Once you can separate these stages, the answer to each question stops being something you memorize.
flowchart TB
accTitle: The difference between authentication and authorization
accDescr: Confirming the subject is one thing, and checking that the subject may operate on the target data or field is a separate check.
A["Authentication and token verification"] --> B["Establish whose request this is"]
B --> C["May it reach that data"]
C --> D["May it change that field"]
Figure 2: The difference between authentication and authorization. Authentication comes first, and authorization is a separate check.
2.2. Tell the Parts of Question 2 Apart by the Value the Attacker Changed
The points in Question 2 that are easiest to confuse sort themselves out once you look at the value the attacker controlled.
| Attack | Value the attacker changed | What should not have been trusted | Root fix |
|---|---|---|---|
| JWT tampering | The alg in the JWT header and the user ID in the payload |
The verification algorithm the token declares for itself | Fix the allowed algorithms on the server side |
| Reading another user’s information | The mid in the request |
The target ID the client supplied | Match it against the JWT’s subject, or derive the target ID from the JWT |
| Turning into a paid user | An undocumented status |
Every property that was auto-bound | Put the updatable properties on an allowlist |
| Breaking the 4-digit code | Candidate values for otp |
Unlimited authentication attempts | Add a failure-count limit, delays, and risk assessment |
It matters that you do not lump all of these together as “validate the input.”
algis a policy for cryptographic processingmidis object-level authorizationstatusis property-level authorizationotpis resistance to online guessing
Even though they all sit inside the same HTTP request, the reason each needs protecting is different.
3. Question 1 — Stateless Still Keeps Business Data and Failure Counts
Question 1 asks about one of the design principles of a RESTful API: the property of not performing session management.
Answer: blank a is “stateless.”2
3.1. The Evidence — Each Request Alone Carries Everything Needed to Process It
Stateless means that each request on its own carries the information needed to process it, so the server does not have to remember the conversational state of the previous request. In this question, the smartphone app attaches a JWT to the Authorization header of every request. The server verifies that JWT and identifies the user for that request.
3.2. An Easy Misreading — It Does Not Mean Throwing Away Data or Failure Counts
The easy mistake is to read “stateless” as “the server holds no state at all.” In reality it quite normally holds the following.
- The database that stores user information and health data
- The billing status
- The authentication code’s value, its expiry, and the failure count
- The JWT signing key
- The revocation information, if the design uses a revocation list
- Logs and audit records
What it gives up is making every API call depend on server-side session state that exists only to keep a conversation going.
Being stateless also does not automatically improve security. Sending the JWT every time makes horizontal scaling easier, but if JWT verification is wrong, that mistake spreads uniformly to every node as well. An architectural property and security correctness are separate things.
flowchart TB
accTitle: Stateless and the state that is stored
accDescr: Processing each request without depending on conversation history still stores state such as business data and failure counts.
A["Request with a JWT"] --> B["Identify the user from that request alone"]
B --> C["Process it and respond"]
D["User information, billing, failure count"] -.-> C
C -.-> E["No dependence on the previous conversation"]
Figure 3: Removing dependence on the conversation and storing business and security state are perfectly compatible.
4. Question 2(1) — Calculating the Average Time to Break a 4-Digit Code
Answer: blank b is “500.” The unit is seconds.2
4.1. The Evidence — Line Up the Candidate Space, the Attempt Rate, and the Validity Period
If the user ID and password match, the authentication API emails a four-digit number. After that, if the user ID and the four-digit code match, it issues a JWT. The code is valid for 10 minutes from the moment it is generated.
In the assessment, 10 attempts per second were possible. The question asks how many seconds it takes on average to break the code.
The Calculation Uses Half the Candidate Space
A four-digit number, counting leading zeros, has the following 10,000 possibilities.
0000, 0001, 0002, ... , 9999
When the correct value is chosen uniformly at random and candidates are tried in order without repeating, the exam calculates the average number of attempts as half the candidate space.
Average attempts = 10,000 / 2 = 5,000 attempts
Average time = 5,000 / 10 attempts per second = 500 seconds
That approximation gives the official answer of 500 seconds. Strictly averaging over attempts 1 through 10,000 gives 5,000.5 attempts, but here we follow the exam’s answer.
At worst it takes 1,000 seconds, but what the question asks for is the average. And the code’s validity period is 600 seconds, longer than the 500-second average time to break it. That is why it was judged likely to be broken.
flowchart TB
accTitle: A sense of scale for the 4-digit authentication code
accDescr: The exam's approximation gives an average of 500 seconds, and non-repeating attempts cover 60 percent of the candidates within the 600-second validity period.
A["10000 candidates"] --> B["About 5000 attempts on average"]
B --> C["About 500 seconds at 10 per second"]
C --> D["Shorter than the 600-second validity"]
D -.-> E["6000 candidates checked in 600 seconds"]
Figure 4: A sense of scale for the 4-digit authentication code. Trying half the candidate space on average lands a hit inside the validity period.
4.2. A Practical Note — Look at the Number of Attempts, Not Just the Expiry
The strength of an authentication code is decided neither by its length alone nor by its validity period alone.
Attempts possible during the validity period
= attempts per second x validity period
= 10 x 600
= 6,000 attempts
Trying non-repeating values in order covers 60% of the 10,000 possibilities within the validity period. Setting an expiry is not enough if the number of attempts is not limited.
The current NIST SP 800-63B requires at least six digits for the short-lived secret used in out-of-band authentication, and makes a limit on the number of attempts mandatory when the secret has less than 64 bits of entropy. It also says not to use email for out-of-band authentication4. On the exam you answer within the given specification of four digits delivered by email, but in a new design in practice you should revisit that premise itself.
5. Question 2(2) — Do Not Let the Attacker Choose How the JWT Is Verified
The key point of the answer
The question asks, in no more than 20 characters each, against which data the fixed library Q performs verification, and what that verification is.
The model answer is as follows.
| Item | Key point of the answer |
|---|---|
| Data to verify | The value specified in alg inside the JWT header |
| Content of the verification | Verify that it is not NONE |
This is the direct fix for the vulnerability in the question text.2 Answering only “verify the signature” restates processing that is already implemented. The grading commentary points out this kind of wrong answer as well.3
5.1. The Evidence — The Existing Signature Verification Chooses Wrongly
The JWT in this question consists of three parts: a header, a payload, and a signature.
base64url(header).base64url(payload).base64url(signature)
The header recorded RS256 as the algorithm used for the signature. The payload holds the user ID, the issue time, and the expiry.
The assessor changed the following two things.
- Changed the header’s
algfromRS256toNONE - Changed the payload’s user ID to a different user
Sending that JWT passed verification and impersonated the other user.
flowchart TB
accTitle: Flow of the JWT alg=none attack
accDescr: When the attacker changes both the verification method and the user ID, a library that permits none accepts the tampered JWT.
A["Obtain a valid JWT"] --> B["Change alg to none"]
B --> C["Change user to a different user"]
C --> D["The library skips signature verification"]
D --> E["Accepted as the other user"]
Figure 5: Flow of the JWT alg=none attack. The attacker is choosing the verification algorithm.
none Is Not a Value That the Specification Omits
RFC 7519 defines the “Unsecured JWT,” a JWT with neither a signature nor encryption, whose alg is none5. So it is not that the value none is absent from the specification altogether.
The problem is that an API that should accept only signed JWTs accepted the none the attacker specified.
Written out conceptually, the vulnerable processing looks like this.
1. Read the JWT header
2. Look at the alg written in the header and choose the verification method
3. If alg is none, skip signature verification
4. Trust the user ID in the payload
Input the attacker controls is being allowed to select the strength of the security itself.
5.2. A Practical Note — Fix the Allowed Algorithms on the Server Side
Here you need to keep the exam’s answer and the practical recommendation apart.
RFC 8725 states that a JWT library must let the caller specify the set of allowed algorithms and must not use anything outside that set6. In other words, the thinking goes like this.
Bad thinking:
accept if token.header.alg != "none"
Good thinking:
accept only if it is in serverConfig.allowedAlgorithms
example: allowedAlgorithms = ["RS256"]
Rejecting only none can still leave other weak algorithms, or algorithm confusion in which a public-key scheme and a shared-key scheme are mistaken for each other. The principle is not to pile up negative conditions for acceptance, but to fix a narrow set of conditions for permission.
When verifying a JWT, check at least the following as appropriate to the use case, not just the algorithm.
| Item | What to check |
|---|---|
| Signature | Does it verify with the expected key and algorithm |
iss |
Is the issuer one you trust |
aud |
Was the token issued for this API |
exp |
Is it within its expiry |
nbf |
Is it not earlier than the time it becomes usable |
sub or the user ID |
Is it a valid subject within the application |
| Token type | Have an ID token and an access token been mixed up |
In this question the payload’s key name is user, but in practice you either use the standard sub or define the meaning of your custom claim clearly.
flowchart TB
accTitle: Safe and dangerous JWT verification
accDescr: Do not let the token decide the verification method; check the signature and each claim only after the server-side permission condition is met.
A["Receive a JWT"] --> B{"Is it an alg the server allows"}
B -->|"No"| X["Reject"]
B -->|"Yes"| C["Verify the signature and each claim"]
C --> D["Treat it as a verified subject"]
Y["Dangerous processing"] -.-> Z["Skip verification for the declared none"]
Figure 6: Safe verification and dangerous verification. In practice, fix a narrow set of permitted algorithms.
5.3. Separate Signing From Secrecy — base64url Is Not Encryption
There is another misunderstanding that comes up often with JWTs. The header and payload are expressed in base64url, but that is not encryption. Anyone can decode and read them.
What the signature guarantees, and only when it verifies correctly, is that the contents have not been tampered with since they were issued. It does not mean that personal information you want kept secret may be placed in a signed JWT’s payload.
flowchart TB
accTitle: Signing and secrecy are separate in a JWT
accDescr: base64url is a representation that third parties can read, so tamper detection by the signature and confidentiality are separate concerns.
A["Signed JWT"] --> B["Read the header and payload"]
B --> C["Decode the base64url"]
C --> D["The contents are not secret"]
A --> E["Verify the signature correctly"]
E --> F["Confirm it has not been tampered with"]
Figure 7: A signature is for detecting tampering, not a feature that hides anything from view.
6. Question 2(3) — A Valid JWT and a Valid Target Are Different Things
The key point of the answer
Underline (2) in Table 5 asks, in no more than 40 characters, what processing to add to the code that calls common module P.
The model answer is as follows.2
Processing that verifies whether the user ID contained in the JWT matches the value of mid
The place the question specifies for the addition is not common module P itself but the “P call processing” that invokes P. That is where the check that the JWT and mid agree is added.1
In practice the aim is to perform authorization consistently in the shared layer that reaches the data, P included. That makes it easier to apply the same authorization to both GET and PUT, and to any other API that comes to use P later. Merely copying the comparison into every screen or endpoint leaves places where it is left out.
6.1. The Evidence — The JWT’s Subject and the Target mid Arrive Separately
Next comes an attack that does not tamper with the JWT itself.
The user API receives a user ID called mid on GET or PUT. Common module P fetches and updates the user information tied to that mid in the database.
The shape of the attack is simple.
User ID in the JWT: user01 <- a correctly signed JWT
mid in the request: user02 <- changed by the attacker
The JWT’s signature is valid, so authentication succeeds. But the API trusts mid=user02 as given and returns user02’s information.
This is a textbook case of what the OWASP API Security Top 10 2023 calls Broken Object Level Authorization (BOLA). Whenever data is accessed using an object ID the user supplied, authorization for that object has to be checked every time7.
flowchart TB
accTitle: The BOLA attack
accDescr: The subject of the valid JWT and the request mid differ, yet only mid is used and another user's information is returned.
A["JWT with a valid signature (user01)"] --> C["User API"]
B["Changed mid (user02)"] --> C
C --> D["No comparison against the subject"]
D --> E["Fetch and update user02's data"]
Figure 8: The BOLA attack. Authentication passes, but authorization is never checked.
6.2. A Practical Note — A Self-Only API Should Not Accept mid
An API that only fetches and updates the caller’s own information has no need to accept a user ID from the client.
GET /users/me
Authorization: Bearer <JWT>
On the server side, take the subject from the verified JWT.
principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)
Updates work the same way.
principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
userId = principal.subject,
name = input.name,
age = input.age
)
A comparison protects you as long as you write it. But a design that does not accept the target ID from outside removes the whole class of bugs where you forget to write that comparison.
If an administrator has to operate on another user’s information, split the APIs like this.
PUT /users/me for ordinary users
PUT /admin/users/{userId} for administrators
The administrator version requires a different permission, an audit log, and re-authentication where needed. That makes the boundary of the authorization policy far easier to see than “adding an exception to the ordinary-user API just for administrators.”
flowchart TB
accTitle: How to prevent BOLA
accDescr: Either check that the verified subject matches mid, or derive the target from the subject in a self-only API so the comparison cannot be forgotten.
A["Subject of the verified JWT"] --> B{"How the target ID is decided"}
B -->|"mid is received"| C{"Does it match the subject"}
C -->|"Yes"| D["Operate on your own data"]
C -->|"No"| E["Reject"]
B -->|"Self-only API"| F["Derive the target ID from the subject"]
F --> D
Figure 9: How to prevent BOLA. Either do not accept mid, or compare it against the JWT’s subject.
6.3. A Recap — Who You Are and What You May Do Are Separate
On the exam and in practice alike, this phrasing helps.
- Authentication: who you are
- Authorization: what that person may do
Successful JWT signature verification gets you only as far as “the subject this token represents can be trusted.” That “this subject may read user02” has to be checked separately.
7. Question 2(4) — Accept Only the Fields That May Be Updated
Answer: blank c is “common module P.”2
7.1. The Evidence — Where the Undocumented status Was Passed
The user API’s specification defines the following update parameters.
mid user ID
name name
age age
The assessor, however, added the following value, which is not in the specification.
status=paid
With that, a free-tier user’s status changed to a paid user.
According to the question text, service L did not validate the parameters it received and passed all of them to common module P. P was built so that it could update the database with them as they were.
So the destination that received the undocumented value and was able to update even the user status is common module P.
flowchart TB
accTitle: Mass Assignment
accDescr: Passing an undocumented status to the common module and updating with it lets a user change their own billing state.
A["The spec is mid, name and age"] --> B["Add status=paid"]
B --> C["Pass every parameter to P"]
C --> D["Applied wholesale to the internal data"]
D --> E["Billing status becomes paid"]
Figure 10: Mass Assignment. An undocumented property is applied wholesale to the internal object.
7.2. How It Differs From BOLA — Protecting the Fields Inside, Not the Target
Swapping mid in the previous chapter and adding status here look alike, but the granularity being protected differs.
| Vulnerability | What the attacker changes | What should have been checked |
|---|---|---|
Swapping mid |
The target object | May this user access this user record |
Adding status |
A property inside the object | May this user change this field |
The OWASP API Security Top 10 2023 treats the latter as Broken Object Property Level Authorization and folds the former Mass Assignment category into it8.
7.3. A Practical Note — Make the Update Input Type an Allowlist
Conceptually, the vulnerable implementation looks like this.
entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)
Even if the screen only has input fields for name and age, an attacker can craft the HTTP request directly. A field being absent from the UI is not a security boundary.
A safe implementation states the updatable fields explicitly.
input = parseExactSchema(body, fields = ["name", "age"])
entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age = input.age
repository.save(entity)
Two things matter here.
- Put only the fields the user may change into the update input type
- Rather than ignoring unknown fields outside the specification, reject them as an error where you can
Silently ignoring unknown fields does hide the fact that an attack failed, but it also means missing client implementation mistakes and signs of an attack. Unless compatibility requires otherwise, rejecting them with a strict schema makes investigation easier.
flowchart TB
accTitle: Field-level authorization
accDescr: Limit the update input type to name and age, and update billing status through a dedicated path from a verified payment notification.
A["Profile update request"] --> B{"Only name and age"}
B -->|"Yes"| C["Update only the permitted fields"]
B -->|"No"| D["Reject the unknown field"]
E["Verified payment notification"] --> F["Match paymentId and prevent duplicates"]
F --> G["Update status through a dedicated path"]
Figure 11: Field-level authorization. Restrict the updatable fields with an allowlist and change billing status through a separate path.
7.4. Change the Billing Status Only From a Verified Payment Result
status=paid is not part of the user profile. It is a state derived from a server-side fact: that a payment succeeded.
User profile update
-> only name / age may be changed
Verified notification from the payment service
-> match paymentId
-> prevent duplicate processing
-> change status to paid
Even when they are stored in the same database column, the permission and the path for changing them are separate. Using an internal entity directly as an external API’s input type erases that boundary.
7.5. Shared Components Should Include Authorization and Input Restriction
Two shared components appear in this question: the JWT management library Q and common module P.
Shared components bring real advantages.
- Fixing one place applies the fix to every API that uses it
- Authorization and validation do not have to be duplicated in each feature
- Testing can be concentrated in one place
- Log and audit formats can be made uniform
On the other hand, mistakes spread just as widely.
- If library Q accepts
alg=none, every API that uses a JWT becomes dangerous - If common module P accepts an arbitrary
midorstatus, both GET and PUT become dangerous - If the vulnerable library H is used in the platform, every path that writes HTTP headers to the log becomes attack surface
So what you should share is not merely data access. You need to share the security invariants and test that shared component rigorously on its own.
For example, make P’s contract look like this.
P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})
It is safer not to expose low-level APIs like the following to ordinary callers as they are.
P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)
The latter are needed only on limited paths such as administrative processing. Handing that low-level freedom to every API produces a design that expects every caller to use it correctly every single time.
flowchart TB
accTitle: Narrow the contract of shared components
accDescr: Putting authorization and the input type into the contract of a shared component reduces reliance on every caller using it correctly.
A["API for ordinary users"] --> B["Verified subject and update DTO"]
B --> C["Authorization unified in the shared component"]
C --> D["Operate on permitted data"]
E["Operations with an arbitrary ID or map"] -.-> F["Split off to limited paths such as administration"]
C -.-> G["Test the shared component rigorously"]
Figure 12: What you share is not only data access but the authorization and input conditions that must hold.
8. Question 2(5) — Count the Failures and Stop the Brute Force
Against brute-forcing the four-digit code, you answer in no more than 30 characters what processing goes into blank d of Table 5. The threshold is 10.
The model answer is as follows.2
Processing that locks the account once the number of consecutive failures exceeds the threshold
8.1. The Evidence — The Failure Count Is State That Security Decisions Need
This does not contradict the statelessness of Question 1. Not holding the conversational state of API calls as a server session is one thing; persisting the failure count that a security decision requires is another.
flowchart TB
accTitle: With and without a limit on attempts
accDescr: Keeping the failure count and locking once the threshold is exceeded stops unlimited online guessing.
A["Code check fails"] --> B["Update the account's failure count"]
B --> C{"Has the threshold been exceeded"}
C -->|"Yes"| D["Lock the account"]
C -->|"No"| E["Allow the remaining attempts"]
F["Without a limit the attacker keeps trying"] -.-> A
Figure 13: With and without a limit on attempts. A failure-count limit stops brute force in practical terms.
8.2. A Practical Note — Prepare for Lockouts Too
A per-account limit on attempts is necessary, but if an attacker knows someone else’s user ID they can fail on purpose until the threshold is exceeded and lock the legitimate user out. In practice, therefore, you combine the following.
| Control | Role |
|---|---|
| Per-account failure count | Stops brute-forcing a single account |
| Graduated wait times | Tolerates typing mistakes by legitimate users while slowing the attack down |
| Controls on source IP, device, ASN and the like | Curbs attacks that try a few attempts each against many accounts |
| Risk-based assessment | Applies tighter restrictions to an unusual region, device or rate |
| Notifying the user | Lets them notice an attack or a mistake |
| A safe recovery procedure | Keeps the unlock channel from becoming an attack path |
Also, do not reset the failure count to 0 when the code is resent. Otherwise the attacker restores the attempt budget every time they call the resend API. The current NIST SP 800-63B likewise requires that the failure count not be reset when a new authentication secret is generated4.
8.3. Protect Reissue, Reuse, and the Send API as One Set
The question text centers on the expiry, but in practice you also need the following.
- Invalidate a code immediately once it succeeds
- Reject reuse of the same code
- Never write the code itself to the log
- Make the response such that whether a user exists cannot be inferred from whether the code check succeeded
- Put a limit on attempts on the code-sending API as well
As long as you use a short secret, you cannot leave security to random generation alone.
flowchart TB
accTitle: Countermeasures for authentication codes
accDescr: On top of the candidate space and the expiry, keep the attempt count, invalidate after success, and combine notification with a recovery procedure.
A["Issue or reissue a code"] --> B["Do not reset the failure count"]
B --> C["Check it with limits on count and rate"]
C --> D{"Did the check succeed"}
D -->|"Yes"| E["Invalidate the code immediately"]
D -->|"No"| F["Record the failure, delay, lock"]
F --> G["Notification and a safe recovery procedure"]
A -.-> H["Limit the send API and never log the code"]
Figure 14: Countermeasures for authentication codes. Combine attempt control and operations with the digit count and the expiry.
9. Question 3(1) — Confirm the Verification Command Ran From the Access Log
The key point of the answer
Question 3(1) asks what you have to implement on the test server in order to confirm that the command ran.
The model answer is as follows.
A mechanism that records and checks accesses to index.html on the test server2
9.1. The Evidence — Observe That the Chain Reached the Final Verification Command
After the service launches, a critical vulnerability V is disclosed in the widely used open-source library H. The question text lays out the following sequence.
- The attacker sends a string containing a JNDI Lookup inside an HTTP header
- The target server writes that value to its log
- The vulnerable library evaluates the JNDI Lookup and queries the attacker’s LDAP server
- The LDAP response returns the URL of the attacker’s HTTP server
- The target server fetches the class file and executes a command
This reads as a Log4Shell (CVE-2021-44228) style attack with the proper names withheld. Apache’s own description also explains it as a vulnerability where an attacker who can control log messages or their parameters can execute arbitrary code loaded from an LDAP server9.
flowchart TB
accTitle: Confirmation flow for a Log4Shell-style vulnerability
accDescr: Beyond the JNDI reference and class retrieval, confirm in the log that the verification command fetched index.html.
A["External input in an HTTP header"] --> B["Log processing on the target server"]
B --> C["Query LDAP over JNDI"]
C --> D["Receive the URL to fetch the class from"]
D --> E["The target server fetches the class"]
E --> F["The target server runs the verification command"]
F --> G["index.html is fetched from the test server"]
G --> H["Record and check that access"]
Figure 15: Confirmation flow for a Log4Shell-style vulnerability. Reachability is confirmed by a recorded HTTP access, not by a destructive command.
All the Verification Command Causes Is a Harmless HTTP Access
Company G runs verification code that has no effect on the system, to confirm whether vulnerability V can be exploited from outside. The only instruction the verification code issues is to fetch index.html from the test server.
If a GET from the target server shows up in the web server’s access log, you can confirm that at least the following chain went through.
External HTTP request
-> log processing
-> JNDI Lookup
-> LDAP response
-> class retrieval
-> verification command execution
-> HTTP access to the test server
9.2. Why the Test Server’s Log Rather Than Something on Screen
The target is a server. Nothing necessarily changes on the user’s browser screen. And even when the vulnerability is present, the outbound traffic along the way may be stopped by a firewall.
Recording the access on the test server side gives you observable evidence that traffic reached the outside from the target server.
9.3. A Practical Note — Get Approval and Confirm With the Smallest Side Effect
When you run this kind of verification in practice, always observe the following.
- Obtain explicit approval from the owner of the target system
- Choose a verification method with no impact on production, or one whose impact is acceptable
- Do not use destructive instructions such as writes, deletions or configuration changes
- Keep the verification domain and server under your own control
- Record the time of the verification, the source, the target, and the callback you expect
- Tear down the temporary LDAP and HTTP servers and any credentials afterwards
Confirming that arbitrary code execution is possible is not the same as executing arbitrary dangerous code. Keep the side effect to the minimum that meets the goal.
flowchart TB
accTitle: Keep verification to the smallest side effect
accDescr: Get approval, decide the harmless method and what will be observed, and tear down the verification environment and credentials afterwards.
A["Explicit approval from the owner"] --> B["Decide the impact and what to observe"]
B --> C["Record the access on a server you control"]
C --> D["Match it against the expected time and source"]
D --> E["Tear down the environment and credentials"]
B -.-> F["No writes, deletions or configuration changes"]
Figure 16: Decide first what fact you want to observe, then make the harmless check and the cleanup part of the procedure.
10. Questions 3(2) to (4) — Decide Where the WAF Looks, What It Matches, and How It Acts
10.1. Question 3(2) — Both Inspection Targets Are Header
The WAF in service N lets you choose GET, POST, PUT, ANY, Header, COOKIE or Multipart as the inspection target.
The attack code goes into the value of an HTTP header called x-api-version. So blanks e and f in Table 6 are both Header.2
The Evidence Is Where the Attack String Sits
This is less a matter of general knowledge than of reading the data flow in the question text.
Where the attack string is placed:
the x-api-version header
|
v
WAF inspection target:
Header
ANY in this question means inspecting the parameter values of every method, which is different from Header, which inspects headers.1
It is not a GET parameter and not a POST body. Rather than looking at the WAF’s feature list and picking ANY because it looks like an attack, answer with the place the question text says the attacker put the value.
flowchart TB
accTitle: Choosing where the WAF inspects
accDescr: Decide the WAF inspection target from where the question text stores the attack string, not from the name of the attack.
A["Read where the attack string is stored"] --> B["The x-api-version header"]
B --> C["The inspection target is Header"]
C --> D["On to a pattern that covers letter case"]
E["ANY covers parameters of every method"] -.-> C
Figure 17: Do not guess from the attack’s name; map the storage location, a header, onto the inspection target.
10.2. Question 3(3) — Allow Both Uppercase and Lowercase for Each Character
The first proposal was, conceptually, the following rules.
Header \Wjndi\W block
Header \Wldap\W block
But swapping the case, as in jNdI, evades a simple lowercase-only pattern.
The model answer to Question 3(3) is either of the following.2
\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W
In the question booklet the backslash may look like a yen sign because of the glyph used in a Japanese environment, but as a regular expression it is \W. \W matches any character other than a letter, a digit or an underscore. In JNDI Lookup syntax, non-word characters such as ${ and : appear before and after jndi, so the pattern takes them in.
The same idea turns the ldap side into a form that ignores letter case.
\W[lL][dD][aA][pP]\W
10.3. Question 3(4) — Detection Exists to Avoid Blocking by Mistake
For the revised WAF rule, Mr. Z, a Registered Information Security Specialist, advises setting the action to “detect” rather than “block” for a period after production operation begins.
The question asks, in no more than 25 characters each, for the benefit of choosing detect and for what should be done to minimize the damage.
The model answer is as follows.2
| Item | Key point of the answer |
|---|---|
| Benefit | It can prevent blocking caused by a false positive |
| What should be done | Examine whether it is an attack whenever an alert is received |
The Evidence — Detection Comes as a Set With Examining the Alerts
In detect mode, traffic that matches a rule is let through while being logged and alerted on. Even when a legitimate API call happens to contain the string jndi or ldap, business does not stop dead.
In exchange, operations needs the following.
Alert received
|
v
Inspect the request in question
|
+-- legitimate traffic -> narrow the rule, consider an exception
|
+-- attack -> isolate the target, preserve logs, assess impact, move to blocking
If nobody looks at the alerts, detect mode provides no protection at all. Detection comes as a set with the operational practice of observing and deciding.
10.4. A Practical Note — Observe, Tune, Then Move to Blocking
A typical rollout goes like this.
- Apply it to real traffic in detect mode
- Classify the hits into false positives and true positives
- Tune the target headers, paths, APIs, character boundaries and so on
- Confirm that the impact on legitimate traffic is acceptable
- Move to blocking mode
- Monitor the number of blocks and the business impact
This is the principle for normal times, though. When the vulnerability is critical, is actually being exploited, and there is no alternative, you may judge that a breach would cost more than an outage caused by a false positive and block from the start. In the exam’s situation, detect is chosen first in order to confirm that the service can still be used as before.
flowchart TB
accTitle: From WAF detection to blocking
accDescr: Examine the alerts raised during detection, tune away false positives and deal with attacks, then keep monitoring after moving to blocking.
A["Observe traffic in detect mode"] --> B{"What does the alert show"}
B -->|"False positive"| C["Tune the rule or the exception"]
C --> A
B -->|"Attack"| D["Isolate, preserve logs, assess impact"]
D --> E["Move to blocking"]
A --> F["Confirm the impact on legitimate traffic"]
F --> E
E --> G["Monitor blocks and business impact"]
Figure 18: From detection to blocking. Observe and tune first, confirm the impact is acceptable, then move to blocking.
11. Handling the Vulnerability in Practice — Buy Time With the WAF While Updating and Investigating
In the question text, library H’s official site has neither a fixed version nor an interim mitigation yet, and the cloud provider’s comprehensive WAF rule will take up to 72 hours. That is why Company G confirms the impact itself and temporarily blocks at least the patterns already known.
What the exam asks about is the interim WAF rule and how it is operated, but a WAF does not fix the vulnerability itself. Impact confirmation, interim mitigation and the root fix should all move forward wherever they can proceed in parallel instead of waiting on one another. The full response, including the practical notes added here, looks like this.
| Stage | Purpose | What is done in this question and in practice |
|---|---|---|
| Impact confirmation | Decide whether your own company really is at risk | Confirm with a harmless callback whether external exploitation is possible |
| Interim mitigation | Buy time until the fix | WAF rules, detection and blocking, outbound traffic restrictions |
| Root fix | Remove the vulnerable cause | Update to the fixed library version |
| After-the-fact check | Find out whether it has already been exploited | Investigate WAF, application, DNS, proxy and other logs |
| Prevention | Make the next decision faster | An inventory of dependencies, an SBOM, an update procedure, communication channels |
11.1. Do Not Treat the Interim Rule as a Complete Log4Shell Countermeasure
The exam asks for a regular expression that handles the evasion shown in the question text. Real attacks can use variations that a signature alone struggles to cover: splitting the string, a different lookup, encoding, another protocol.
So its place in practice is as follows.
- Temporarily stop the attack patterns known so far with the WAF
- Find out whether the affected library really is included
- Restrict outbound LDAP, RMI and unnecessary HTTP traffic
- Update to the fixed version
- Keep checking the logs after the update and investigate whether a breach occurred
The WAF is a layer that buys time until the fixed version arrives.
flowchart TB
accTitle: Where the WAF fits
accDescr: Detection lets traffic through and observes it, blocking and outbound restrictions mitigate, and updating removes the root cause.
A["A critical vulnerability is disclosed"] --> B["Confirm impact and take interim action"]
B --> C["Get logs and alerts from detection"]
B --> D["Blocking and outbound traffic restrictions"]
C --> E["Tune and respond based on observation"]
D --> F["Update to the fixed library version"]
E --> F
F --> G["Check afterwards whether a breach occurred"]
Figure 19: Where the WAF fits. A WAF buys time until the fixed version arrives, and the root countermeasure is the update.
11.2. Preparation in Normal Times — Know Which Libraries You Use and Where They Run
In the question text, when Company G asks Company F whether library H is in use, the answer takes time because a detailed configuration analysis is needed.
In practice, starting to hunt for JAR files only after a critical vulnerability is disclosed makes the response late. You should keep at least the following on hand in normal times.
- A list of direct and transitive dependencies
- The components and versions actually contained in what you ship
- Which services, containers and machines they are deployed to
- The procedure for updating a dependent library and rebuilding and redistributing
- The communication channel that approves an emergency change
- The permitted destinations for outbound traffic and the impact of cutting them off
- Where logs are kept and how to search them
An SBOM is not the goal. It is an index for answering quickly which running systems a given vulnerability affects.
flowchart TB
accTitle: Tie dependencies to running systems
accDescr: Mapping dependent components to where they are deployed ahead of time speeds up investigation and update decisions after a disclosure.
A["List of direct and transitive dependencies"] --> B["Actual versions in what you ship"]
B --> C["Running services and where they are deployed"]
C --> D["Judge the affected scope quickly"]
D --> E["Approve, rebuild, redistribute"]
C -.-> F["Know the destinations and where logs are kept"]
Figure 20: An SBOM is not collected for its own sake; it is an index for judging quickly what is affected and how to update it.
11.3. The After-the-Fact Check — Updating Alone Does Not End the Breach Investigation
You may already have been attacked around the time the vulnerability was disclosed. Updating to the fixed version stops future exploitation, but it does not erase credentials that were already compromised or a backdoor that was already planted.
For a Log4Shell-style case, investigate at least the following.
- HTTP requests containing suspicious strings that point to JNDI or LDAP
- Traffic from the application server to external LDAP, RMI or HTTP
- Child processes starting that are not the usual ones
- Suspicious JAR, class, script or executable files being created
- Access to cloud credentials or environment variables
- Authentication, permission changes and outbound transfers before and after the update
It matters that you do not conclude from WAF logs alone that you were not attacked. There are internal paths that never pass through the WAF, and logs that were never retained.
flowchart TB
accTitle: Separate the update from the breach investigation
accDescr: Preventing future exploitation by updating to the fixed version and investigating whether a breach already happened are two separate needs.
A["Update to the fixed version"] --> B["Stop future exploitation"]
C["Examine records from before and after the update"] --> D["Correlate traffic, processes and files"]
D --> E["Check credentials and outbound transfers"]
B -.-> F["Past compromise does not disappear"]
F -.-> C
Figure 21: Fixing the cause and investigating a breach that has already happened proceed as separate tasks.
12. How to Write the Answers — State the Gap From the Specification in the Question’s Own Terms
This question is less a test of knowledge than an exercise in reading the gap between specification and implementation.
12.1 Separate the Specification From the Implementation in the Tables
In the status problem, a value absent from the API specification gets through in the implementation.
Specification:
mid / name / age
Implementation:
send every received parameter to P
Once you see that gap, blank c is clearly common module P.
12.2 Underline the Values the Attacker Changed
The values changed in each attack are these.
- The
algin the JWT header - The user ID in the JWT payload
- The
midAPI parameter - The undocumented
status - The
otpin the authentication API - The
x-api-versionHTTP header
Almost every question asks where that value should have been verified.
12.3 Bring the Answer Back to the Question’s Own Terms
In practice you can call these BOLA, Mass Assignment and rate limiting. What the question asks for, though, is concrete processing that fits the scenario as it is written.
A poor answer:
Perform authorization appropriately.
A good answer:
Verify that the user ID contained in the JWT matches the value of mid.
A poor answer:
Take countermeasures against brute force.
A good answer:
Lock the account once the number of consecutive failures exceeds the threshold.
Knowing the abstract name is not enough to produce a gradable answer within the character limit.
12.4 With the WAF, Follow Where the String Went
Decide the WAF’s inspection target from where the attack string is stored, not by guessing from the type of attack.
placed in the x-api-version header
|
v
inspection target is Header
The grading commentary says the overall correct-answer rate was average, with Question 2(2) and Question 3(1) somewhat lower.3 Question 3(1) scored somewhat lower because many answers did not fit the attack flow in the question’s Figure 6. Simply rewriting the attack procedure as a chain of arrows makes clear what you should be observing.
flowchart TB
accTitle: Working back from the question text to the answer
accDescr: Follow the gap between specification and implementation, the values that were changed, and the processing that trusted them, then write concrete logic in the question's own terms.
A["Lay the spec and the implementation side by side"] --> B["Identify the values that were changed"]
B --> C["Trace where they were trusted"]
C --> D["Decide the processing to add"]
D --> E["Return to the question's terms and character limit"]
Figure 22: Rather than memorizing terms, follow the flow of values and answer with concrete processing.
13. A Checklist for API Reviews in Practice
This is a checklist for carrying this question back into real design and code reviews.
JWT Verification
- The permitted signature algorithms are fixed in the server configuration
noneand unexpected algorithms are rejected- The signature,
iss,aud,expandnbfare verified as appropriate to the use case - ID tokens, access tokens and refresh tokens are never mixed up
- There is a procedure for key rotation and for revocation
- No information that must stay secret is placed in the JWT payload
Object-Level Authorization
- Changing an ID in the request does not reach another user’s data
- Authorization is applied to list, detail, update, delete and download alike
- Authorization happens in the shared layer that reaches the data, not in the screen
- For a self-only API, you considered deriving the target ID from the token
- Administrator operations have a policy separate from the ordinary-user API
Property-Level Authorization
- The external input type is separate from the database entity
- The updatable fields are enumerated in an allowlist
- Properties outside the specification are rejected or audited
- State such as permissions, billing, approval and ownership cannot be changed from user input
- Responses do not carry confidential properties that are not needed either
Authentication Attempts
- There is a per-account limit on the number of failures
- There are graduated delays and per-source controls
- Reissuing a code does not reset the failure count
- An authentication code can be used only once
- Authentication codes and passwords are never written to the log
- The unlock and recovery procedure is not itself a weaker authentication path
Critical Vulnerabilities in Dependent Libraries
- You can map running services to the dependency versions they use
- There is a procedure for verifying the impact by harmless means
- Interim measures such as a WAF or outbound traffic restrictions can be applied
- Someone is assigned to review detection alerts
- There is an emergency release path for updating to the fixed version
- Logs are investigated for possible exploitation before the update
flowchart TB
accTitle: A review tries what comes after the happy path
accDescr: Beyond a legitimate request succeeding, check changed IDs, changed fields and repeated attempts separately to validate the shared layer's controls.
A["Confirm a legitimate request succeeds"] --> B["Change only the target ID and check"]
B --> C["Add an unknown update field and check"]
C --> D["Repeat authentication failures and reissues"]
D --> E["Confirm rejection, recording and recovery"]
E -.-> F["Treat each one as a separate check"]
Figure 23: Starting from a successful happy path, confirm that each separate boundary really does reject what it should.
14. Summary — Never Skip the Next Trust Boundary
Afternoon Question 1 of Spring 2024 (Reiwa 6) is an exercise in separating the issues of API security and reading them one at a time.
Using a JWT does not mean authentication is secure. Let the attacker choose the signature algorithm and the user ID can be rewritten.
A valid JWT signature does not mean authorization is correct. Trust the mid in the request and a legitimate user can reach another user’s information.
Being able to update your own object does not mean you may change every property. Auto-bind internal state such as status and permissions or billing status can be rewritten.
An authentication code having an expiry does not mean it resists brute force. You have to calculate the candidate space and the attempt rate, and limit the number of failures.
Putting a rule in the WAF does not mean the vulnerability is fixed. Detection and blocking buy time, you confirm the impact, and in the end you update the library.
flowchart TB
accTitle: Vulnerabilities mapped to countermeasures
accDescr: Prepare a countermeasure for each boundary, covering tokens and authentication attempts, the target and the updated fields, and execution driven by external input.
A["Separate the boundaries you trust"] --> B["Tokens and authentication attempts"]
A --> C["Target data and updated fields"]
A --> D["Execution driven by external input"]
B --> E["Fix the permitted alg and limit attempts"]
C --> F["Match the subject and limit the update DTO"]
D --> G["Interim mitigation and a library update"]
Figure 24: Vulnerabilities mapped to countermeasures. Split the countermeasures by which boundary was broken.
One principle runs through this whole question.
Never treat the success of the previous check as a reason to skip the next trust boundary.
Earlier articles in this series cover the stored XSS in Autumn 2023 (Reiwa 5) Afternoon Question 1 and files walking out over the guest Wi-Fi in Autumn 2023 (Reiwa 5) Afternoon Question 2. For checking a website as a whole, see also Using IPA’s “How to Secure Your Website” as a Checklist.
flowchart TB
accTitle: Final summary
accDescr: Succeeding at the previous check is never a reason to skip the next check or the operational countermeasures that go with it.
A["The previous check succeeded"] --> B{"Is the next check satisfied too"}
B -->|"Check it separately"| C["Stack up a decision per boundary"]
B -->|"Skip it because this one passed"| D["An unchecked boundary is left behind"]
C --> E["Carry it into everyday design and testing"]
Figure 25: Final summary. Trust boundaries are checked in stages, and none of them may be skipped.
References
-
IPA, Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination, Afternoon Question Booklet. This is the question text covered by this article. ↩ ↩2 ↩3 ↩4
-
IPA, Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination, Afternoon Model Answers. The official model answer for each question. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12
-
IPA, Spring 2024 (Reiwa 6) Registered Information Security Specialist Examination, Afternoon Grading Commentary. An account of correct-answer rates and common wrong answers. ↩ ↩2 ↩3
-
NIST, SP 800-63B: Authentication and Authenticator Management. Sets out the length of short-lived secrets, limits on the number of attempts, the failure count on reissue, and not using email for out-of-band authentication. ↩ ↩2
-
RFC Editor, RFC 7519: JSON Web Token (JWT). The JWT specification, including the Unsecured JWT and
alg=none. ↩ -
RFC Editor, RFC 8725: JSON Web Token Best Current Practices. The BCP that lays down fixing the permitted algorithms and verifying the issuer, the subject and the audience. ↩
-
OWASP, API1:2023 Broken Object Level Authorization. Explains why authorization has to be checked for every object ID the user supplies. ↩
-
OWASP, API3:2023 Broken Object Property Level Authorization. Explains property-level authorization flaws, Mass Assignment included, and the countermeasures for them. ↩
-
Apache Logging Services, Security. Explains the impact of CVE-2021-44228, code execution via JNDI and LDAP, and the fixed versions. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Registered Information Security Specialist Examination, Autumn 2023 (Reiwa 5) Afternoon Question 1 Explained — The Stored XSS Where 16 Reviews Show Up as 2
This walkthrough of Autumn 2023 RISS exam Afternoon Question 1 shows how split posts beat the character limit, how the session ID left as...
Registered Information Security Specialist Examination, Autumn 2023 (Reiwa 5) Afternoon Question 2 Explained — Files Walking Out Over the Guest Wi-Fi
Autumn 2023 RISS exam Afternoon Question 2 shows how a USB-banned company loses files over guest Wi-Fi. It covers certificates, HSTS, MAC...
What Website Clients Should Know Too — Using IPA's 'How to Secure Your Website' as a Checklist
What standard should you use to check your company website's security against? This article explains the 11 vulnerabilities and counterme...
Information Security 10 Major Threats 2026 — How to Read the Ranking, and What SMEs Should Actually Guard Against
In IPA's 'Information Security 10 Major Threats 2026,' ransomware attacks took first place for the 11th year running, supply chain attack...
Where Should SMEs Start on Security? — A Walkthrough of IPA's 'Information Security Guidelines for SMEs,' 4th Edition
Where should small and medium-sized businesses start on security? Drawing on IPA's 'Information Security Guidelines for Small and Medium ...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Website Development
Because in member APIs and smartphone integrations, JWT verification, object-level authorization, and restricting which properties can be updated map directly onto the security of the web system itself.
Technical Consulting & Design Review
Because identifying authorization gaps in existing APIs, the scope of impact of dependent libraries, and interim WAF rules through a design review falls within the scope of technical consulting.
Frequently Asked Questions
Common questions about the topic of this article.
- Why can an attacker impersonate someone else even though JWT signature verification succeeded?
- In this question, the JWT management library accepted the JWT header's alg exactly as the attacker specified it, treating an alg=none JWT as valid with no signature at all. So even rewriting the user ID in the payload still passes verification. The exam's answer is to verify the JWT header's alg and confirm that its value is not NONE. In practice, though, rejecting only NONE is not enough. Fix the algorithms allowed for use, for example to RS256, in server-side configuration, so the algorithm the token declares is never used directly to make the choice. Verify the issuer, audience, expiry, subject, and so on as well, as appropriate to the use case.
- Is comparing the user ID inside the JWT against the request's mid enough as an authorization countermeasure?
- For the purposes of this exam's answer, it is enough. Verifying, in common module P, that the user ID contained in the JWT matches mid stops an attack that specifies someone else's mid. However, for an API that only handles the caller's own information, it is safer in practice not to accept mid from the client at all, and instead to determine the user ID from the verified JWT's subject. Using GET /users/me or PUT /users/me, for example, makes it harder to end up with the comparison logic simply missing. An API where an administrator operates on another user is split into a separate endpoint with its own authorization policy.
- Why can't ordinary input validation alone stop the attack that adds status?
- Because validating the length of name or the range of age does nothing if status, a field that should never have been accepted in the first place, is auto-bound and passed straight to the internal object. The issue is not the format of a value but property-level authorization: whether the user is allowed to change that property at all. Define only name and age in the update input type, and reject unknown properties. Billing status has to be changed only from events the server can trust, such as a successful result from the payment service.
- The four-digit authentication code expires after 10 minutes, so why is it still dangerous?
- Because there are only 10,000 candidates from 0000 to 9999, and at 10 attempts per second an attacker succeeds after an average of 5,000 attempts, which is 500 seconds. The 10-minute validity period is 600 seconds, so trying non-repeating candidates in order lets an attacker check 6,000 of them within that window. Expiry time alone cannot stop brute-forcing. The candidate space, the attempt rate, and the cap on the number of attempts all have to be designed together.
- The exam's countermeasure is account locking. Is immediate locking on its own enough in practice too?
- No. The blank in the question calls for logic that locks the account once the number of consecutive failures exceeds a threshold, but a fixed, permanent lock on its own lets an attacker deliberately lock someone else's account as a denial of service. In practice you combine a per-account failure count with graduated wait times, risk assessment of the source and the device, notifications, and a recovery procedure. It also matters that issuing a new code does not reset the failure count back to zero.
- What is the point of setting the WAF to detect rather than block?
- It means business traffic is not stopped even when a legitimate string is mistakenly judged an attack. In the model answer, the benefit is that it prevents blocking caused by a false positive, and what should be done is to examine whether it is an attack whenever an alert is received. Detect mode is not a setting you leave alone. It is an observation period during which you check the logs, filter out false positives, tune the rule, and then move to blocking. In an emergency where a known critical vulnerability is actually being exploited, it can be reasonable to weigh that against availability risk and block from the start instead.
- Is library H in this question Log4j?
- The question withholds the product name, but the attack sequence of a JNDI Lookup, an LDAP server, class retrieval from an HTTP server, a string embedded in an HTTP header, and a high CVSS v3.1 base score reads naturally as an abstraction of CVE-2021-44228, known as Log4Shell. This article explains that correspondence, but the exam does not require you to name the product. It can be answered from the given attack procedure and the WAF specification alone.
- What should you take back into practice from this question?
- That succeeding at authentication, the JWT not having been tampered with, being allowed to access the target object, and being allowed to change the target property are all separate checks. On top of that, a short authentication code needs a limit on the number of attempts, and for a critical library vulnerability you run impact confirmation, interim defense, and the root fix in parallel. The core practical points are to consolidate authorization into shared components, to make the input schema an allowlist, to fix the JWT verification conditions on the server side, and to keep track of dependent libraries so you can update them.