Search code examples
jmetergenexus

Jmeter 440 error code. How can I resolve this error?


I am working with Jmeter and Blazemeter in a login script for a web made with Genexus. The problem that I am having is in the POST.

Whenever I try to make a POST http petition, Jmeter throws the next thing: enter image description here

As you can see, in the response body, I am having a 440 http error code. This is a login Time-out which means the client's session has expired and must log in again. I used to have a 403 error code but now, after doing some arrangements, I have 440. Do you have any suggestions on how to resolve this?


Solution

  • First, I'm not an expert on Genexus. All my findings are from a black-box point of view.

    Genexus Security

    I found that Genexus requires at least two things to authenticate on Web Application (I tested only Java and .Net generated apps).

    1. The GXState parameter. This param is sent in post request, and from my understanding works as "Synchronizer token pattern", see more info on Cross-site request forgery. We need to send this param on every post request.

    2. The gxajaxEvt parameter. This is very specific to Genexus Apps. In the documentation mentions this parameter is send encrypted in the URL, and this behavior is managed by the "Javascript debug mode property":

    # Javascript Debug Mode: Yes
    http://{server}:{port}/{webappname}/servlet/com.{kbname}.{objectname}?gxfullajaxEvt,gx-no-cache=1442811265833
    # Javascript Debug Mode: No (default value)
    http://{server}:{port}/{webappname}/servlet/com.{kbname}.{objectname}?64df96a2d9b8480aed416e470dae529e,gx-no-cache=1442811265833
    

    JMeter Script

    1. So, to get the GXState, we can use the Regular Expression Extractor: Regular Expression Extractor

      Name of created variable: GXState

      Regular expression: name="GXState" value='(.*?)'

      Template: $1$

      Match No.: 1

      Default Value: NOT_FOUND

    2. The GXState is a JSON, object, from it we can extract the GX_AJAX_KEY to encrypt gxajaxEvt string. Note that, I found the GX_AJAX_KEY is the key used to encrypt in this case, but some others could apply. We can debug this using Browser Web Console, with this:

    gx.sec.encrypt("gxajaxEvt")
    

    We'll see something like this: "8722e2ea52fd44f599d35d1534485d8e206d507a46070a816ca7fcdbe812b0ad"

    As we can found, all the client encryption code is in the gxgral.js file. Genexus uses the Rijndael algortihm (Sub set of AES) with block size of 128 bits.

    Browser Web Console

    To emulate this client behavior in the JMeter Script we can use the "JSR 233 sampler". A way to get the Rijndael results is use the Bouncy Castle library. We need to add this jar (bouncycastle:bcprov-jdk15to18:1.68) to the JMeter's lib folder to use it.

    Our code script will be something like this (Language Groovy 3.0.5/Groovy Scripting Engine 2.0):

    import com.jayway.jsonpath.JsonPath
    import java.nio.charset.StandardCharsets
    import java.util.Arrays
    import org.bouncycastle.crypto.BufferedBlockCipher
    import org.bouncycastle.crypto.InvalidCipherTextException
    import org.bouncycastle.crypto.engines.RijndaelEngine
    import org.bouncycastle.crypto.params.KeyParameter
    import org.bouncycastle.util.encoders.Hex
    import org.apache.jmeter.threads.JMeterContextService
    import org.apache.jmeter.threads.JMeterContext
    import org.apache.jmeter.threads.JMeterVariables
    
    String gxState = vars.get('GXState')
    String gxAjaxKey = JsonPath.read(gxState,'$.GX_AJAX_KEY')
    byte[] input = Arrays.copyOf('gxajaxEvt'.getBytes(StandardCharsets.UTF_8), 16)
    RijndaelEngine engine = new RijndaelEngine(128)
    KeyParameter key = new KeyParameter(Hex.decode(gxAjaxKey))
    BufferedBlockCipher cipher = new BufferedBlockCipher(engine)
    cipher.init(true, key)
    byte[] out = new byte[16]
    int length = cipher.processBytes(input, 0, 16, out, 0)
    cipher.doFinal(out, length)
    String encryptedOutput= Hex.toHexString(out)
    log.info 'gx.sec.encrypt("gxajaxEvt")='+encryptedOutput
    String gxNoCache = String.valueOf(System.currentTimeMillis())
    log.info 'gx-no-cache='+gxNoCache
    vars.put('gxajaxEvt', encryptedOutput)
    vars.put('gxNoCache', gxNoCache)
    

    The script work like this:

    • First, We get the previos GXState variable extracted.
    • Second, Using JSON Path (Already available in JMeter 5.4.1) extract the GX_AJAX_KEY property.
    • Third, We apply the Rijndael algorithm over the gxajaxEvt using the GX_AJAX_KEY as a key.
    • We also create the gx-no-cache to handle the cache.

    JSR233 Sampler

    1. With these variables we can send the next request successfully: Next HTTP Request

    We can found this sample JMeter script available here.

    For complex scripts, please refer to this guide (Requires GXTest)

    In case we get this exception in JMeter ( java.util.zip.ZipException: Not in GZIP format) please refer this answer too.