Registered Information Security Specialist Exam, Spring 2024 (Reiwa 6) Afternoon Q1 — JWT alg=none, API Authorization, and Interim WAF Mitigation

· Updated: · · 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

Overview of the questionRead the authentication code, the JWT, the target ID, the updated fields, and execution driven by external input as five separate checks.User requestCheck the authentication codeIssue and verify the JWTUser APIAuthorization for the target midAuthorization for updating statusWrite external input to the logVulnerable libraryExternal 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.

The difference between authentication and authorizationConfirming the subject is one thing, and checking that the subject may operate on the target data or field is a separate check.Authentication and token verificationEstablish whose request this isMay it reach that dataMay 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.”

  • alg is a policy for cryptographic processing
  • mid is object-level authorization
  • status is property-level authorization
  • otp is 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.

Stateless and the state that is storedProcessing each request without depending on conversation history still stores state such as business data and failure counts.Request with a JWTIdentify the user from that request aloneProcess it and respondUser information, billing, failure countNo 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.

A sense of scale for the 4-digit authentication codeThe 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.10000 candidatesAbout 5000 attempts on averageAbout 500 seconds at 10 per secondShorter than the 600-second validity6000 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.

  1. Changed the header’s alg from RS256 to NONE
  2. Changed the payload’s user ID to a different user

Sending that JWT passed verification and impersonated the other user.

Flow of the JWT alg=none attackWhen the attacker changes both the verification method and the user ID, a library that permits none accepts the tampered JWT.Obtain a valid JWTChange alg to noneChange user to a different userThe library skips signature verificationAccepted 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.

Safe and dangerous JWT verificationDo not let the token decide the verification method; check the signature and each claim only after the server-side permission condition is met.NoYesReceive a JWTIs it an alg the server allowsRejectVerify the signature and each claimTreat it as a verified subjectDangerous processingSkip 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.

Signing and secrecy are separate in a JWTbase64url is a representation that third parties can read, so tamper detection by the signature and confidentiality are separate concerns.Signed JWTRead the header and payloadDecode the base64urlThe contents are not secretVerify the signature correctlyConfirm 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.

The BOLA attackThe subject of the valid JWT and the request mid differ, yet only mid is used and another user's information is returned.JWT with a valid signature (user01)User APIChanged mid (user02)No comparison against the subjectFetch 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.”

How to prevent BOLAEither 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.mid is receivedYesNoSelf-only APISubject of the verified JWTHow the target ID is decidedDoes it match the subjectOperate on your own dataRejectDerive the target ID from the subject

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.

Mass AssignmentPassing an undocumented status to the common module and updating with it lets a user change their own billing state.The spec is mid, name and ageAdd status=paidPass every parameter to PApplied wholesale to the internal dataBilling 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.

  1. Put only the fields the user may change into the update input type
  2. 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.

Field-level authorizationLimit the update input type to name and age, and update billing status through a dedicated path from a verified payment notification.YesNoProfile update requestOnly name and ageUpdate only the permitted fieldsReject the unknown fieldVerified payment notificationMatch paymentId and prevent duplicatesUpdate 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 mid or status, 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.

Narrow the contract of shared componentsPutting authorization and the input type into the contract of a shared component reduces reliance on every caller using it correctly.API for ordinary usersVerified subject and update DTOAuthorization unified in the shared componentOperate on permitted dataOperations with an arbitrary ID or mapSplit off to limited paths such as administrationTest 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.

With and without a limit on attemptsKeeping the failure count and locking once the threshold is exceeded stops unlimited online guessing.YesNoCode check failsUpdate the account's failure countHas the threshold been exceededLock the accountAllow the remaining attemptsWithout a limit the attacker keeps trying

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.

Countermeasures for authentication codesOn top of the candidate space and the expiry, keep the attempt count, invalidate after success, and combine notification with a recovery procedure.YesNoIssue or reissue a codeDo not reset the failure countCheck it with limits on count and rateDid the check succeedInvalidate the code immediatelyRecord the failure, delay, lockNotification and a safe recovery procedureLimit 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.

  1. The attacker sends a string containing a JNDI Lookup inside an HTTP header
  2. The target server writes that value to its log
  3. The vulnerable library evaluates the JNDI Lookup and queries the attacker’s LDAP server
  4. The LDAP response returns the URL of the attacker’s HTTP server
  5. 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.

Confirmation flow for a Log4Shell-style vulnerabilityBeyond the JNDI reference and class retrieval, confirm in the log that the verification command fetched index.html.External input in an HTTP headerLog processing on the target serverQuery LDAP over JNDIReceive the URL to fetch the class fromThe target server fetches the classThe target server runs the verification commandindex.html is fetched from the test serverRecord 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.

Keep verification to the smallest side effectGet approval, decide the harmless method and what will be observed, and tear down the verification environment and credentials afterwards.Explicit approval from the ownerDecide the impact and what to observeRecord the access on a server you controlMatch it against the expected time and sourceTear down the environment and credentialsNo 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.

Choosing where the WAF inspectsDecide the WAF inspection target from where the question text stores the attack string, not from the name of the attack.Read where the attack string is storedThe x-api-version headerThe inspection target is HeaderOn to a pattern that covers letter caseANY covers parameters of every method

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.

  1. Apply it to real traffic in detect mode
  2. Classify the hits into false positives and true positives
  3. Tune the target headers, paths, APIs, character boundaries and so on
  4. Confirm that the impact on legitimate traffic is acceptable
  5. Move to blocking mode
  6. 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.

From WAF detection to blockingExamine the alerts raised during detection, tune away false positives and deal with attacks, then keep monitoring after moving to blocking.False positiveAttackObserve traffic in detect modeWhat does the alert showTune the rule or the exceptionIsolate, preserve logs, assess impactMove to blockingConfirm the impact on legitimate trafficMonitor 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.

  1. Temporarily stop the attack patterns known so far with the WAF
  2. Find out whether the affected library really is included
  3. Restrict outbound LDAP, RMI and unnecessary HTTP traffic
  4. Update to the fixed version
  5. 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.

Where the WAF fitsDetection lets traffic through and observes it, blocking and outbound restrictions mitigate, and updating removes the root cause.A critical vulnerability is disclosedConfirm impact and take interim actionGet logs and alerts from detectionBlocking and outbound traffic restrictionsTune and respond based on observationUpdate to the fixed library versionCheck 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.

Tie dependencies to running systemsMapping dependent components to where they are deployed ahead of time speeds up investigation and update decisions after a disclosure.List of direct and transitive dependenciesActual versions in what you shipRunning services and where they are deployedJudge the affected scope quicklyApprove, rebuild, redistributeKnow 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.

Separate the update from the breach investigationPreventing future exploitation by updating to the fixed version and investigating whether a breach already happened are two separate needs.Update to the fixed versionStop future exploitationExamine records from before and after the updateCorrelate traffic, processes and filesCheck credentials and outbound transfersPast compromise does not disappear

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 alg in the JWT header
  • The user ID in the JWT payload
  • The mid API parameter
  • The undocumented status
  • The otp in the authentication API
  • The x-api-version HTTP 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.

Working back from the question text to the answerFollow 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.Lay the spec and the implementation side by sideIdentify the values that were changedTrace where they were trustedDecide the processing to addReturn 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
  • none and unexpected algorithms are rejected
  • The signature, iss, aud, exp and nbf are 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
A review tries what comes after the happy pathBeyond a legitimate request succeeding, check changed IDs, changed fields and repeated attempts separately to validate the shared layer's controls.Confirm a legitimate request succeedsChange only the target ID and checkAdd an unknown update field and checkRepeat authentication failures and reissuesConfirm rejection, recording and recoveryTreat 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.

Vulnerabilities mapped to countermeasuresPrepare a countermeasure for each boundary, covering tokens and authentication attempts, the target and the updated fields, and execution driven by external input.Separate the boundaries you trustTokens and authentication attemptsTarget data and updated fieldsExecution driven by external inputFix the permitted alg and limit attemptsMatch the subject and limit the update DTOInterim 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.

Final summarySucceeding at the previous check is never a reason to skip the next check or the operational countermeasures that go with it.Check it separatelySkip it because this one passedThe previous check succeededIs the next check satisfied tooStack up a decision per boundaryAn unchecked boundary is left behindCarry it into everyday design and testing

Figure 25: Final summary. Trust boundaries are checked in stages, and none of them may be skipped.

References

  1. 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

  2. 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

  3. 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

  4. 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

  5. RFC Editor, RFC 7519: JSON Web Token (JWT). The JWT specification, including the Unsecured JWT and alg=none

  6. 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. 

  7. OWASP, API1:2023 Broken Object Level Authorization. Explains why authorization has to be checked for every object ID the user supplies. 

  8. OWASP, API3:2023 Broken Object Property Level Authorization. Explains property-level authorization flaws, Mass Assignment included, and the countermeasures for them. 

  9. Apache Logging Services, Security. Explains the impact of CVE-2021-44228, code execution via JNDI and LDAP, and the fixed versions. 

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

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.

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.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog